Merge "Remove an obsolete TODO for vkGetPhysicalDevicePresentRectanglesKHR"
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: NOTICE
+}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 1911119..9ba4819 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -169,6 +169,7 @@
 #define OTA_METADATA_DIR "/metadata/ota"
 #define SNAPSHOTCTL_LOG_DIR "/data/misc/snapshotctl_log"
 #define LINKERCONFIG_DIR "/linkerconfig"
+#define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list"
 
 // TODO(narayan): Since this information has to be kept in sync
 // with tombstoned, we should just put it in a common header.
@@ -1612,6 +1613,7 @@
     if (!PropertiesHelper::IsUserBuild()) {
         ds.AddDir(PROFILE_DATA_DIR_CUR, true);
         ds.AddDir(PROFILE_DATA_DIR_REF, true);
+        ds.AddZipEntry(ZIP_ROOT_DIR + PACKAGE_DEX_USE_LIST, PACKAGE_DEX_USE_LIST);
     }
     ds.AddDir(PREREBOOT_DATA_DIR, false);
     add_mountinfo();
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 3c04435..82be007 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -318,6 +318,16 @@
 // Phenotype property name for enabling profiling the boot class path.
 static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
 
+static bool IsBootClassPathProfilingEnable() {
+    std::string profile_boot_class_path = GetProperty("dalvik.vm.profilebootclasspath", "");
+    profile_boot_class_path =
+        server_configurable_flags::GetServerConfigurableFlag(
+            RUNTIME_NATIVE_BOOT_NAMESPACE,
+            PROFILE_BOOT_CLASS_PATH,
+            /*default_value=*/ profile_boot_class_path);
+    return profile_boot_class_path == "true";
+}
+
 class RunDex2Oat : public ExecVHelper {
   public:
     RunDex2Oat(int zip_fd,
@@ -450,14 +460,7 @@
                                                                  ENABLE_JITZYGOTE_IMAGE,
                                                                  /*default_value=*/ "");
 
-        std::string profile_boot_class_path = GetProperty("dalvik.vm.profilebootclasspath", "");
-        profile_boot_class_path =
-            server_configurable_flags::GetServerConfigurableFlag(
-                RUNTIME_NATIVE_BOOT_NAMESPACE,
-                PROFILE_BOOT_CLASS_PATH,
-                /*default_value=*/ profile_boot_class_path);
-
-        if (use_jitzygote_image == "true" || profile_boot_class_path == "true") {
+        if (use_jitzygote_image == "true" || IsBootClassPathProfilingEnable()) {
           boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
         } else {
           boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
@@ -896,7 +899,15 @@
     }
 
     RunProfman profman_merge;
-    profman_merge.SetupMerge(profiles_fd, reference_profile_fd);
+    const std::vector<unique_fd>& apk_fds = std::vector<unique_fd>();
+    const std::vector<std::string>& dex_locations = std::vector<std::string>();
+    profman_merge.SetupMerge(
+            profiles_fd,
+            reference_profile_fd,
+            apk_fds,
+            dex_locations,
+            /* for_snapshot= */ false,
+            IsBootClassPathProfilingEnable());
     pid_t pid = fork();
     if (pid == 0) {
         /* child -- drop privileges before continuing */
@@ -2810,7 +2821,13 @@
     }
 
     RunProfman args;
-    args.SetupMerge(profiles_fd, snapshot_fd, apk_fds, dex_locations, /*for_snapshot=*/true);
+    // This is specifically a snapshot for an app, so don't use boot image profiles.
+    args.SetupMerge(profiles_fd,
+            snapshot_fd,
+            apk_fds,
+            dex_locations,
+            /* for_snapshot= */ true,
+            /* for_boot_image= */ false);
     pid_t pid = fork();
     if (pid == 0) {
         /* child -- drop privileges before continuing */
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index cbbea12..1f9892a 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -49,14 +49,28 @@
     const std::string iface = name.substr(lastDot+1, firstSlash-lastDot-1);
     const std::string instance = name.substr(firstSlash+1);
 
-    for (const auto& manifest : {
-            vintf::VintfObject::GetDeviceHalManifest(),
-            vintf::VintfObject::GetFrameworkHalManifest()
+    struct ManifestWithDescription {
+        std::shared_ptr<const vintf::HalManifest> manifest;
+        const char* description;
+    };
+    for (const ManifestWithDescription& mwd : {
+            ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" },
+            ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" },
         }) {
-        if (manifest != nullptr && manifest->hasAidlInstance(package, iface, instance)) {
+        if (mwd.manifest == nullptr) {
+          LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description;
+          // note, we explicitly do not retry here, so that we can detect VINTF
+          // or other bugs (b/151696835)
+          continue;
+        }
+        if (mwd.manifest->hasAidlInstance(package, iface, instance)) {
+            LOG(INFO) << "Found " << name << " in " << mwd.description << " VINTF manifest.";
             return true;
         }
     }
+
+    // Although it is tested, explicitly rebuilding qualified name, in case it
+    // becomes something unexpected.
     LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance
                << " in the VINTF manifest.";
     return false;
@@ -72,13 +86,20 @@
 #endif  // !VENDORSERVICEMANAGER
 
 ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) {
-#ifndef VENDORSERVICEMANAGER
-    // can process these at any times, don't want to delay first VINTF client
-    std::thread([] {
-        vintf::VintfObject::GetDeviceHalManifest();
-        vintf::VintfObject::GetFrameworkHalManifest();
-    }).detach();
-#endif  // !VENDORSERVICEMANAGER
+// TODO(b/151696835): reenable performance hack when we solve bug, since with
+//     this hack and other fixes, it is unlikely we will see even an ephemeral
+//     failure when the manifest parse fails. The goal is that the manifest will
+//     be read incorrectly and cause the process trying to register a HAL to
+//     fail. If this is in fact an early boot kernel contention issue, then we
+//     will get no failure, and by its absence, be signalled to invest more
+//     effort in re-adding this performance hack.
+// #ifndef VENDORSERVICEMANAGER
+//     // can process these at any times, don't want to delay first VINTF client
+//     std::thread([] {
+//         vintf::VintfObject::GetDeviceHalManifest();
+//         vintf::VintfObject::GetFrameworkHalManifest();
+//     }).detach();
+// #endif  // !VENDORSERVICEMANAGER
 }
 ServiceManager::~ServiceManager() {
     // this should only happen in tests
@@ -547,4 +568,4 @@
     return Status::ok();
 }
 
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/data/etc/android.hardware.camera.concurrent.xml b/data/etc/android.hardware.camera.concurrent.xml
new file mode 100644
index 0000000..2cbb263
--- /dev/null
+++ b/data/etc/android.hardware.camera.concurrent.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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 is the set of features required for a camera2 device that supports concurrent operation
+     of front and back cameras -->
+<permissions>
+    <feature name="android.hardware.camera.front" />
+    <feature name="android.hardware.camera.concurrent" />
+</permissions>
diff --git a/data/etc/android.hardware.sensor.hinge_angle.xml b/data/etc/android.hardware.sensor.hinge_angle.xml
new file mode 100644
index 0000000..d744305
--- /dev/null
+++ b/data/etc/android.hardware.sensor.hinge_angle.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Feature for devices with a hinge angle sensor. -->
+<permissions>
+    <feature name="android.hardware.sensor.hinge_angle" />
+</permissions>
\ No newline at end of file
diff --git a/data/etc/android.software.controls.xml b/data/etc/android.software.controls.xml
new file mode 100644
index 0000000..2cba34b
--- /dev/null
+++ b/data/etc/android.software.controls.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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 is the standard feature indicating device controls support on the device,
+     as specified in the CDD. ONLY devices that meet the CDD's requirements may
+     declare this feature. -->
+<permissions>
+    <feature name="android.software.controls" />
+</permissions>
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 619d017..dc6963f 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -59,6 +59,9 @@
     <!-- Feature to specify if the device support managed users. -->
     <feature name="android.software.managed_users" notLowRam="true"/>
 
+    <!-- Feature to specify if the device supports controls. -->
+    <feature name="android.software.controls" />
+
     <!-- Devices with all optimizations required to support VR Mode and
          pass all CDD requirements for this feature may include
          android.hardware.vr.high_performance -->
diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml
index 52524ca..e878f86 100644
--- a/data/etc/tablet_core_hardware.xml
+++ b/data/etc/tablet_core_hardware.xml
@@ -59,6 +59,9 @@
     <!-- Feature to specify if the device support managed users. -->
     <feature name="android.software.managed_users" />
 
+    <!-- Feature to specify if the device supports controls. -->
+    <feature name="android.software.controls" />
+
     <!-- devices with GPS must include android.hardware.location.gps.xml -->
     <!-- devices with a rear-facing camera must include one of these as appropriate:
          android.hardware.camera.xml or
diff --git a/docs/Doxyfile b/docs/Doxyfile
index efa639d..a1bd960 100644
--- a/docs/Doxyfile
+++ b/docs/Doxyfile
@@ -1621,7 +1621,23 @@
 # undefined via #undef or recursively expanded use the := operator
 # instead of the = operator.
 
-PREDEFINED             = __attribute__(x)=
+PREDEFINED             = \
+    "__ANDROID_API__=10000" \
+    "__BEGIN_DECLS=" \
+    "__END_DECLS=" \
+    "__INTRODUCED_IN(x)=" \
+    "__INTRODUCED_IN_32(x)=" \
+    "__INTRODUCED_IN_64(x)=" \
+    "__RENAME(x)=" \
+    "__RENAME_LDBL(x,y,z)=" \
+    "__printflike(x,y)=" \
+    "__attribute__(x)=" \
+    "__wur=" \
+    "__mallocfunc=" \
+    "__attribute_pure__=" \
+    "__attribute__(x)=" \
+    __ANDROID__ \
+    __BIONIC__ \
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
 # this tag can be used to specify a list of macro names that should be expanded.
diff --git a/headers/Android.bp b/headers/Android.bp
index 82bc8a1..8f41c2b 100644
--- a/headers/Android.bp
+++ b/headers/Android.bp
@@ -17,4 +17,12 @@
         "libutils_headers",
         "libstagefright_foundation_headers",
     ],
+    min_sdk_version: "29",
+
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
 }
diff --git a/libs/nativedisplay/include/android/choreographer.h b/include/android/choreographer.h
similarity index 88%
rename from libs/nativedisplay/include/android/choreographer.h
rename to include/android/choreographer.h
index 5fd3de9..c1c4a72 100644
--- a/libs/nativedisplay/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -75,14 +75,16 @@
  * Deprecated: Use AChoreographer_postFrameCallback64 instead.
  */
 void AChoreographer_postFrameCallback(AChoreographer* choreographer,
-        AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
+                                      AChoreographer_frameCallback callback, void* data)
+        __INTRODUCED_IN(24) __DEPRECATED_IN(29);
 
 /**
  * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead.
  */
 void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
-                AChoreographer_frameCallback callback, void* data,
-                long delayMillis) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
+                                             AChoreographer_frameCallback callback, void* data,
+                                             long delayMillis) __INTRODUCED_IN(24)
+        __DEPRECATED_IN(29);
 
 #endif /* __ANDROID_API__ >= 24 */
 
@@ -95,7 +97,8 @@
  * Available since API level 29.
  */
 void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
-                AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29);
+                                        AChoreographer_frameCallback64 callback, void* data)
+        __INTRODUCED_IN(29);
 
 /**
  * Post a callback to be run on the frame following the specified delay.  The
@@ -105,7 +108,8 @@
  * Available since API level 29.
  */
 void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
-                AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29);
+                                               AChoreographer_frameCallback64 callback, void* data,
+                                               uint32_t delayMillis) __INTRODUCED_IN(29);
 
 #endif /* __ANDROID_API__ >= 29 */
 
diff --git a/include/android/trace.h b/include/android/trace.h
index d59690a..dbad6f6 100644
--- a/include/android/trace.h
+++ b/include/android/trace.h
@@ -115,7 +115,7 @@
 #endif /* __ANDROID_API__ >= 29 */
 
 #ifdef __cplusplus
-};
+}
 #endif
 
 #endif // ANDROID_NATIVE_TRACE_H
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index 6100626..2427a07 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -74,36 +74,40 @@
     int32_t physicalBottom;
     int32_t deviceWidth;
     int32_t deviceHeight;
+    bool isActive;
     std::string uniqueId;
     // The actual (hardware) port that the associated display is connected to.
     // Not all viewports will have this specified.
     std::optional<uint8_t> physicalPort;
     ViewportType type;
 
-    DisplayViewport() :
-            displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0),
-            logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0),
-            physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0),
-            deviceWidth(0), deviceHeight(0), uniqueId(), physicalPort(std::nullopt),
-            type(ViewportType::VIEWPORT_INTERNAL) {
-    }
+    DisplayViewport()
+          : displayId(ADISPLAY_ID_NONE),
+            orientation(DISPLAY_ORIENTATION_0),
+            logicalLeft(0),
+            logicalTop(0),
+            logicalRight(0),
+            logicalBottom(0),
+            physicalLeft(0),
+            physicalTop(0),
+            physicalRight(0),
+            physicalBottom(0),
+            deviceWidth(0),
+            deviceHeight(0),
+            isActive(false),
+            uniqueId(),
+            physicalPort(std::nullopt),
+            type(ViewportType::VIEWPORT_INTERNAL) {}
 
     bool operator==(const DisplayViewport& other) const {
-        return displayId == other.displayId
-                && orientation == other.orientation
-                && logicalLeft == other.logicalLeft
-                && logicalTop == other.logicalTop
-                && logicalRight == other.logicalRight
-                && logicalBottom == other.logicalBottom
-                && physicalLeft == other.physicalLeft
-                && physicalTop == other.physicalTop
-                && physicalRight == other.physicalRight
-                && physicalBottom == other.physicalBottom
-                && deviceWidth == other.deviceWidth
-                && deviceHeight == other.deviceHeight
-                && uniqueId == other.uniqueId
-                && physicalPort == other.physicalPort
-                && type == other.type;
+        return displayId == other.displayId && orientation == other.orientation &&
+                logicalLeft == other.logicalLeft && logicalTop == other.logicalTop &&
+                logicalRight == other.logicalRight && logicalBottom == other.logicalBottom &&
+                physicalLeft == other.physicalLeft && physicalTop == other.physicalTop &&
+                physicalRight == other.physicalRight && physicalBottom == other.physicalBottom &&
+                deviceWidth == other.deviceWidth && deviceHeight == other.deviceHeight &&
+                isActive == other.isActive && uniqueId == other.uniqueId &&
+                physicalPort == other.physicalPort && type == other.type;
     }
 
     bool operator!=(const DisplayViewport& other) const {
@@ -127,6 +131,7 @@
         physicalBottom = height;
         deviceWidth = width;
         deviceHeight = height;
+        isActive = false;
         uniqueId.clear();
         physicalPort = std::nullopt;
         type = ViewportType::VIEWPORT_INTERNAL;
@@ -134,18 +139,16 @@
 
     std::string toString() const {
         return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, "
-            "logicalFrame=[%d, %d, %d, %d], "
-            "physicalFrame=[%d, %d, %d, %d], "
-            "deviceSize=[%d, %d]",
-            viewportTypeToString(type), displayId,
-            uniqueId.c_str(),
-            physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "<none>",
-            orientation,
-            logicalLeft, logicalTop,
-            logicalRight, logicalBottom,
-            physicalLeft, physicalTop,
-            physicalRight, physicalBottom,
-            deviceWidth, deviceHeight);
+                            "logicalFrame=[%d, %d, %d, %d], "
+                            "physicalFrame=[%d, %d, %d, %d], "
+                            "deviceSize=[%d, %d], "
+                            "isActive=[%d]",
+                            viewportTypeToString(type), displayId, uniqueId.c_str(),
+                            physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str()
+                                         : "<none>",
+                            orientation, logicalLeft, logicalTop, logicalRight, logicalBottom,
+                            physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth,
+                            deviceHeight, isActive);
     }
 };
 
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index a258a67..f2841ea 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -75,47 +75,49 @@
     // Window types from WindowManager.LayoutParams
     enum {
         FIRST_APPLICATION_WINDOW = 1,
-        TYPE_BASE_APPLICATION   = 1,
-        TYPE_APPLICATION        = 2,
+        TYPE_BASE_APPLICATION = 1,
+        TYPE_APPLICATION = 2,
         TYPE_APPLICATION_STARTING = 3,
         LAST_APPLICATION_WINDOW = 99,
-        FIRST_SUB_WINDOW        = 1000,
-        TYPE_APPLICATION_PANEL  = FIRST_SUB_WINDOW,
-        TYPE_APPLICATION_MEDIA  = FIRST_SUB_WINDOW+1,
-        TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2,
-        TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3,
-        TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW+4,
-        LAST_SUB_WINDOW         = 1999,
-        FIRST_SYSTEM_WINDOW     = 2000,
-        TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW,
-        TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1,
-        TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2,
-        TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3,
-        TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4,
-        TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5,
-        TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6,
-        TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7,
-        TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8,
-        TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9,
-        TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10,
-        TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11,
-        TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12,
-        TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13,
-        TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14,
-        TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15,
-        TYPE_DRAG               = FIRST_SYSTEM_WINDOW+16,
-        TYPE_STATUS_BAR_SUB_PANEL  = FIRST_SYSTEM_WINDOW+17,
-        TYPE_POINTER            = FIRST_SYSTEM_WINDOW+18,
-        TYPE_NAVIGATION_BAR     = FIRST_SYSTEM_WINDOW+19,
-        TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20,
-        TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21,
-        TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22,
-        TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24,
-        TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27,
-        TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32,
-        TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34,
-        TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+39,
-        LAST_SYSTEM_WINDOW      = 2999,
+        FIRST_SUB_WINDOW = 1000,
+        TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW,
+        TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1,
+        TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2,
+        TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3,
+        TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4,
+        LAST_SUB_WINDOW = 1999,
+        FIRST_SYSTEM_WINDOW = 2000,
+        TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW,
+        TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1,
+        TYPE_PHONE = FIRST_SYSTEM_WINDOW + 2,
+        TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3,
+        TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW + 4,
+        TYPE_TOAST = FIRST_SYSTEM_WINDOW + 5,
+        TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6,
+        TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7,
+        TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8,
+        TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9,
+        TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10,
+        TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11,
+        TYPE_INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12,
+        TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW + 13,
+        TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14,
+        TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15,
+        TYPE_DRAG = FIRST_SYSTEM_WINDOW + 16,
+        TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17,
+        TYPE_POINTER = FIRST_SYSTEM_WINDOW + 18,
+        TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19,
+        TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20,
+        TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21,
+        TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22,
+        TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24,
+        TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27,
+        TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32,
+        TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34,
+        TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39,
+        TYPE_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40,
+        TYPE_TRUSTED_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 42,
+        LAST_SYSTEM_WINDOW = 2999,
     };
 
     enum {
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 727865a..ee010a3 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -18,8 +18,8 @@
 #define _LIBINPUT_VELOCITY_TRACKER_H
 
 #include <input/Input.h>
-#include <utils/Timers.h>
 #include <utils/BitSet.h>
+#include <utils/Timers.h>
 
 namespace android {
 
@@ -30,6 +30,22 @@
  */
 class VelocityTracker {
 public:
+    enum class Strategy : int32_t {
+        DEFAULT = -1,
+        MIN = 0,
+        IMPULSE = 0,
+        LSQ1 = 1,
+        LSQ2 = 2,
+        LSQ3 = 3,
+        WLSQ2_DELTA = 4,
+        WLSQ2_CENTRAL = 5,
+        WLSQ2_RECENT = 6,
+        INT1 = 7,
+        INT2 = 8,
+        LEGACY = 9,
+        MAX = LEGACY,
+    };
+
     struct Position {
         float x, y;
     };
@@ -62,8 +78,8 @@
     };
 
     // Creates a velocity tracker using the specified strategy.
-    // If strategy is NULL, uses the default strategy for the platform.
-    VelocityTracker(const char* strategy = nullptr);
+    // If strategy is not provided, uses the default strategy for the platform.
+    VelocityTracker(const Strategy strategy = Strategy::DEFAULT);
 
     ~VelocityTracker();
 
@@ -102,16 +118,21 @@
     inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; }
 
 private:
-    static const char* DEFAULT_STRATEGY;
+    // The default velocity tracker strategy.
+    // Although other strategies are available for testing and comparison purposes,
+    // this is the strategy that applications will actually use.  Be very careful
+    // when adjusting the default strategy because it can dramatically affect
+    // (often in a bad way) the user experience.
+    static const Strategy DEFAULT_STRATEGY = Strategy::LSQ2;
 
     nsecs_t mLastEventTime;
     BitSet32 mCurrentPointerIdBits;
     int32_t mActivePointerId;
-    VelocityTrackerStrategy* mStrategy;
+    std::unique_ptr<VelocityTrackerStrategy> mStrategy;
 
-    bool configureStrategy(const char* strategy);
+    bool configureStrategy(const Strategy strategy);
 
-    static VelocityTrackerStrategy* createStrategy(const char* strategy);
+    static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy);
 };
 
 
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
new file mode 100644
index 0000000..44b8915
--- /dev/null
+++ b/include/powermanager/PowerHalController.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *                        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_POWERHALCONTROLLER_H
+#define ANDROID_POWERHALCONTROLLER_H
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+
+#include <powermanager/PowerHalWrapper.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::PowerHint;
+
+namespace android {
+
+// -------------------------------------------------------------------------------------------------
+
+// Connects to underlying Power HAL handles.
+class PowerHalConnector {
+public:
+    PowerHalConnector() = default;
+    virtual ~PowerHalConnector() = default;
+
+    virtual std::unique_ptr<PowerHalWrapper> connect();
+    virtual void reset();
+};
+
+// -------------------------------------------------------------------------------------------------
+
+// Controller for Power HAL handle.
+// This relies on PowerHalConnector to connect to the underlying Power HAL service and reconnects to
+// it after each failed api call. This also ensures connecting to the service is thread-safe.
+class PowerHalController : public PowerHalWrapper {
+public:
+    PowerHalController() : PowerHalController(std::make_unique<PowerHalConnector>()) {}
+    explicit PowerHalController(std::unique_ptr<PowerHalConnector> connector)
+        : mHalConnector(std::move(connector)) {}
+
+    void init();
+
+    PowerHalResult setBoost(Boost boost, int32_t durationMs) override;
+    PowerHalResult setMode(Mode mode, bool enabled) override;
+
+private:
+    std::mutex mConnectedHalMutex;
+    std::unique_ptr<PowerHalConnector> mHalConnector;
+
+    // Shared pointers to keep global pointer and allow local copies to be used in different threads
+    std::shared_ptr<PowerHalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex) = nullptr;
+    const std::shared_ptr<PowerHalWrapper> mDefaultHal = std::make_shared<EmptyPowerHalWrapper>();
+
+    std::shared_ptr<PowerHalWrapper> initHal();
+    PowerHalResult processHalResult(PowerHalResult result, const char* functionName);
+};
+
+// -------------------------------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_POWERHALCONTROLLER_H
diff --git a/include/ui/FatVector.h b/include/ui/FatVector.h
new file mode 120000
index 0000000..c2047c0
--- /dev/null
+++ b/include/ui/FatVector.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/FatVector.h
\ No newline at end of file
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index 2518b14..f66673f 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -35,4 +35,5 @@
             enabled: true,
         },
     },
+    min_sdk_version: "29",
 }
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 4f2709d..5e4c98f 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -98,15 +98,6 @@
     return PROCESS_STATE_UNKNOWN;
 }
 
-bool ActivityManager::isUidActiveOrForeground(const uid_t uid, const String16& callingPackage)
-{
-    sp<IActivityManager> service = getService();
-    if (service != nullptr) {
-        return service->isUidActiveOrForeground(uid, callingPackage);
-    }
-    return false;
-}
-
 status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
     sp<IActivityManager> service = getService();
     if (service != nullptr) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 7298282..db4aba8 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -28,6 +28,7 @@
         "libcutils_headers",
         "libutils_headers",
     ],
+    min_sdk_version: "29",
 }
 
 // These interfaces are android-specific implementation unrelated to binder
@@ -152,6 +153,7 @@
     sanitize: {
         misc_undefined: ["integer"],
     },
+    min_sdk_version: "29",
 }
 
 // AIDL interface between libbinder and framework.jar
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 9e1249b..1eb5363 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -104,18 +104,6 @@
         }
         return reply.readInt32();
     }
-
-    virtual bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage)
-    {
-         Parcel data, reply;
-         data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
-         data.writeInt32(uid);
-         data.writeString16(callingPackage);
-         remote()->transact(IS_UID_ACTIVE_OR_FOREGROUND_TRANSACTION, data, &reply);
-         // fail on exception
-         if (reply.readExceptionCode() != 0) return false;
-         return reply.readInt32() == 1;
-    }
 };
 
 // ------------------------------------------------------------------------------------
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 9888b59..dab7ad5 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -205,6 +205,10 @@
  : mTheRealServiceManager(impl)
 {}
 
+// This implementation could be simplified and made more efficient by delegating
+// to waitForService. However, this changes the threading structure in some
+// cases and could potentially break prebuilts. Once we have higher logistical
+// complexity, this could be attempted.
 sp<IBinder> ServiceManagerShim::getService(const String16& name) const
 {
     static bool gSystemBootCompleted = false;
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 0bb6d28..9108e31 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -46,24 +46,25 @@
         PROCESS_STATE_PERSISTENT = 0,
         PROCESS_STATE_PERSISTENT_UI = 1,
         PROCESS_STATE_TOP = 2,
-        PROCESS_STATE_BOUND_TOP = 3,
-        PROCESS_STATE_FOREGROUND_SERVICE = 4,
-        PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 5,
-        PROCESS_STATE_IMPORTANT_FOREGROUND = 6,
-        PROCESS_STATE_IMPORTANT_BACKGROUND = 7,
-        PROCESS_STATE_TRANSIENT_BACKGROUND = 8,
-        PROCESS_STATE_BACKUP = 9,
-        PROCESS_STATE_SERVICE = 10,
-        PROCESS_STATE_RECEIVER = 11,
-        PROCESS_STATE_TOP_SLEEPING = 12,
-        PROCESS_STATE_HEAVY_WEIGHT = 13,
-        PROCESS_STATE_HOME = 14,
-        PROCESS_STATE_LAST_ACTIVITY = 15,
-        PROCESS_STATE_CACHED_ACTIVITY = 16,
-        PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 17,
-        PROCESS_STATE_CACHED_RECENT = 18,
-        PROCESS_STATE_CACHED_EMPTY = 19,
-        PROCESS_STATE_NONEXISTENT = 20,
+        PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3,
+        PROCESS_STATE_BOUND_TOP = 4,
+        PROCESS_STATE_FOREGROUND_SERVICE = 5,
+        PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6,
+        PROCESS_STATE_IMPORTANT_FOREGROUND = 7,
+        PROCESS_STATE_IMPORTANT_BACKGROUND = 8,
+        PROCESS_STATE_TRANSIENT_BACKGROUND = 9,
+        PROCESS_STATE_BACKUP = 10,
+        PROCESS_STATE_SERVICE = 11,
+        PROCESS_STATE_RECEIVER = 12,
+        PROCESS_STATE_TOP_SLEEPING = 13,
+        PROCESS_STATE_HEAVY_WEIGHT = 14,
+        PROCESS_STATE_HOME = 15,
+        PROCESS_STATE_LAST_ACTIVITY = 16,
+        PROCESS_STATE_CACHED_ACTIVITY = 17,
+        PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18,
+        PROCESS_STATE_CACHED_RECENT = 19,
+        PROCESS_STATE_CACHED_EMPTY = 20,
+        PROCESS_STATE_NONEXISTENT = 21,
     };
 
     ActivityManager();
@@ -76,7 +77,6 @@
     void unregisterUidObserver(const sp<IUidObserver>& observer);
     bool isUidActive(const uid_t uid, const String16& callingPackage);
     int getUidProcessState(const uid_t uid, const String16& callingPackage);
-    bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage);
 
 
   status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index 1815ecc..e0248f6 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -39,15 +39,13 @@
     virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
     virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
     virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
-    virtual bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage) = 0;
 
     enum {
         OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
         REGISTER_UID_OBSERVER_TRANSACTION,
         UNREGISTER_UID_OBSERVER_TRANSACTION,
         IS_UID_ACTIVE_TRANSACTION,
-        GET_UID_PROCESS_STATE_TRANSACTION,
-        IS_UID_ACTIVE_OR_FOREGROUND_TRANSACTION,
+        GET_UID_PROCESS_STATE_TRANSACTION
     };
 };
 
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/tests/Android.bp
similarity index 100%
rename from libs/binder/ndk/test/Android.bp
rename to libs/binder/ndk/tests/Android.bp
diff --git a/libs/binder/ndk/test/AndroidTest.xml b/libs/binder/ndk/tests/AndroidTest.xml
similarity index 100%
rename from libs/binder/ndk/test/AndroidTest.xml
rename to libs/binder/ndk/tests/AndroidTest.xml
diff --git a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl b/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl
similarity index 100%
rename from libs/binder/ndk/test/IBinderNdkUnitTest.aidl
rename to libs/binder/ndk/tests/IBinderNdkUnitTest.aidl
diff --git a/libs/binder/ndk/test/IBinderVendorDoubleLoadTest.aidl b/libs/binder/ndk/tests/IBinderVendorDoubleLoadTest.aidl
similarity index 100%
rename from libs/binder/ndk/test/IBinderVendorDoubleLoadTest.aidl
rename to libs/binder/ndk/tests/IBinderVendorDoubleLoadTest.aidl
diff --git a/libs/binder/ndk/test/IEmpty.aidl b/libs/binder/ndk/tests/IEmpty.aidl
similarity index 100%
rename from libs/binder/ndk/test/IEmpty.aidl
rename to libs/binder/ndk/tests/IEmpty.aidl
diff --git a/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
similarity index 100%
rename from libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp
rename to libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp
diff --git a/libs/binder/ndk/test/iface.cpp b/libs/binder/ndk/tests/iface.cpp
similarity index 100%
rename from libs/binder/ndk/test/iface.cpp
rename to libs/binder/ndk/tests/iface.cpp
diff --git a/libs/binder/ndk/test/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h
similarity index 100%
rename from libs/binder/ndk/test/include/iface/iface.h
rename to libs/binder/ndk/tests/include/iface/iface.h
diff --git a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
similarity index 100%
rename from libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
rename to libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 69fdd7c..c0da2cd 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -27,7 +27,9 @@
     defaults: ["binder_test_defaults"],
     srcs: ["binderDriverInterfaceTest.cpp"],
     compile_multilib: "32",
+    multilib: { lib32: { suffix: "" } },
     cflags: ["-DBINDER_IPC_32BIT=1"],
+    test_suites: ["vts"],
 }
 
 cc_test {
@@ -52,7 +54,10 @@
         "libutils",
     ],
     compile_multilib: "32",
+    multilib: { lib32: { suffix: "" } },
     cflags: ["-DBINDER_IPC_32BIT=1"],
+    test_suites: ["vts"],
+    require_root: true,
 }
 
 cc_test {
diff --git a/libs/binder/tests/binderAbiHelper.h b/libs/binder/tests/binderAbiHelper.h
new file mode 100644
index 0000000..369b55d
--- /dev/null
+++ b/libs/binder/tests/binderAbiHelper.h
@@ -0,0 +1,52 @@
+/*
+ * 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 <stdlib.h>
+#include <iostream>
+
+#ifdef BINDER_IPC_32BIT
+static constexpr bool kBuild32Abi = true;
+#else
+static constexpr bool kBuild32Abi = false;
+#endif
+
+// TODO: remove when CONFIG_ANDROID_BINDER_IPC_32BIT is no longer supported
+static inline bool ReadKernelConfigIs32BitAbi() {
+    // failure case implies we run with standard ABI
+    return 0 == system("zcat /proc/config.gz | grep -E \"^CONFIG_ANDROID_BINDER_IPC_32BIT=y$\"");
+}
+
+static inline void ExitIfWrongAbi() {
+    bool runtime32Abi = ReadKernelConfigIs32BitAbi();
+
+    if (kBuild32Abi != runtime32Abi) {
+        std::cout << "[==========] Running 1 test from 1 test suite." << std::endl;
+        std::cout << "[----------] Global test environment set-up." << std::endl;
+        std::cout << "[----------] 1 tests from BinderLibTest" << std::endl;
+        std::cout << "[ RUN      ] BinderTest.AbortForWrongAbi" << std::endl;
+        std::cout << "[ INFO     ] test build abi 32: " << kBuild32Abi << " runtime abi 32: " << runtime32Abi << " so, skipping tests " << std::endl;
+        std::cout << "[       OK ] BinderTest.AbortForWrongAbi (0 ms) " << std::endl;
+        std::cout << "[----------] 1 tests from BinderTest (0 ms total)" << std::endl;
+        std::cout << "" << std::endl;
+        std::cout << "[----------] Global test environment tear-down" << std::endl;
+        std::cout << "[==========] 1 test from 1 test suite ran. (0 ms total)" << std::endl;
+        std::cout << "[  PASSED  ] 1 tests." << std::endl;
+        exit(0);
+    }
+}
+
diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp
index f3ed6a6..8cc3054 100644
--- a/libs/binder/tests/binderDriverInterfaceTest.cpp
+++ b/libs/binder/tests/binderDriverInterfaceTest.cpp
@@ -25,6 +25,8 @@
 #include <sys/mman.h>
 #include <poll.h>
 
+#include "binderAbiHelper.h"
+
 #define BINDER_DEV_NAME "/dev/binder"
 
 testing::Environment* binder_env;
@@ -361,6 +363,7 @@
 }
 
 int main(int argc, char **argv) {
+    ExitIfWrongAbi();
     ::testing::InitGoogleTest(&argc, argv);
 
     binder_env = AddGlobalTestEnvironment(new BinderDriverInterfaceTestEnv());
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index e343df7..40de2db 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -32,6 +32,8 @@
 #include <sys/epoll.h>
 #include <sys/prctl.h>
 
+#include "binderAbiHelper.h"
+
 #define ARRAY_SIZE(array) (sizeof array / sizeof array[0])
 
 using namespace android;
@@ -1451,6 +1453,8 @@
 }
 
 int main(int argc, char **argv) {
+    ExitIfWrongAbi();
+
     if (argc == 4 && !strcmp(argv[1], "--servername")) {
         binderservername = argv[2];
     } else {
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index 5eb509c..88752ee 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -22,7 +22,7 @@
 
     shared_libs: [
         "libbinder",
-        "libhidlbase",  // libhwbinder is in here
+        "libhidlbase", // libhwbinder is in here
     ],
 
     export_include_dirs: ["include"],
@@ -31,6 +31,7 @@
         "-Wall",
         "-Werror",
     ],
+    min_sdk_version: "29",
 }
 
 hidl_package_root {
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index e49fb08..cfc7e98 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -623,8 +623,7 @@
     mAngleNamespace = android_create_namespace("ANGLE",
                                                nullptr,            // ld_library_path
                                                mAnglePath.c_str(), // default_library_path
-                                               ANDROID_NAMESPACE_TYPE_SHARED |
-                                                       ANDROID_NAMESPACE_TYPE_ISOLATED,
+                                               ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED,
                                                nullptr, // permitted_when_isolated_path
                                                nullptr);
 
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 7f57f5d..7976ecb 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -27,6 +27,7 @@
         "android.hardware.graphics.bufferqueue@1.0",
         "android.hardware.graphics.bufferqueue@2.0",
     ],
+    min_sdk_version: "29",
 }
 
 cc_library_shared {
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index b0d9521..56591bd 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -19,8 +19,6 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <cutils/properties.h>
-
 #include <gui/BLASTBufferQueue.h>
 #include <gui/BufferItemConsumer.h>
 #include <gui/GLConsumer.h>
@@ -95,7 +93,8 @@
     if (needsDisconnect != nullptr) *needsDisconnect = disconnect;
 }
 
-BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height)
+BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height,
+                                   bool enableTripleBuffering)
       : mSurfaceControl(surface),
         mWidth(width),
         mHeight(height),
@@ -105,8 +104,7 @@
     // explicitly so that dequeueBuffer will block
     mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());
 
-    int8_t disableTripleBuffer = property_get_bool("ro.sf.disable_triple_buffer", 0);
-    if (!disableTripleBuffer) {
+    if (enableTripleBuffering) {
         mProducer->setMaxDequeuedBufferCount(2);
     }
     mBufferItemConsumer =
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index a1803d8..8d80833 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -28,7 +28,6 @@
 
 #include <inttypes.h>
 
-#include <cutils/properties.h>
 #include <cutils/atomic.h>
 
 #include <gui/BufferItem.h>
diff --git a/libs/gui/DebugEGLImageTracker.cpp b/libs/gui/DebugEGLImageTracker.cpp
index ab6f364..5762dab 100644
--- a/libs/gui/DebugEGLImageTracker.cpp
+++ b/libs/gui/DebugEGLImageTracker.cpp
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
-#include <cutils/properties.h>
 #include <gui/DebugEGLImageTracker.h>
 
 #include <cinttypes>
 #include <unordered_map>
 
+using android::base::GetBoolProperty;
 using android::base::StringAppendF;
 
 std::mutex DebugEGLImageTracker::mInstanceLock;
@@ -57,10 +58,7 @@
 DebugEGLImageTracker *DebugEGLImageTracker::getInstance() {
     std::lock_guard lock(mInstanceLock);
     if (mInstance == nullptr) {
-        char value[PROPERTY_VALUE_MAX];
-        property_get("debug.sf.enable_egl_image_tracker", value, "0");
-        const bool enabled = static_cast<bool>(atoi(value));
-
+        const bool enabled = GetBoolProperty("debug.sf.enable_egl_image_tracker", false);
         if (enabled) {
             mInstance = new DebugEGLImageTrackerImpl();
         } else {
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 15f966d..b33bc9e 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -36,10 +36,7 @@
 DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
                                                ISurfaceComposer::VsyncSource vsyncSource,
                                                ISurfaceComposer::ConfigChanged configChanged)
-      : mLooper(looper),
-        mReceiver(vsyncSource, configChanged),
-        mWaitingForVsync(false),
-        mConfigChangeFlag(configChanged) {
+      : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
     ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
 }
 
@@ -92,16 +89,12 @@
     return OK;
 }
 
-void DisplayEventDispatcher::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) {
-    if (mConfigChangeFlag == configChangeFlag) {
-        return;
-    }
-    status_t status = mReceiver.toggleConfigEvents(configChangeFlag);
+void DisplayEventDispatcher::requestLatestConfig() {
+    status_t status = mReceiver.requestLatestConfig();
     if (status) {
         ALOGW("Failed enable config events, status=%d", status);
         return;
     }
-    mConfigChangeFlag = configChangeFlag;
 }
 
 int DisplayEventDispatcher::getFd() const {
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index fd6aaf8..1fed509 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -79,10 +79,9 @@
     return NO_INIT;
 }
 
-status_t DisplayEventReceiver::toggleConfigEvents(
-        ISurfaceComposer::ConfigChanged configChangeFlag) {
+status_t DisplayEventReceiver::requestLatestConfig() {
     if (mEventConnection != nullptr) {
-        mEventConnection->toggleConfigEvents(configChangeFlag);
+        mEventConnection->requestLatestConfig();
         return NO_ERROR;
     }
     return NO_INIT;
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index dda5acf..aa74bfd 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -26,8 +26,8 @@
     STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
     SET_VSYNC_RATE,
     REQUEST_NEXT_VSYNC,
-    TOGGLE_CONFIG_EVENTS,
-    LAST = TOGGLE_CONFIG_EVENTS,
+    REQUEST_LATEST_CONFIG,
+    LAST = REQUEST_LATEST_CONFIG,
 };
 
 } // Anonymous namespace
@@ -55,10 +55,9 @@
                 Tag::REQUEST_NEXT_VSYNC);
     }
 
-    void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override {
-        callRemoteAsync<decltype(
-                &IDisplayEventConnection::toggleConfigEvents)>(Tag::TOGGLE_CONFIG_EVENTS,
-                                                               configChangeFlag);
+    void requestLatestConfig() override {
+        callRemoteAsync<decltype(&IDisplayEventConnection::requestLatestConfig)>(
+                Tag::REQUEST_LATEST_CONFIG);
     }
 };
 
@@ -81,8 +80,8 @@
             return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
         case Tag::REQUEST_NEXT_VSYNC:
             return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
-        case Tag::TOGGLE_CONFIG_EVENTS:
-            return callLocalAsync(data, reply, &IDisplayEventConnection::toggleConfigEvents);
+        case Tag::REQUEST_LATEST_CONFIG:
+            return callLocalAsync(data, reply, &IDisplayEventConnection::requestLatestConfig);
     }
 }
 
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 9483156..6c9fd07 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -929,8 +929,11 @@
     }
 
     virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                  int32_t defaultConfig, float minRefreshRate,
-                                                  float maxRefreshRate) {
+                                                  int32_t defaultConfig,
+                                                  float primaryRefreshRateMin,
+                                                  float primaryRefreshRateMax,
+                                                  float appRequestRefreshRateMin,
+                                                  float appRequestRefreshRateMax) {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         if (result != NO_ERROR) {
@@ -947,14 +950,26 @@
             ALOGE("setDesiredDisplayConfigSpecs failed to write defaultConfig: %d", result);
             return result;
         }
-        result = data.writeFloat(minRefreshRate);
+        result = data.writeFloat(primaryRefreshRateMin);
         if (result != NO_ERROR) {
-            ALOGE("setDesiredDisplayConfigSpecs failed to write minRefreshRate: %d", result);
+            ALOGE("setDesiredDisplayConfigSpecs failed to write primaryRefreshRateMin: %d", result);
             return result;
         }
-        result = data.writeFloat(maxRefreshRate);
+        result = data.writeFloat(primaryRefreshRateMax);
         if (result != NO_ERROR) {
-            ALOGE("setDesiredDisplayConfigSpecs failed to write maxRefreshRate: %d", result);
+            ALOGE("setDesiredDisplayConfigSpecs failed to write primaryRefreshRateMax: %d", result);
+            return result;
+        }
+        result = data.writeFloat(appRequestRefreshRateMin);
+        if (result != NO_ERROR) {
+            ALOGE("setDesiredDisplayConfigSpecs failed to write appRequestRefreshRateMin: %d",
+                  result);
+            return result;
+        }
+        result = data.writeFloat(appRequestRefreshRateMax);
+        if (result != NO_ERROR) {
+            ALOGE("setDesiredDisplayConfigSpecs failed to write appRequestRefreshRateMax: %d",
+                  result);
             return result;
         }
 
@@ -969,9 +984,14 @@
 
     virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
                                                   int32_t* outDefaultConfig,
-                                                  float* outMinRefreshRate,
-                                                  float* outMaxRefreshRate) {
-        if (!outDefaultConfig || !outMinRefreshRate || !outMaxRefreshRate) return BAD_VALUE;
+                                                  float* outPrimaryRefreshRateMin,
+                                                  float* outPrimaryRefreshRateMax,
+                                                  float* outAppRequestRefreshRateMin,
+                                                  float* outAppRequestRefreshRateMax) {
+        if (!outDefaultConfig || !outPrimaryRefreshRateMin || !outPrimaryRefreshRateMax ||
+            !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) {
+            return BAD_VALUE;
+        }
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         if (result != NO_ERROR) {
@@ -994,14 +1014,26 @@
             ALOGE("getDesiredDisplayConfigSpecs failed to read defaultConfig: %d", result);
             return result;
         }
-        result = reply.readFloat(outMinRefreshRate);
+        result = reply.readFloat(outPrimaryRefreshRateMin);
         if (result != NO_ERROR) {
-            ALOGE("getDesiredDisplayConfigSpecs failed to read minRefreshRate: %d", result);
+            ALOGE("getDesiredDisplayConfigSpecs failed to read primaryRefreshRateMin: %d", result);
             return result;
         }
-        result = reply.readFloat(outMaxRefreshRate);
+        result = reply.readFloat(outPrimaryRefreshRateMax);
         if (result != NO_ERROR) {
-            ALOGE("getDesiredDisplayConfigSpecs failed to read maxRefreshRate: %d", result);
+            ALOGE("getDesiredDisplayConfigSpecs failed to read primaryRefreshRateMax: %d", result);
+            return result;
+        }
+        result = reply.readFloat(outAppRequestRefreshRateMin);
+        if (result != NO_ERROR) {
+            ALOGE("getDesiredDisplayConfigSpecs failed to read appRequestRefreshRateMin: %d",
+                  result);
+            return result;
+        }
+        result = reply.readFloat(outAppRequestRefreshRateMax);
+        if (result != NO_ERROR) {
+            ALOGE("getDesiredDisplayConfigSpecs failed to read appRequestRefreshRateMax: %d",
+                  result);
             return result;
         }
         return reply.readInt32();
@@ -1060,22 +1092,22 @@
         return NO_ERROR;
     }
 
-    virtual status_t notifyPowerHint(int32_t hintId) {
+    virtual status_t notifyPowerBoost(int32_t boostId) {
         Parcel data, reply;
         status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         if (error != NO_ERROR) {
-            ALOGE("notifyPowerHint: failed to write interface token: %d", error);
+            ALOGE("notifyPowerBoost: failed to write interface token: %d", error);
             return error;
         }
-        error = data.writeInt32(hintId);
+        error = data.writeInt32(boostId);
         if (error != NO_ERROR) {
-            ALOGE("notifyPowerHint: failed to write hintId: %d", error);
+            ALOGE("notifyPowerBoost: failed to write boostId: %d", error);
             return error;
         }
-        error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_HINT, data, &reply,
+        error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_BOOST, data, &reply,
                                    IBinder::FLAG_ONEWAY);
         if (error != NO_ERROR) {
-            ALOGE("notifyPowerHint: failed to transact: %d", error);
+            ALOGE("notifyPowerBoost: failed to transact: %d", error);
             return error;
         }
         return NO_ERROR;
@@ -1831,20 +1863,38 @@
                 ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultConfig: %d", result);
                 return result;
             }
-            float minRefreshRate;
-            result = data.readFloat(&minRefreshRate);
+            float primaryRefreshRateMin;
+            result = data.readFloat(&primaryRefreshRateMin);
             if (result != NO_ERROR) {
-                ALOGE("setDesiredDisplayConfigSpecs: failed to read minRefreshRate: %d", result);
+                ALOGE("setDesiredDisplayConfigSpecs: failed to read primaryRefreshRateMin: %d",
+                      result);
                 return result;
             }
-            float maxRefreshRate;
-            result = data.readFloat(&maxRefreshRate);
+            float primaryRefreshRateMax;
+            result = data.readFloat(&primaryRefreshRateMax);
             if (result != NO_ERROR) {
-                ALOGE("setDesiredDisplayConfigSpecs: failed to read maxRefreshRate: %d", result);
+                ALOGE("setDesiredDisplayConfigSpecs: failed to read primaryRefreshRateMax: %d",
+                      result);
                 return result;
             }
-            result = setDesiredDisplayConfigSpecs(displayToken, defaultConfig, minRefreshRate,
-                                                  maxRefreshRate);
+            float appRequestRefreshRateMin;
+            result = data.readFloat(&appRequestRefreshRateMin);
+            if (result != NO_ERROR) {
+                ALOGE("setDesiredDisplayConfigSpecs: failed to read appRequestRefreshRateMin: %d",
+                      result);
+                return result;
+            }
+            float appRequestRefreshRateMax;
+            result = data.readFloat(&appRequestRefreshRateMax);
+            if (result != NO_ERROR) {
+                ALOGE("setDesiredDisplayConfigSpecs: failed to read appRequestRefreshRateMax: %d",
+                      result);
+                return result;
+            }
+            result =
+                    setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin,
+                                                 primaryRefreshRateMax, appRequestRefreshRateMin,
+                                                 appRequestRefreshRateMax);
             if (result != NO_ERROR) {
                 ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: "
                       "%d",
@@ -1858,11 +1908,16 @@
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> displayToken = data.readStrongBinder();
             int32_t defaultConfig;
-            float minRefreshRate;
-            float maxRefreshRate;
+            float primaryRefreshRateMin;
+            float primaryRefreshRateMax;
+            float appRequestRefreshRateMin;
+            float appRequestRefreshRateMax;
 
-            status_t result = getDesiredDisplayConfigSpecs(displayToken, &defaultConfig,
-                                                           &minRefreshRate, &maxRefreshRate);
+            status_t result =
+                    getDesiredDisplayConfigSpecs(displayToken, &defaultConfig,
+                                                 &primaryRefreshRateMin, &primaryRefreshRateMax,
+                                                 &appRequestRefreshRateMin,
+                                                 &appRequestRefreshRateMax);
             if (result != NO_ERROR) {
                 ALOGE("getDesiredDisplayConfigSpecs: failed to get getDesiredDisplayConfigSpecs: "
                       "%d",
@@ -1875,14 +1930,28 @@
                 ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultConfig: %d", result);
                 return result;
             }
-            result = reply->writeFloat(minRefreshRate);
+            result = reply->writeFloat(primaryRefreshRateMin);
             if (result != NO_ERROR) {
-                ALOGE("getDesiredDisplayConfigSpecs: failed to write minRefreshRate: %d", result);
+                ALOGE("getDesiredDisplayConfigSpecs: failed to write primaryRefreshRateMin: %d",
+                      result);
                 return result;
             }
-            result = reply->writeFloat(maxRefreshRate);
+            result = reply->writeFloat(primaryRefreshRateMax);
             if (result != NO_ERROR) {
-                ALOGE("getDesiredDisplayConfigSpecs: failed to write maxRefreshRate: %d", result);
+                ALOGE("getDesiredDisplayConfigSpecs: failed to write primaryRefreshRateMax: %d",
+                      result);
+                return result;
+            }
+            result = reply->writeFloat(appRequestRefreshRateMin);
+            if (result != NO_ERROR) {
+                ALOGE("getDesiredDisplayConfigSpecs: failed to write appRequestRefreshRateMin: %d",
+                      result);
+                return result;
+            }
+            result = reply->writeFloat(appRequestRefreshRateMax);
+            if (result != NO_ERROR) {
+                ALOGE("getDesiredDisplayConfigSpecs: failed to write appRequestRefreshRateMax: %d",
+                      result);
                 return result;
             }
             reply->writeInt32(result);
@@ -1917,15 +1986,15 @@
             }
             return setDisplayBrightness(displayToken, brightness);
         }
-        case NOTIFY_POWER_HINT: {
+        case NOTIFY_POWER_BOOST: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            int32_t hintId;
-            status_t error = data.readInt32(&hintId);
+            int32_t boostId;
+            status_t error = data.readInt32(&boostId);
             if (error != NO_ERROR) {
-                ALOGE("notifyPowerHint: failed to read hintId: %d", error);
+                ALOGE("notifyPowerBoost: failed to read boostId: %d", error);
                 return error;
             }
-            return notifyPowerHint(hintId);
+            return notifyPowerBoost(boostId);
         }
         case SET_GLOBAL_SHADOW_SETTINGS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index f7158d0..e43446a 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -116,6 +116,7 @@
     output.writeInt32(frameRateSelectionPriority);
     output.writeFloat(frameRate);
     output.writeByte(frameRateCompatibility);
+    output.writeUint32(fixedTransformHint);
     return NO_ERROR;
 }
 
@@ -198,6 +199,7 @@
     frameRateSelectionPriority = input.readInt32();
     frameRate = input.readFloat();
     frameRateCompatibility = input.readByte();
+    fixedTransformHint = static_cast<ui::Transform::RotationFlags>(input.readUint32());
     return NO_ERROR;
 }
 
@@ -433,6 +435,10 @@
         frameRate = other.frameRate;
         frameRateCompatibility = other.frameRateCompatibility;
     }
+    if (other.what & eFixedTransformHintChanged) {
+        what |= eFixedTransformHintChanged;
+        fixedTransformHint = other.fixedTransformHint;
+    }
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
               "other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index b77dfda..cbb4b97 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -1,6 +1,7 @@
 adyabr@google.com
 akrulec@google.com
 alecmouri@google.com
+chaviw@google.com
 chrisforbes@google.com
 jessehall@google.com
 lpy@google.com
@@ -9,3 +10,6 @@
 steventhomas@google.com
 stoza@google.com
 vhau@google.com
+vishnun@google.com
+
+per-file EndToEndNativeInputTest.cpp = svv@google.com
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index f911e70..2bf8ff7 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -57,7 +57,8 @@
     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;
+            op == NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR ||
+            op == NATIVE_WINDOW_SET_QUERY_INTERCEPTOR;
 }
 
 } // namespace
@@ -501,6 +502,19 @@
 
 int Surface::hook_query(const ANativeWindow* window, int what, int* value) {
     const Surface* c = getSelf(window);
+    {
+        std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+        if (c->mQueryInterceptor != nullptr) {
+            auto interceptor = c->mQueryInterceptor;
+            auto data = c->mQueryInterceptorData;
+            return interceptor(window, Surface::queryInternal, data, what, value);
+        }
+    }
+    return c->query(what, value);
+}
+
+int Surface::queryInternal(const ANativeWindow* window, int what, int* value) {
+    const Surface* c = getSelf(window);
     return c->query(what, value);
 }
 
@@ -1177,6 +1191,9 @@
     case NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR:
         res = dispatchAddQueueInterceptor(args);
         break;
+    case NATIVE_WINDOW_SET_QUERY_INTERCEPTOR:
+        res = dispatchAddQueryInterceptor(args);
+        break;
     case NATIVE_WINDOW_ALLOCATE_BUFFERS:
         allocateBuffers();
         res = NO_ERROR;
@@ -1457,6 +1474,15 @@
     return NO_ERROR;
 }
 
+int Surface::dispatchAddQueryInterceptor(va_list args) {
+    ANativeWindow_queryInterceptor interceptor = va_arg(args, ANativeWindow_queryInterceptor);
+    void* data = va_arg(args, void*);
+    std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+    mQueryInterceptor = interceptor;
+    mQueryInterceptorData = data;
+    return NO_ERROR;
+}
+
 int Surface::dispatchGetLastQueuedBuffer(va_list args) {
     AHardwareBuffer** buffer = va_arg(args, AHardwareBuffer**);
     int* fence = va_arg(args, int*);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index d9cbeb7..88232fa 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1437,6 +1437,22 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixedTransformHint(
+        const sp<SurfaceControl>& sc, int32_t fixedTransformHint) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    const ui::Transform::RotationFlags transform = fixedTransformHint == -1
+            ? ui::Transform::ROT_INVALID
+            : ui::Transform::toRotationFlags(static_cast<ui::Rotation>(fixedTransformHint));
+    s->what |= layer_state_t::eFixedTransformHintChanged;
+    s->fixedTransformHint = transform;
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -1698,22 +1714,26 @@
 
 status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
                                                              int32_t defaultConfig,
-                                                             float minRefreshRate,
-                                                             float maxRefreshRate) {
-    return ComposerService::getComposerService()->setDesiredDisplayConfigSpecs(displayToken,
-                                                                               defaultConfig,
-                                                                               minRefreshRate,
-                                                                               maxRefreshRate);
+                                                             float primaryRefreshRateMin,
+                                                             float primaryRefreshRateMax,
+                                                             float appRequestRefreshRateMin,
+                                                             float appRequestRefreshRateMax) {
+    return ComposerService::getComposerService()
+            ->setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin,
+                                           primaryRefreshRateMax, appRequestRefreshRateMin,
+                                           appRequestRefreshRateMax);
 }
 
 status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
                                                              int32_t* outDefaultConfig,
-                                                             float* outMinRefreshRate,
-                                                             float* outMaxRefreshRate) {
-    return ComposerService::getComposerService()->getDesiredDisplayConfigSpecs(displayToken,
-                                                                               outDefaultConfig,
-                                                                               outMinRefreshRate,
-                                                                               outMaxRefreshRate);
+                                                             float* outPrimaryRefreshRateMin,
+                                                             float* outPrimaryRefreshRateMax,
+                                                             float* outAppRequestRefreshRateMin,
+                                                             float* outAppRequestRefreshRateMax) {
+    return ComposerService::getComposerService()
+            ->getDesiredDisplayConfigSpecs(displayToken, outDefaultConfig, outPrimaryRefreshRateMin,
+                                           outPrimaryRefreshRateMax, outAppRequestRefreshRateMin,
+                                           outAppRequestRefreshRateMax);
 }
 
 status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display,
@@ -1842,8 +1862,8 @@
     return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness);
 }
 
-status_t SurfaceComposerClient::notifyPowerHint(int32_t hintId) {
-    return ComposerService::getComposerService()->notifyPowerHint(hintId);
+status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) {
+    return ComposerService::getComposerService()->notifyPowerBoost(boostId);
 }
 
 status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor,
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index b902615..2320771 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -66,7 +66,9 @@
     : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener
 {
 public:
-    BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height);
+    BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height,
+                     bool enableTripleBuffering = true);
+
     sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
         return mProducer;
     }
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index fcdf6bf..f210c34 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -31,7 +31,7 @@
     status_t initialize();
     void dispose();
     status_t scheduleVsync();
-    void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag);
+    void requestLatestConfig();
     int getFd() const;
     virtual int handleEvent(int receiveFd, int events, void* data);
 
@@ -42,7 +42,6 @@
     sp<Looper> mLooper;
     DisplayEventReceiver mReceiver;
     bool mWaitingForVsync;
-    ISurfaceComposer::ConfigChanged mConfigChangeFlag;
 
     virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
     virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index d9a0253..8d49184 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -147,9 +147,10 @@
     status_t requestNextVsync();
 
     /*
-     * toggleConfigEvents() toggles delivery of config change events.
+     * requestLatestConfig() force-requests the current config for the primary
+     * display.
      */
-    status_t toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag);
+    status_t requestLatestConfig();
 
 private:
     sp<IDisplayEventConnection> mEventConnection;
diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h
index 8b35ef6..674aafd 100644
--- a/libs/gui/include/gui/IDisplayEventConnection.h
+++ b/libs/gui/include/gui/IDisplayEventConnection.h
@@ -53,11 +53,9 @@
     virtual void requestNextVsync() = 0; // Asynchronous
 
     /*
-     * togglesConfigEvents() configures whether or not display config changes
-     * should be propagated.
+     * requestLatestConfig() requests the config for the primary display.
      */
-    virtual void toggleConfigEvents(
-            ISurfaceComposer::ConfigChanged configChangeFlag) = 0; // Asynchronous
+    virtual void requestLatestConfig() = 0; // Asynchronous
 };
 
 class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index b4a3fbe..21ad2b2 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -426,19 +426,36 @@
      */
     virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
 
-    /*
-     * Sets the refresh rate boundaries for display configuration.
-     * For all other parameters, default configuration is used. The index for the default is
-     * corresponding to the configs returned from getDisplayConfigs().
+    /* Sets the refresh rate boundaries for the display.
+     *
+     * The primary refresh rate range represents display manager's general guidance on the display
+     * configs we'll consider when switching refresh rates. Unless we get an explicit signal from an
+     * app, we should stay within this range.
+     *
+     * The app request refresh rate range allows us to consider more display configs when switching
+     * refresh rates. Although we should generally stay within the primary range, specific
+     * considerations, such as layer frame rate settings specified via the setFrameRate() api, may
+     * cause us to go outside the primary range. We never go outside the app request range. The app
+     * request range will be greater than or equal to the primary refresh rate range, never smaller.
+     *
+     * defaultConfig is used to narrow the list of display configs SurfaceFlinger will consider
+     * switching between. Only configs with a config group and resolution matching defaultConfig
+     * will be considered for switching. The defaultConfig index corresponds to the list of configs
+     * returned from getDisplayConfigs().
      */
     virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                  int32_t defaultConfig, float minRefreshRate,
-                                                  float maxRefreshRate) = 0;
+                                                  int32_t defaultConfig,
+                                                  float primaryRefreshRateMin,
+                                                  float primaryRefreshRateMax,
+                                                  float appRequestRefreshRateMin,
+                                                  float appRequestRefreshRateMax) = 0;
 
     virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
                                                   int32_t* outDefaultConfig,
-                                                  float* outMinRefreshRate,
-                                                  float* outMaxRefreshRate) = 0;
+                                                  float* outPrimaryRefreshRateMin,
+                                                  float* outPrimaryRefreshRateMax,
+                                                  float* outAppRequestRefreshRateMin,
+                                                  float* outAppRequestRefreshRateMax) = 0;
     /*
      * Gets whether brightness operations are supported on a display.
      *
@@ -471,14 +488,14 @@
     virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) = 0;
 
     /*
-     * Sends a power hint to the composer. This function is asynchronous.
+     * Sends a power boost to the composer. This function is asynchronous.
      *
-     * hintId
-     *      hint id according to android::hardware::power::V1_0::PowerHint
+     * boostId
+     *      boost id according to android::hardware::power::Boost
      *
      * Returns NO_ERROR upon success.
      */
-    virtual status_t notifyPowerHint(int32_t hintId) = 0;
+    virtual status_t notifyPowerBoost(int32_t boostId) = 0;
 
     /*
      * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
@@ -566,7 +583,7 @@
         GET_DISPLAY_BRIGHTNESS_SUPPORT,
         SET_DISPLAY_BRIGHTNESS,
         CAPTURE_SCREEN_BY_ID,
-        NOTIFY_POWER_HINT,
+        NOTIFY_POWER_BOOST,
         SET_GLOBAL_SHADOW_SETTINGS,
         GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
         SET_AUTO_LOW_LATENCY_MODE,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 2b2f773..e60f677 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -35,6 +35,7 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Rotation.h>
+#include <ui/Transform.h>
 #include <utils/Errors.h>
 
 namespace android {
@@ -103,6 +104,7 @@
         eFrameRateChanged = 0x40'00000000,
         eBackgroundBlurRadiusChanged = 0x80'00000000,
         eProducerDisconnect = 0x100'00000000,
+        eFixedTransformHintChanged = 0x200'00000000,
     };
 
     layer_state_t()
@@ -136,7 +138,8 @@
             shadowRadius(0.0f),
             frameRateSelectionPriority(-1),
             frameRate(0.0f),
-            frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT) {
+            frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT),
+            fixedTransformHint(ui::Transform::ROT_INVALID) {
         matrix.dsdx = matrix.dtdy = 1.0f;
         matrix.dsdy = matrix.dtdx = 0.0f;
         hdrMetadata.validTypes = 0;
@@ -225,6 +228,15 @@
     // Layer frame rate and compatibility. See ANativeWindow_setFrameRate().
     float frameRate;
     int8_t frameRateCompatibility;
+
+    // Set by window manager indicating the layer and all its children are
+    // in a different orientation than the display. The hint suggests that
+    // the graphic producers should receive a transform hint as if the
+    // display was in this orientation. When the display changes to match
+    // the layer orientation, the graphic producer may not need to allocate
+    // a buffer of a different size. -1 means the transform hint is not set,
+    // otherwise the value will be a valid ui::Rotation.
+    ui::Transform::RotationFlags fixedTransformHint;
 };
 
 struct ComposerState {
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 917c0d4..49c83da 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -209,6 +209,7 @@
                                      int* fenceFd);
     static int performInternal(ANativeWindow* window, int operation, va_list args);
     static int queueBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+    static int queryInternal(const ANativeWindow* window, int what, int* value);
 
     static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
             ANativeWindowBuffer* buffer);
@@ -261,6 +262,7 @@
     int dispatchAddDequeueInterceptor(va_list args);
     int dispatchAddPerformInterceptor(va_list args);
     int dispatchAddQueueInterceptor(va_list args);
+    int dispatchAddQueryInterceptor(va_list args);
     int dispatchGetLastQueuedBuffer(va_list args);
     bool transformToDisplayInverse();
 
@@ -468,7 +470,7 @@
     mutable Mutex mMutex;
 
     // mInterceptorMutex is the mutex guarding interceptors.
-    std::shared_mutex mInterceptorMutex;
+    mutable std::shared_mutex mInterceptorMutex;
 
     ANativeWindow_cancelBufferInterceptor mCancelInterceptor = nullptr;
     void* mCancelInterceptorData = nullptr;
@@ -478,6 +480,8 @@
     void* mPerformInterceptorData = nullptr;
     ANativeWindow_queueBufferInterceptor mQueueInterceptor = nullptr;
     void* mQueueInterceptorData = nullptr;
+    ANativeWindow_queryInterceptor mQueryInterceptor = nullptr;
+    void* mQueryInterceptorData = nullptr;
 
     // must be used from the lock/unlock thread
     sp<GraphicBuffer>           mLockedBuffer;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2fb9538..32c0bab 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -118,21 +118,19 @@
     // 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().
+    // Sets the refresh rate boundaries for the display.
     static status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                 int32_t defaultConfig, float minRefreshRate,
-                                                 float maxRefreshRate);
-    // Gets 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().
-    // The reason is passed in for telemetry tracking, and it corresponds to the list of all
-    // the policy rules that were used.
+                                                 int32_t defaultConfig, float primaryRefreshRateMin,
+                                                 float primaryRefreshRateMax,
+                                                 float appRequestRefreshRateMin,
+                                                 float appRequestRefreshRateMax);
+    // Gets the refresh rate boundaries for the display.
     static status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
                                                  int32_t* outDefaultConfig,
-                                                 float* outMinRefreshRate,
-                                                 float* outMaxRefreshRate);
+                                                 float* outPrimaryRefreshRateMin,
+                                                 float* outPrimaryRefreshRateMax,
+                                                 float* outAppRequestRefreshRateMin,
+                                                 float* outAppRequestRefreshRateMax);
 
     // Gets the list of supported color modes for the given display
     static status_t getDisplayColorModes(const sp<IBinder>& display,
@@ -219,14 +217,14 @@
     static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness);
 
     /*
-     * Sends a power hint to the composer. This function is asynchronous.
+     * Sends a power boost to the composer. This function is asynchronous.
      *
-     * hintId
-     *      hint id according to android::hardware::power::V1_0::PowerHint
+     * boostId
+     *      boost id according to android::hardware::power::Boost
      *
      * Returns NO_ERROR upon success.
      */
-    static status_t notifyPowerHint(int32_t hintId);
+    static status_t notifyPowerBoost(int32_t boostId);
 
     /*
      * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
@@ -521,6 +519,14 @@
         Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
                                   int8_t compatibility);
 
+        // Set by window manager indicating the layer and all its children are
+        // in a different orientation than the display. The hint suggests that
+        // the graphic producers should receive a transform hint as if the
+        // display was in this orientation. When the display changes to match
+        // the layer orientation, the graphic producer may not need to allocate
+        // a buffer of a different size.
+        Transaction& setFixedTransformHint(const sp<SurfaceControl>& sc, int32_t transformHint);
+
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
 
diff --git a/libs/gui/sysprop/Android.bp b/libs/gui/sysprop/Android.bp
index e7f7c1f..64b1eac 100644
--- a/libs/gui/sysprop/Android.bp
+++ b/libs/gui/sysprop/Android.bp
@@ -4,4 +4,7 @@
     api_packages: ["android.sysprop"],
     property_owner: "Platform",
     vendor_available: true,
+    cpp: {
+        min_sdk_version: "29",
+    },
 }
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 7f960ab..a6bcd10 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -39,6 +39,7 @@
     shared_libs: [
         "android.hardware.configstore@1.0",
         "android.hardware.configstore-utils",
+        "libSurfaceFlingerProp",
         "libbase",
         "liblog",
         "libEGL",
@@ -53,6 +54,8 @@
         "libutils",
         "libnativewindow"
     ],
+
+    header_libs: ["libsurfaceflinger_headers"],
 }
 
 // Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index c59afba..5188a09 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -178,7 +178,7 @@
         mInputInfo.name = "Test info";
         mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
         mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
-        mInputInfo.dispatchingTimeout = 100000;
+        mInputInfo.dispatchingTimeout = seconds_to_nanoseconds(5);
         mInputInfo.globalScaleFactor = 1.0;
         mInputInfo.canReceiveKeys = true;
         mInputInfo.hasFocus = true;
@@ -196,7 +196,7 @@
         InputApplicationInfo aInfo;
         aInfo.token = new BBinder();
         aInfo.name = "Test app info";
-        aInfo.dispatchingTimeout = 100000;
+        aInfo.dispatchingTimeout = seconds_to_nanoseconds(5);
 
         mInputInfo.applicationInfo = aInfo;
     }
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 2de6b69..3b80945 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -18,17 +18,17 @@
 
 #include <gtest/gtest.h>
 
+#include <SurfaceFlingerProperties.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <binder/ProcessState.h>
 #include <configstore/Utils.h>
-#include <cutils/properties.h>
-#include <inttypes.h>
 #include <gui/BufferItemConsumer.h>
 #include <gui/IDisplayEventConnection.h>
 #include <gui/IProducerListener.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
+#include <inttypes.h>
 #include <private/gui/ComposerService.h>
 #include <ui/Rect.h>
 #include <utils/String8.h>
@@ -46,11 +46,9 @@
 
 using Transaction = SurfaceComposerClient::Transaction;
 
-static bool hasWideColorDisplay =
-        getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+static bool hasWideColorDisplay = android::sysprop::has_wide_color_display(false);
 
-static bool hasHdrDisplay =
-        getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(false);
+static bool hasHdrDisplay = android::sysprop::has_HDR_display(false);
 
 class FakeSurfaceComposer;
 class FakeProducerFrameEventHistory;
@@ -836,17 +834,22 @@
         return NO_ERROR;
     }
     status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
-                                          int32_t /*defaultConfig*/, float /*minRefreshRate*/,
-                                          float /*maxRefreshRate*/) {
+                                          int32_t /*defaultConfig*/,
+                                          float /*primaryRefreshRateMin*/,
+                                          float /*primaryRefreshRateMax*/,
+                                          float /*appRequestRefreshRateMin*/,
+                                          float /*appRequestRefreshRateMax*/) {
         return NO_ERROR;
     }
     status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
                                           int32_t* /*outDefaultConfig*/,
-                                          float* /*outMinRefreshRate*/,
-                                          float* /*outMaxRefreshRate*/) override {
+                                          float* /*outPrimaryRefreshRateMin*/,
+                                          float* /*outPrimaryRefreshRateMax*/,
+                                          float* /*outAppRequestRefreshRateMin*/,
+                                          float* /*outAppRequestRefreshRateMax*/) override {
         return NO_ERROR;
     };
-    status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; }
+    status_t notifyPowerBoost(int32_t /*boostId*/) override { return NO_ERROR; }
 
     status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/,
                                      float /*lightPosY*/, float /*lightPosZ*/,
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index ef7cc7d..11af23e 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -468,7 +468,7 @@
     msg.body.key.deviceId = deviceId;
     msg.body.key.source = source;
     msg.body.key.displayId = displayId;
-    msg.body.key.hmac = hmac;
+    msg.body.key.hmac = std::move(hmac);
     msg.body.key.action = action;
     msg.body.key.flags = flags;
     msg.body.key.keyCode = keyCode;
@@ -526,7 +526,7 @@
     msg.body.motion.deviceId = deviceId;
     msg.body.motion.source = source;
     msg.body.motion.displayId = displayId;
-    msg.body.motion.hmac = hmac;
+    msg.body.motion.hmac = std::move(hmac);
     msg.body.motion.action = action;
     msg.body.motion.actionButton = actionButton;
     msg.body.motion.flags = flags;
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index 998cef6..3d3bec8 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -155,18 +155,19 @@
             && y >= frameTop && y < frameBottom;
 }
 
+// TODO(b/155781676): Remove and replace call points with trustedOverlay when that is ready.
 bool InputWindowInfo::isTrustedOverlay() const {
-    return layoutParamsType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY
-            || layoutParamsType == TYPE_INPUT_METHOD
-            || layoutParamsType == TYPE_INPUT_METHOD_DIALOG
-            || layoutParamsType == TYPE_MAGNIFICATION_OVERLAY
-            || layoutParamsType == TYPE_STATUS_BAR
-            || layoutParamsType == TYPE_NAVIGATION_BAR
-            || layoutParamsType == TYPE_NAVIGATION_BAR_PANEL
-            || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY
-            || layoutParamsType == TYPE_DOCK_DIVIDER
-            || layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY
-            || layoutParamsType == TYPE_INPUT_CONSUMER;
+    return layoutParamsType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY ||
+            layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG ||
+            layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR ||
+            layoutParamsType == TYPE_NOTIFICATION_SHADE ||
+            layoutParamsType == TYPE_NAVIGATION_BAR ||
+            layoutParamsType == TYPE_NAVIGATION_BAR_PANEL ||
+            layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY ||
+            layoutParamsType == TYPE_DOCK_DIVIDER ||
+            layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY ||
+            layoutParamsType == TYPE_INPUT_CONSUMER ||
+            layoutParamsType == TYPE_TRUSTED_APPLICATION_OVERLAY;
 }
 
 bool InputWindowInfo::supportsSplitTouch() const {
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index c6cc4fc..7c28ac5 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -104,107 +104,73 @@
 
 // --- VelocityTracker ---
 
-// The default velocity tracker strategy.
-// Although other strategies are available for testing and comparison purposes,
-// this is the strategy that applications will actually use.  Be very careful
-// when adjusting the default strategy because it can dramatically affect
-// (often in a bad way) the user experience.
-const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
-
-VelocityTracker::VelocityTracker(const char* strategy) :
-        mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
-    char value[PROPERTY_VALUE_MAX];
-
-    // Allow the default strategy to be overridden using a system property for debugging.
-    if (!strategy) {
-        int length = property_get("persist.input.velocitytracker.strategy", value, nullptr);
-        if (length > 0) {
-            strategy = value;
-        } else {
-            strategy = DEFAULT_STRATEGY;
-        }
-    }
-
+VelocityTracker::VelocityTracker(const Strategy strategy)
+      : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
     // Configure the strategy.
     if (!configureStrategy(strategy)) {
-        ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy);
-        if (!configureStrategy(DEFAULT_STRATEGY)) {
-            LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!",
-                    strategy);
+        ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy);
+        if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) {
+            LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32
+                             "'!",
+                             strategy);
         }
     }
 }
 
 VelocityTracker::~VelocityTracker() {
-    delete mStrategy;
 }
 
-bool VelocityTracker::configureStrategy(const char* strategy) {
-    mStrategy = createStrategy(strategy);
+bool VelocityTracker::configureStrategy(Strategy strategy) {
+    if (strategy == VelocityTracker::Strategy::DEFAULT) {
+        mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY);
+    } else {
+        mStrategy = createStrategy(strategy);
+    }
     return mStrategy != nullptr;
 }
 
-VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
-    if (!strcmp("impulse", strategy)) {
-        // Physical model of pushing an object.  Quality: VERY GOOD.
-        // Works with duplicate coordinates, unclean finger liftoff.
-        return new ImpulseVelocityTrackerStrategy();
-    }
-    if (!strcmp("lsq1", strategy)) {
-        // 1st order least squares.  Quality: POOR.
-        // Frequently underfits the touch data especially when the finger accelerates
-        // or changes direction.  Often underestimates velocity.  The direction
-        // is overly influenced by historical touch points.
-        return new LeastSquaresVelocityTrackerStrategy(1);
-    }
-    if (!strcmp("lsq2", strategy)) {
-        // 2nd order least squares.  Quality: VERY GOOD.
-        // Pretty much ideal, but can be confused by certain kinds of touch data,
-        // particularly if the panel has a tendency to generate delayed,
-        // duplicate or jittery touch coordinates when the finger is released.
-        return new LeastSquaresVelocityTrackerStrategy(2);
-    }
-    if (!strcmp("lsq3", strategy)) {
-        // 3rd order least squares.  Quality: UNUSABLE.
-        // Frequently overfits the touch data yielding wildly divergent estimates
-        // of the velocity when the finger is released.
-        return new LeastSquaresVelocityTrackerStrategy(3);
-    }
-    if (!strcmp("wlsq2-delta", strategy)) {
-        // 2nd order weighted least squares, delta weighting.  Quality: EXPERIMENTAL
-        return new LeastSquaresVelocityTrackerStrategy(2,
-                LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA);
-    }
-    if (!strcmp("wlsq2-central", strategy)) {
-        // 2nd order weighted least squares, central weighting.  Quality: EXPERIMENTAL
-        return new LeastSquaresVelocityTrackerStrategy(2,
-                LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL);
-    }
-    if (!strcmp("wlsq2-recent", strategy)) {
-        // 2nd order weighted least squares, recent weighting.  Quality: EXPERIMENTAL
-        return new LeastSquaresVelocityTrackerStrategy(2,
-                LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT);
-    }
-    if (!strcmp("int1", strategy)) {
-        // 1st order integrating filter.  Quality: GOOD.
-        // Not as good as 'lsq2' because it cannot estimate acceleration but it is
-        // more tolerant of errors.  Like 'lsq1', this strategy tends to underestimate
-        // the velocity of a fling but this strategy tends to respond to changes in
-        // direction more quickly and accurately.
-        return new IntegratingVelocityTrackerStrategy(1);
-    }
-    if (!strcmp("int2", strategy)) {
-        // 2nd order integrating filter.  Quality: EXPERIMENTAL.
-        // For comparison purposes only.  Unlike 'int1' this strategy can compensate
-        // for acceleration but it typically overestimates the effect.
-        return new IntegratingVelocityTrackerStrategy(2);
-    }
-    if (!strcmp("legacy", strategy)) {
-        // Legacy velocity tracker algorithm.  Quality: POOR.
-        // For comparison purposes only.  This algorithm is strongly influenced by
-        // old data points, consistently underestimates velocity and takes a very long
-        // time to adjust to changes in direction.
-        return new LegacyVelocityTrackerStrategy();
+std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
+        VelocityTracker::Strategy strategy) {
+    switch (strategy) {
+        case VelocityTracker::Strategy::IMPULSE:
+            return std::make_unique<ImpulseVelocityTrackerStrategy>();
+
+        case VelocityTracker::Strategy::LSQ1:
+            return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
+
+        case VelocityTracker::Strategy::LSQ2:
+            return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2);
+
+        case VelocityTracker::Strategy::LSQ3:
+            return std::make_unique<LeastSquaresVelocityTrackerStrategy>(3);
+
+        case VelocityTracker::Strategy::WLSQ2_DELTA:
+            return std::make_unique<
+                    LeastSquaresVelocityTrackerStrategy>(2,
+                                                         LeastSquaresVelocityTrackerStrategy::
+                                                                 WEIGHTING_DELTA);
+        case VelocityTracker::Strategy::WLSQ2_CENTRAL:
+            return std::make_unique<
+                    LeastSquaresVelocityTrackerStrategy>(2,
+                                                         LeastSquaresVelocityTrackerStrategy::
+                                                                 WEIGHTING_CENTRAL);
+        case VelocityTracker::Strategy::WLSQ2_RECENT:
+            return std::make_unique<
+                    LeastSquaresVelocityTrackerStrategy>(2,
+                                                         LeastSquaresVelocityTrackerStrategy::
+                                                                 WEIGHTING_RECENT);
+
+        case VelocityTracker::Strategy::INT1:
+            return std::make_unique<IntegratingVelocityTrackerStrategy>(1);
+
+        case VelocityTracker::Strategy::INT2:
+            return std::make_unique<IntegratingVelocityTrackerStrategy>(2);
+
+        case VelocityTracker::Strategy::LEGACY:
+            return std::make_unique<LegacyVelocityTrackerStrategy>();
+
+        default:
+            break;
     }
     return nullptr;
 }
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index bf452c0..249d9d4 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -191,8 +191,9 @@
     return events;
 }
 
-static void computeAndCheckVelocity(const char* strategy,
-        const std::vector<MotionEventEntry>& motions, int32_t axis, float targetVelocity) {
+static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy,
+                                    const std::vector<MotionEventEntry>& motions, int32_t axis,
+                                    float targetVelocity) {
     VelocityTracker vt(strategy);
     float Vx, Vy;
 
@@ -217,7 +218,7 @@
 
 static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions,
         const std::array<float, 3>& coefficients) {
-    VelocityTracker vt("lsq2");
+    VelocityTracker vt(VelocityTracker::Strategy::LSQ2);
     std::vector<MotionEvent> events = createMotionEventStream(motions);
     for (MotionEvent event : events) {
         vt.addMovement(&event);
@@ -243,7 +244,8 @@
         {14730us, {{293, NAN}}},
         {14730us, {{293, NAN}}}, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1600);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            1600);
 }
 
 TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) {
@@ -254,8 +256,8 @@
         { 11283us, {{293, NAN}} },
         { 11283us, {{293, NAN}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 0);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0);
 }
 
 TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) {
@@ -266,8 +268,8 @@
         { 20ms, {{10, NAN}} },
         { 20ms, {{10, NAN}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 500);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 500);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 500);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500);
 }
 
 
@@ -297,8 +299,10 @@
         { 96948871ns, {{274.79245, 428.113159}} },
         { 96948871ns, {{274.79245, 428.113159}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 623.577637);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 5970.7309);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            623.577637);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            5970.7309);
 }
 
 // --------------- Recorded by hand on sailfish, generated by a script -----------------------------
@@ -339,10 +343,14 @@
         { 235089162955851ns, {{560.66, 843.82}} },
         { 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 872.794617);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 951.698181);
-    computeAndCheckVelocity("impulse",motions, AMOTION_EVENT_AXIS_Y, -3604.819336);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3044.966064);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            872.794617);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            951.698181);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -3604.819336);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -3044.966064);
 }
 
 
@@ -368,8 +376,10 @@
         { 235110660368000ns, {{530.00, 980.00}} },
         { 235110660368000ns, {{530.00, 980.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4096.583008);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3455.094238);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -4096.583008);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -3455.094238);
 }
 
 
@@ -396,10 +406,14 @@
         { 792629200000ns, {{619.00, 1115.00}} },
         { 792629200000ns, {{619.00, 1115.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 574.33429);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 617.40564);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -2361.982666);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -2500.055664);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            574.33429);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            617.40564);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -2361.982666);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -2500.055664);
 }
 
 
@@ -426,10 +440,14 @@
         { 235160520366000ns, {{679.00, 814.00}} },
         { 235160520366000ns, {{679.00, 814.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1274.141724);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 1438.53186);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -3001.4348);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3695.859619);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            1274.141724);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            1438.53186);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -3001.4348);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -3695.859619);
 }
 
 
@@ -452,8 +470,10 @@
         { 847237986000ns, {{610.00, 1095.00}} },
         { 847237986000ns, {{610.00, 1095.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4280.07959);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -4241.004395);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -4280.07959);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -4241.004395);
 }
 
 
@@ -476,8 +496,10 @@
         { 235200616933000ns, {{590.00, 844.00}} },
         { 235200616933000ns, {{590.00, 844.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -8715.686523);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -7639.026367);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -8715.686523);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -7639.026367);
 }
 
 
@@ -499,10 +521,14 @@
         { 920989261000ns, {{715.00, 903.00}} },
         { 920989261000ns, {{715.00, 903.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 5670.329102);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 5991.866699);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -13021.101562);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -15093.995117);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            5670.329102);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            5991.866699);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -13021.101562);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -15093.995117);
 }
 
 
@@ -522,8 +548,10 @@
         { 235247220736000ns, {{620.00, 641.00}} },
         { 235247220736000ns, {{620.00, 641.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -20286.958984);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -20494.587891);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -20286.958984);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -20494.587891);
 }
 
 
@@ -541,8 +569,10 @@
         { 235302613019881ns, {{679.26, 526.73}} },
         { 235302613019881ns, {{679.26, 526.73}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -39295.941406);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -36461.421875);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            -39295.941406);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            -36461.421875);
 }
 
 
@@ -569,10 +599,14 @@
         { 235655842893000ns, {{563.00, 649.00}} },
         { 235655842893000ns, {{563.00, 649.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -419.749695);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -398.303894);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3309.016357);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 3969.099854);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            -419.749695);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            -398.303894);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            3309.016357);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            3969.099854);
 }
 
 
@@ -599,10 +633,14 @@
         { 235671246532000ns, {{470.00, 799.00}} },
         { 235671246532000ns, {{470.00, 799.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -262.80426);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -243.665344);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4215.682129);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4587.986816);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            -262.80426);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            -243.665344);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            4215.682129);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            4587.986816);
 }
 
 
@@ -622,10 +660,14 @@
         { 171051052000ns, {{536.00, 586.00}} },
         { 171051052000ns, {{536.00, 586.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -723.413513);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -651.038452);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 2091.502441);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 1934.517456);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            -723.413513);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            -651.038452);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            2091.502441);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            1934.517456);
 }
 
 
@@ -652,8 +694,10 @@
         { 235695373403000ns, {{564.00, 744.00}} },
         { 235695373403000ns, {{564.00, 744.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4254.639648);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4698.415039);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            4254.639648);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            4698.415039);
 }
 
 
@@ -677,10 +721,14 @@
         { 235709710626776ns, {{511.72, 741.85}} },
         { 235709710626776ns, {{511.72, 741.85}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -430.440247);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -447.600311);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3953.859375);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4316.155273);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            -430.440247);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            -447.600311);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            3953.859375);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            4316.155273);
 }
 
 
@@ -706,8 +754,10 @@
         { 235727721580000ns, {{516.00, 658.00}} },
         { 235727721580000ns, {{516.00, 658.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4484.617676);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4927.92627);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            4484.617676);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            4927.92627);
 }
 
 
@@ -725,8 +775,10 @@
         { 235762396429369ns, {{404.37, 680.67}} },
         { 235762396429369ns, {{404.37, 680.67}} }, //ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 14227.0224);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16064.685547);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            14227.0224);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            16064.685547);
 }
 
 
@@ -744,8 +796,10 @@
         { 235772537635000ns, {{484.00, 589.00}} },
         { 235772537635000ns, {{484.00, 589.00}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 18660.048828);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16918.439453);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            18660.048828);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            16918.439453);
 }
 
 
@@ -764,10 +818,14 @@
         { 507703352649ns, {{443.71, 857.77}} },
         { 507703352649ns, {{443.71, 857.77}} }, // ACTION_UP
     };
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -4111.8173);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -6388.48877);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 29765.908203);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 28354.796875);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
+                            -4111.8173);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
+                            -6388.48877);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
+                            29765.908203);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y,
+                            28354.796875);
 }
 
 /**
@@ -789,10 +847,10 @@
 
     // Velocity should actually be zero, but we expect 0.016 here instead.
     // This is close enough to zero, and is likely caused by division by a very small number.
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -0.016);
-    computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -0.016);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0);
-    computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 0);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, -0.016);
+    computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, -0.016);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0);
+    computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, 0);
 }
 
 /**
diff --git a/libs/nativebase/Android.bp b/libs/nativebase/Android.bp
index 7375a2b..9e7e4a2 100644
--- a/libs/nativebase/Android.bp
+++ b/libs/nativebase/Android.bp
@@ -25,5 +25,6 @@
         windows: {
             enabled: true,
         },
-    }
+    },
+    min_sdk_version: "29",
 }
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 0ff33ac..e458b2e 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -17,24 +17,63 @@
 #define LOG_TAG "Choreographer"
 //#define LOG_NDEBUG 0
 
-#include <apex/choreographer.h>
+#include <android-base/thread_annotations.h>
 #include <gui/DisplayEventDispatcher.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
+#include <nativehelper/JNIHelp.h>
+#include <private/android/choreographer.h>
 #include <utils/Looper.h>
-#include <utils/Mutex.h>
 #include <utils/Timers.h>
 
 #include <cinttypes>
+#include <mutex>
 #include <optional>
 #include <queue>
 #include <thread>
 
-namespace android {
+namespace {
+struct {
+    // Global JVM that is provided by zygote
+    JavaVM* jvm = nullptr;
+    struct {
+        jclass clazz;
+        jmethodID getInstance;
+        jmethodID registerNativeChoreographerForRefreshRateCallbacks;
+        jmethodID unregisterNativeChoreographerForRefreshRateCallbacks;
+    } displayManagerGlobal;
+} gJni;
 
-static inline const char* toString(bool value) {
+// Gets the JNIEnv* for this thread, and performs one-off initialization if we
+// have never retrieved a JNIEnv* pointer before.
+JNIEnv* getJniEnv() {
+    if (gJni.jvm == nullptr) {
+        ALOGW("AChoreographer: No JVM provided!");
+        return nullptr;
+    }
+
+    JNIEnv* env = nullptr;
+    if (gJni.jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
+        ALOGD("Attaching thread to JVM for AChoreographer");
+        JavaVMAttachArgs args = {JNI_VERSION_1_4, "AChoreographer_env", NULL};
+        jint attachResult = gJni.jvm->AttachCurrentThreadAsDaemon(&env, (void*)&args);
+        if (attachResult != JNI_OK) {
+            ALOGE("Unable to attach thread. Error: %d", attachResult);
+            return nullptr;
+        }
+    }
+    if (env == nullptr) {
+        ALOGW("AChoreographer: No JNI env available!");
+    }
+    return env;
+}
+
+inline const char* toString(bool value) {
     return value ? "true" : "false";
 }
+} // namespace
+
+namespace android {
 
 struct FrameCallback {
     AChoreographer_frameCallback callback;
@@ -52,24 +91,43 @@
 struct RefreshRateCallback {
     AChoreographer_refreshRateCallback callback;
     void* data;
+    bool firstCallbackFired = false;
 };
 
+class Choreographer;
+
+struct {
+    std::mutex lock;
+    std::vector<Choreographer*> ptrs GUARDED_BY(lock);
+    bool registeredToDisplayManager GUARDED_BY(lock) = false;
+
+    std::atomic<nsecs_t> mLastKnownVsync = -1;
+} gChoreographers;
+
 class Choreographer : public DisplayEventDispatcher, public MessageHandler {
 public:
-    explicit Choreographer(const sp<Looper>& looper);
+    explicit Choreographer(const sp<Looper>& looper) EXCLUDES(gChoreographers.lock);
     void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
                                   AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
-    void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
+    void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
+            EXCLUDES(gChoreographers.lock);
     void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
+    // Drains the queue of pending vsync periods and dispatches refresh rate
+    // updates to callbacks.
+    // The assumption is that this method is only called on a single
+    // processing thread, either by looper or by AChoreographer_handleEvents
+    void handleRefreshRateUpdates();
+    void scheduleLatestConfigRequest();
 
     enum {
         MSG_SCHEDULE_CALLBACKS = 0,
-        MSG_SCHEDULE_VSYNC = 1
+        MSG_SCHEDULE_VSYNC = 1,
+        MSG_HANDLE_REFRESH_RATE_UPDATES = 2,
     };
     virtual void handleMessage(const Message& message) override;
 
     static Choreographer* getForThread();
-    virtual ~Choreographer() = default;
+    virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
 
 private:
     Choreographer(const Choreographer&) = delete;
@@ -81,21 +139,17 @@
 
     void scheduleCallbacks();
 
+    std::mutex mLock;
     // Protected by mLock
     std::priority_queue<FrameCallback> mFrameCallbacks;
-
-    // Protected by mLock
     std::vector<RefreshRateCallback> mRefreshRateCallbacks;
-    nsecs_t mVsyncPeriod = 0;
 
-    mutable Mutex mLock;
+    nsecs_t mLatestVsyncPeriod = -1;
 
     const sp<Looper> mLooper;
     const std::thread::id mThreadId;
-    const std::optional<PhysicalDisplayId> mInternalDisplayId;
 };
 
-
 static thread_local Choreographer* gChoreographer;
 Choreographer* Choreographer::getForThread() {
     if (gChoreographer == nullptr) {
@@ -115,17 +169,47 @@
 }
 
 Choreographer::Choreographer(const sp<Looper>& looper)
-      : DisplayEventDispatcher(looper),
+      : DisplayEventDispatcher(looper, ISurfaceComposer::VsyncSource::eVsyncSourceApp,
+                               ISurfaceComposer::ConfigChanged::eConfigChangedDispatch),
         mLooper(looper),
-        mThreadId(std::this_thread::get_id()),
-        mInternalDisplayId(SurfaceComposerClient::getInternalDisplayId()) {}
+        mThreadId(std::this_thread::get_id()) {
+    std::lock_guard<std::mutex> _l(gChoreographers.lock);
+    gChoreographers.ptrs.push_back(this);
+}
+
+Choreographer::~Choreographer() {
+    std::lock_guard<std::mutex> _l(gChoreographers.lock);
+    gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(),
+                                              gChoreographers.ptrs.end(),
+                                              [=](Choreographer* c) { return c == this; }),
+                               gChoreographers.ptrs.end());
+    // Only poke DisplayManagerGlobal to unregister if we previously registered
+    // callbacks.
+    if (gChoreographers.ptrs.empty() && gChoreographers.registeredToDisplayManager) {
+        JNIEnv* env = getJniEnv();
+        if (env == nullptr) {
+            ALOGW("JNI environment is unavailable, skipping choreographer cleanup");
+            return;
+        }
+        jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+                                                  gJni.displayManagerGlobal.getInstance);
+        if (dmg == nullptr) {
+            ALOGW("DMS is not initialized yet, skipping choreographer cleanup");
+        } else {
+            env->CallVoidMethod(dmg,
+                                gJni.displayManagerGlobal
+                                        .unregisterNativeChoreographerForRefreshRateCallbacks);
+            env->DeleteLocalRef(dmg);
+        }
+    }
+}
 
 void Choreographer::postFrameCallbackDelayed(
         AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
     FrameCallback callback{cb, cb64, data, now + delay};
     {
-        AutoMutex _l{mLock};
+        std::lock_guard<std::mutex> _l{mLock};
         mFrameCallbacks.push(callback);
     }
     if (callback.dueTime <= now) {
@@ -150,37 +234,68 @@
 }
 
 void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
+    std::lock_guard<std::mutex> _l{mLock};
+    for (const auto& callback : mRefreshRateCallbacks) {
+        // Don't re-add callbacks.
+        if (cb == callback.callback && data == callback.data) {
+            return;
+        }
+    }
+    mRefreshRateCallbacks.emplace_back(
+            RefreshRateCallback{.callback = cb, .data = data, .firstCallbackFired = false});
+    bool needsRegistration = false;
     {
-        AutoMutex _l{mLock};
-        for (const auto& callback : mRefreshRateCallbacks) {
-            // Don't re-add callbacks.
-            if (cb == callback.callback && data == callback.data) {
-                return;
+        std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+        needsRegistration = !gChoreographers.registeredToDisplayManager;
+    }
+    if (needsRegistration) {
+        JNIEnv* env = getJniEnv();
+        if (env == nullptr) {
+            ALOGW("JNI environment is unavailable, skipping registration");
+            return;
+        }
+        jobject dmg = env->CallStaticObjectMethod(gJni.displayManagerGlobal.clazz,
+                                                  gJni.displayManagerGlobal.getInstance);
+        if (dmg == nullptr) {
+            ALOGW("DMS is not initialized yet: skipping registration");
+            return;
+        } else {
+            env->CallVoidMethod(dmg,
+                                gJni.displayManagerGlobal
+                                        .registerNativeChoreographerForRefreshRateCallbacks,
+                                reinterpret_cast<int64_t>(this));
+            env->DeleteLocalRef(dmg);
+            {
+                std::lock_guard<std::mutex> _l2(gChoreographers.lock);
+                gChoreographers.registeredToDisplayManager = true;
             }
         }
-        mRefreshRateCallbacks.emplace_back(RefreshRateCallback{cb, data});
-        toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedDispatch);
+    } else {
+        scheduleLatestConfigRequest();
     }
 }
 
 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 &&
-                                                               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;
-        }
+    std::lock_guard<std::mutex> _l{mLock};
+    mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
+                                               mRefreshRateCallbacks.end(),
+                                               [&](const RefreshRateCallback& callback) {
+                                                   return cb == callback.callback &&
+                                                           data == callback.data;
+                                               }),
+                                mRefreshRateCallbacks.end());
+}
+
+void Choreographer::scheduleLatestConfigRequest() {
+    if (mLooper != nullptr) {
+        Message m{MSG_HANDLE_REFRESH_RATE_UPDATES};
+        mLooper->sendMessage(this, m);
+    } else {
+        // If the looper thread is detached from Choreographer, then refresh rate
+        // changes will be handled in AChoreographer_handlePendingEvents, so we
+        // need to redispatch a config from SF
+        requestLatestConfig();
     }
 }
 
@@ -188,7 +303,7 @@
     const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
     nsecs_t dueTime;
     {
-        AutoMutex _{mLock};
+        std::lock_guard<std::mutex> _l{mLock};
         // If there are no pending callbacks then don't schedule a vsync
         if (mFrameCallbacks.empty()) {
             return;
@@ -203,13 +318,35 @@
     }
 }
 
+void Choreographer::handleRefreshRateUpdates() {
+    std::vector<RefreshRateCallback> callbacks{};
+    const nsecs_t pendingPeriod = gChoreographers.mLastKnownVsync.load();
+    const nsecs_t lastPeriod = mLatestVsyncPeriod;
+    if (pendingPeriod > 0) {
+        mLatestVsyncPeriod = pendingPeriod;
+    }
+    {
+        std::lock_guard<std::mutex> _l{mLock};
+        for (auto& cb : mRefreshRateCallbacks) {
+            callbacks.push_back(cb);
+            cb.firstCallbackFired = true;
+        }
+    }
+
+    for (auto& cb : callbacks) {
+        if (!cb.firstCallbackFired || (pendingPeriod > 0 && pendingPeriod != lastPeriod)) {
+            cb.callback(pendingPeriod, cb.data);
+        }
+    }
+}
+
 // TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
 // internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
 // the internal display implicitly.
 void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
     std::vector<FrameCallback> callbacks{};
     {
-        AutoMutex _l{mLock};
+        std::lock_guard<std::mutex> _l{mLock};
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
         while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
             callbacks.push_back(mFrameCallbacks.top());
@@ -236,20 +373,29 @@
 // display, so as such Choreographer does not support the notion of multiple
 // displays. When multi-display choreographer is properly supported, then
 // PhysicalDisplayId should no longer be ignored.
-void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId, int32_t,
+void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId,
                                           nsecs_t vsyncPeriod) {
+    ALOGV("choreographer %p ~ received config change event "
+          "(displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%d).",
+          this, displayId, configId);
+
+    const nsecs_t lastPeriod = mLatestVsyncPeriod;
+    std::vector<RefreshRateCallback> callbacks{};
     {
-        AutoMutex _l{mLock};
-        for (const auto& cb : mRefreshRateCallbacks) {
-            // Only perform the callback when the old refresh rate is different
-            // from the new refresh rate, so that we don't dispatch the callback
-            // on every single configuration change.
-            if (mVsyncPeriod != vsyncPeriod) {
-                cb.callback(vsyncPeriod, cb.data);
-            }
+        std::lock_guard<std::mutex> _l{mLock};
+        for (auto& cb : mRefreshRateCallbacks) {
+            callbacks.push_back(cb);
+            cb.firstCallbackFired = true;
         }
-        mVsyncPeriod = vsyncPeriod;
     }
+
+    for (auto& cb : callbacks) {
+        if (!cb.firstCallbackFired || (vsyncPeriod > 0 && vsyncPeriod != lastPeriod)) {
+            cb.callback(vsyncPeriod, cb.data);
+        }
+    }
+
+    mLatestVsyncPeriod = vsyncPeriod;
 }
 
 void Choreographer::handleMessage(const Message& message) {
@@ -260,19 +406,80 @@
     case MSG_SCHEDULE_VSYNC:
         scheduleVsync();
         break;
+    case MSG_HANDLE_REFRESH_RATE_UPDATES:
+        handleRefreshRateUpdates();
+        break;
     }
 }
 
-}
-
-/* Glue for the NDK interface */
-
+} // namespace android
 using namespace android;
 
 static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
     return reinterpret_cast<Choreographer*>(choreographer);
 }
 
+// Glue for private C api
+namespace android {
+void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) {
+    std::lock_guard<std::mutex> _l(gChoreographers.lock);
+    gChoreographers.mLastKnownVsync.store(vsyncPeriod);
+    for (auto c : gChoreographers.ptrs) {
+        c->scheduleLatestConfigRequest();
+    }
+}
+
+void AChoreographer_initJVM(JNIEnv* env) {
+    env->GetJavaVM(&gJni.jvm);
+    // Now we need to find the java classes.
+    jclass dmgClass = env->FindClass("android/hardware/display/DisplayManagerGlobal");
+    gJni.displayManagerGlobal.clazz = static_cast<jclass>(env->NewGlobalRef(dmgClass));
+    gJni.displayManagerGlobal.getInstance =
+            env->GetStaticMethodID(dmgClass, "getInstance",
+                                   "()Landroid/hardware/display/DisplayManagerGlobal;");
+    gJni.displayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks =
+            env->GetMethodID(dmgClass, "registerNativeChoreographerForRefreshRateCallbacks", "()V");
+    gJni.displayManagerGlobal.unregisterNativeChoreographerForRefreshRateCallbacks =
+            env->GetMethodID(dmgClass, "unregisterNativeChoreographerForRefreshRateCallbacks",
+                             "()V");
+}
+
+AChoreographer* AChoreographer_routeGetInstance() {
+    return AChoreographer_getInstance();
+}
+void AChoreographer_routePostFrameCallback(AChoreographer* choreographer,
+                                           AChoreographer_frameCallback callback, void* data) {
+    return AChoreographer_postFrameCallback(choreographer, callback, data);
+}
+void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer,
+                                                  AChoreographer_frameCallback callback, void* data,
+                                                  long delayMillis) {
+    return AChoreographer_postFrameCallbackDelayed(choreographer, callback, data, delayMillis);
+}
+void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer,
+                                             AChoreographer_frameCallback64 callback, void* data) {
+    return AChoreographer_postFrameCallback64(choreographer, callback, data);
+}
+void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer,
+                                                    AChoreographer_frameCallback64 callback,
+                                                    void* data, uint32_t delayMillis) {
+    return AChoreographer_postFrameCallbackDelayed64(choreographer, callback, data, delayMillis);
+}
+void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
+                                                     AChoreographer_refreshRateCallback callback,
+                                                     void* data) {
+    return AChoreographer_registerRefreshRateCallback(choreographer, callback, data);
+}
+void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer,
+                                                       AChoreographer_refreshRateCallback callback,
+                                                       void* data) {
+    return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data);
+}
+
+} // namespace android
+
+/* Glue for the NDK interface */
+
 static inline const Choreographer* AChoreographer_to_Choreographer(
         const AChoreographer* choreographer) {
     return reinterpret_cast<const Choreographer*>(choreographer);
@@ -343,5 +550,6 @@
     // Pass dummy fd and events args to handleEvent, since the underlying
     // DisplayEventDispatcher doesn't need them outside of validating that a
     // Looper instance didn't break, but these args circumvent those checks.
-    AChoreographer_to_Choreographer(choreographer)->handleEvent(-1, Looper::EVENT_INPUT, data);
+    Choreographer* impl = AChoreographer_to_Choreographer(choreographer);
+    impl->handleEvent(-1, Looper::EVENT_INPUT, data);
 }
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index c956578..f56b3a2 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -12,23 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-ndk_headers {
-    name: "libnativedisplay_ndk_headers",
-    from: "include/android",
-    to: "android",
-    srcs: ["include/android/*.h"],
-    license: "NOTICE",
-}
-
 cc_library_headers {
     name: "libnativedisplay_headers",
-    export_include_dirs: ["include"],
+    export_include_dirs: ["include",],
 }
 
-cc_library {
+cc_library_shared {
     name: "libnativedisplay",
     export_include_dirs: [
         "include",
+        "include-private",
     ],
 
     clang: true,
@@ -63,6 +56,10 @@
         "libnativehelper",
     ],
 
+    export_shared_lib_headers: [
+        "libnativehelper",
+    ],
+
     header_libs: [
         "libnativedisplay_headers",
     ],
diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h
new file mode 100644
index 0000000..2164930
--- /dev/null
+++ b/libs/nativedisplay/include-private/private/android/choreographer.h
@@ -0,0 +1,55 @@
+/*
+ * 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 <apex/choreographer.h>
+#include <inttypes.h>
+#include <nativehelper/JNIHelp.h>
+
+namespace android {
+
+// Registers the global JVM for AChoreographer
+void AChoreographer_initJVM(JNIEnv* env);
+
+// Signals all AChoregorapher* instances that a new vsync period is available
+// for consumption by callbacks.
+void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod);
+
+// Trampoline functions allowing libandroid.so to define the NDK symbols without including
+// the entirety of libnativedisplay as a whole static lib. As libnativedisplay
+// maintains global state, libnativedisplay can never be directly statically
+// linked so that global state won't be duplicated. This way libandroid.so can
+// reroute the NDK methods into the implementations defined by libnativedisplay
+AChoreographer* AChoreographer_routeGetInstance();
+void AChoreographer_routePostFrameCallback(AChoreographer* choreographer,
+                                           AChoreographer_frameCallback callback, void* data);
+void AChoreographer_routePostFrameCallbackDelayed(AChoreographer* choreographer,
+                                                  AChoreographer_frameCallback callback, void* data,
+                                                  long delayMillis);
+void AChoreographer_routePostFrameCallback64(AChoreographer* choreographer,
+                                             AChoreographer_frameCallback64 callback, void* data);
+void AChoreographer_routePostFrameCallbackDelayed64(AChoreographer* choreographer,
+                                                    AChoreographer_frameCallback64 callback,
+                                                    void* data, uint32_t delayMillis);
+void AChoreographer_routeRegisterRefreshRateCallback(AChoreographer* choreographer,
+                                                     AChoreographer_refreshRateCallback callback,
+                                                     void* data);
+void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreographer,
+                                                       AChoreographer_refreshRateCallback callback,
+                                                       void* data);
+
+} // namespace android
diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
index 6a94a77..e2d036b 100644
--- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
+++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
@@ -19,7 +19,7 @@
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
-
+#include <nativehelper/JNIHelp.h>
 #include <system/graphics.h>
 
 // This file provides a facade API on top of SurfaceTexture, which avoids using
@@ -30,6 +30,20 @@
 
 namespace android {
 
+// Trampoline functions allowing libandroid.so to define the NDK symbols without including
+// the entirety of libnativedisplay as a whole static lib. As libnativedisplay
+// maintains global state, libnativedisplay can never be directly statically
+// linked so that global state won't be duplicated. This way libandroid.so can
+// reroute the NDK methods into the implementations defined by libnativedisplay
+ANativeWindow* ASurfaceTexture_routeAcquireANativeWindow(ASurfaceTexture* st);
+int ASurfaceTexture_routeAttachToGLContext(ASurfaceTexture* st, uint32_t texName);
+int ASurfaceTexture_routeDetachFromGLContext(ASurfaceTexture* st);
+void ASurfaceTexture_routeRelease(ASurfaceTexture* st);
+int ASurfaceTexture_routeUpdateTexImage(ASurfaceTexture* st);
+void ASurfaceTexture_routeGetTransformMatrix(ASurfaceTexture* st, float mtx[16]);
+int64_t ASurfaceTexture_routeGetTimestamp(ASurfaceTexture* st);
+ASurfaceTexture* ASurfaceTexture_routeFromSurfaceTexture(JNIEnv* env, jobject surfacetexture);
+
 /**
  * ASurfaceTexture_getCurrentTextureTarget returns the texture target of the
  * current texture.
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 483fb25..fc59431 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -20,6 +20,15 @@
 LIBNATIVEDISPLAY_PLATFORM {
   global:
     extern "C++" {
+      android::AChoreographer_initJVM*;
+      android::AChoreographer_routeGetInstance*;
+      android::AChoreographer_routePostFrameCallback*;
+      android::AChoreographer_routePostFrameCallbackDelayed*;
+      android::AChoreographer_routePostFrameCallback64*;
+      android::AChoreographer_routePostFrameCallbackDelayed64*;
+      android::AChoreographer_routeRegisterRefreshRateCallback*;
+      android::AChoreographer_routeUnregisterRefreshRateCallback*;
+      android::AChoreographer_signalRefreshRateCallbacks*;
       android::ADisplay_acquirePhysicalDisplays*;
       android::ADisplay_release*;
       android::ADisplay_getMaxSupportedFps*;
@@ -36,6 +45,14 @@
       android::ASurfaceTexture_takeConsumerOwnership*;
       android::ASurfaceTexture_releaseConsumerOwnership*;
       android::ASurfaceTexture_dequeueBuffer*;
+      android::ASurfaceTexture_routeAcquireANativeWindow*;
+      android::ASurfaceTexture_routeAttachToGLContext*;
+      android::ASurfaceTexture_routeDetachFromGLContext*;
+      android::ASurfaceTexture_routeGetTimestamp*;
+      android::ASurfaceTexture_routeGetTransformMatrix*;
+      android::ASurfaceTexture_routeUpdateTexImage*;
+      android::ASurfaceTexture_routeFromSurfaceTexture*;
+      android::ASurfaceTexture_routeRelease*;
       android::SurfaceTexture*;
     };
     ASurfaceTexture_acquireANativeWindow;
diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp
index 1670fbb..d1bcd8d 100644
--- a/libs/nativedisplay/surfacetexture/surface_texture.cpp
+++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp
@@ -149,6 +149,37 @@
 
 // The following functions are private/unstable API.
 namespace android {
+ANativeWindow* ASurfaceTexture_routeAcquireANativeWindow(ASurfaceTexture* st) {
+    return ASurfaceTexture_acquireANativeWindow(st);
+}
+
+int ASurfaceTexture_routeAttachToGLContext(ASurfaceTexture* st, uint32_t texName) {
+    return ASurfaceTexture_attachToGLContext(st, texName);
+}
+
+void ASurfaceTexture_routeRelease(ASurfaceTexture* st) {
+    return ASurfaceTexture_release(st);
+}
+
+int ASurfaceTexture_routeDetachFromGLContext(ASurfaceTexture* st) {
+    return ASurfaceTexture_detachFromGLContext(st);
+}
+
+int ASurfaceTexture_routeUpdateTexImage(ASurfaceTexture* st) {
+    return ASurfaceTexture_updateTexImage(st);
+}
+
+void ASurfaceTexture_routeGetTransformMatrix(ASurfaceTexture* st, float mtx[16]) {
+    return ASurfaceTexture_getTransformMatrix(st, mtx);
+}
+
+int64_t ASurfaceTexture_routeGetTimestamp(ASurfaceTexture* st) {
+    return ASurfaceTexture_getTimestamp(st);
+}
+
+ASurfaceTexture* ASurfaceTexture_routeFromSurfaceTexture(JNIEnv* env, jobject surfacetexture) {
+    return ASurfaceTexture_fromSurfaceTexture(env, surfacetexture);
+}
 
 unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st) {
     return st->consumer->getCurrentTextureTarget();
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 55400c7..ee006aa 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -25,6 +25,7 @@
     name: "libnativewindow_headers",
     export_include_dirs: ["include"],
     vendor_available: true,
+    min_sdk_version: "29",
 }
 
 ndk_library {
@@ -59,7 +60,6 @@
     ],
 
     shared_libs: [
-        "libhardware",
         "libcutils",
         "liblog",
         "libutils",
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 869ca9e..b78fc5d 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -254,6 +254,7 @@
     NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR           = 44,    /* private */
     NATIVE_WINDOW_ALLOCATE_BUFFERS                = 45,    /* private */
     NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER          = 46,    /* private */
+    NATIVE_WINDOW_SET_QUERY_INTERCEPTOR           = 47,    /* private */
     // clang-format on
 };
 
@@ -1062,4 +1063,38 @@
     return value;
 }
 
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_query is called.
+ */
+typedef int (*ANativeWindow_queryFn)(const ANativeWindow* window, int what, int* value);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_queryFn, 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_queryFn if it were to be called.
+ */
+typedef int (*ANativeWindow_queryInterceptor)(const ANativeWindow* window,
+                                                ANativeWindow_queryFn perform, void* data,
+                                                int what, int* value);
+
+/**
+ * Registers an interceptor for ANativeWindow_query. Instead of calling
+ * the underlying query function, instead the provided interceptor is
+ * called, which may optionally call the underlying query 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.
+ */
+static inline int ANativeWindow_setQueryInterceptor(ANativeWindow* window,
+                                            ANativeWindow_queryInterceptor interceptor,
+                                            void* data) {
+    return window->perform(window, NATIVE_WINDOW_SET_QUERY_INTERCEPTOR, interceptor, data);
+}
+
 __END_DECLS
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index eb6080f..3dcb498 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -64,6 +64,13 @@
     ],
 }
 
+filegroup {
+    name: "librenderengine_threaded_sources",
+    srcs: [
+        "threaded/RenderEngineThreaded.cpp",
+    ],
+}
+
 cc_library_static {
     name: "librenderengine",
     defaults: ["librenderengine_defaults"],
@@ -80,6 +87,7 @@
     srcs: [
         ":librenderengine_sources",
         ":librenderengine_gl_sources",
+        ":librenderengine_threaded_sources",
     ],
     lto: {
         thin: true,
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 0fdf093..f0eb34b 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -20,19 +20,33 @@
 #include <log/log.h>
 #include <private/gui/SyncFeatures.h>
 #include "gl/GLESRenderEngine.h"
+#include "threaded/RenderEngineThreaded.h"
 
 namespace android {
 namespace renderengine {
 
 std::unique_ptr<impl::RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
+    RenderEngineType renderEngineType = args.renderEngineType;
+
+    // Keep the ability to override by PROPERTIES:
     char prop[PROPERTY_VALUE_MAX];
-    property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles");
+    property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
     if (strcmp(prop, "gles") == 0) {
-        ALOGD("RenderEngine GLES Backend");
-        return renderengine::gl::GLESRenderEngine::create(args);
+        renderEngineType = RenderEngineType::GLES;
     }
-    ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop);
-    return renderengine::gl::GLESRenderEngine::create(args);
+    if (strcmp(prop, "threaded") == 0) {
+        renderEngineType = RenderEngineType::THREADED;
+    }
+
+    switch (renderEngineType) {
+        case RenderEngineType::THREADED:
+            ALOGD("Threaded RenderEngine with GLES Backend");
+            return renderengine::threaded::RenderEngineThreaded::create(args);
+        case RenderEngineType::GLES:
+        default:
+            ALOGD("RenderEngine with GLES Backend");
+            return renderengine::gl::GLESRenderEngine::create(args);
+    }
 }
 
 RenderEngine::~RenderEngine() = default;
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index f1f140b..d102696 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -208,9 +208,20 @@
         LOG_ALWAYS_FATAL("failed to initialize EGL");
     }
 
+    const auto eglVersion = eglQueryStringImplementationANDROID(display, EGL_VERSION);
+    if (!eglVersion) {
+        checkGlError(__FUNCTION__, __LINE__);
+        LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_VERSION) failed");
+    }
+
+    const auto eglExtensions = eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS);
+    if (!eglExtensions) {
+        checkGlError(__FUNCTION__, __LINE__);
+        LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_EXTENSIONS) failed");
+    }
+
     GLExtensions& extensions = GLExtensions::getInstance();
-    extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION),
-                                  eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS));
+    extensions.initWithEGLStrings(eglVersion, eglExtensions);
 
     // The code assumes that ES2 or later is available if this extension is
     // supported.
@@ -869,13 +880,32 @@
     return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE;
 }
 
-void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) {
+void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /*framebuffer*/) {
     ATRACE_CALL();
 
     // back to main framebuffer
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
 }
 
+bool GLESRenderEngine::cleanupPostRender() {
+    ATRACE_CALL();
+
+    if (mPriorResourcesCleaned ||
+        (mLastDrawFence != nullptr && mLastDrawFence->getStatus() != Fence::Status::Signaled)) {
+        // If we don't have a prior frame needing cleanup, then don't do anything.
+        return false;
+    }
+
+    // Bind the texture to dummy data so that backing image data can be freed.
+    GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing());
+    glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer);
+    // Release the cached fence here, so that we don't churn reallocations when
+    // we could no-op repeated calls of this method instead.
+    mLastDrawFence = nullptr;
+    mPriorResourcesCleaned = true;
+    return true;
+}
+
 void GLESRenderEngine::checkErrors() const {
     checkErrors(nullptr);
 }
@@ -1165,7 +1195,13 @@
             // us bad parameters, or we messed up our shader generation).
             return INVALID_OPERATION;
         }
+        mLastDrawFence = nullptr;
+    } else {
+        // The caller takes ownership of drawFence, so we need to duplicate the
+        // fd here.
+        mLastDrawFence = new Fence(dup(drawFence->get()));
     }
+    mPriorResourcesCleaned = false;
 
     checkErrors();
     return NO_ERROR;
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index d3c94a6..9ab5ee6 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -17,7 +17,6 @@
 #ifndef SF_GLESRENDERENGINE_H_
 #define SF_GLESRENDERENGINE_H_
 
-#include <stdint.h>
 #include <condition_variable>
 #include <deque>
 #include <mutex>
@@ -76,6 +75,7 @@
                         const std::vector<const LayerSettings*>& layers,
                         const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+    bool cleanupPostRender() override;
 
     EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
     // Creates an output image for rendering to
@@ -231,6 +231,17 @@
     std::mutex mRenderingMutex;
 
     std::unique_ptr<Framebuffer> mDrawingBuffer;
+    // this is a 1x1 RGB buffer, but over-allocate in case a driver wants more
+    // memory or if it needs to satisfy alignment requirements. In this case:
+    // assume that each channel requires 4 bytes, and add 3 additional bytes to
+    // ensure that we align on a word. Allocating 16 bytes will provide a
+    // guarantee that we don't clobber memory.
+    uint32_t mPlaceholderDrawBuffer[4];
+    sp<Fence> mLastDrawFence;
+    // Store a separate boolean checking if prior resources were cleaned up, as
+    // devices that don't support native sync fences can't rely on a last draw
+    // fence that doesn't exist.
+    bool mPriorResourcesCleaned = true;
 
     // Blur effect processor, only instantiated when a layer requests it.
     BlurFilter* mBlurFilter = nullptr;
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index cb0d5cf..383486b 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -68,11 +68,11 @@
     return true;
 }
 
-void GLFramebuffer::allocateBuffers(uint32_t width, uint32_t height) {
+void GLFramebuffer::allocateBuffers(uint32_t width, uint32_t height, void* data) {
     ATRACE_CALL();
 
     glBindTexture(GL_TEXTURE_2D, mTextureName);
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index b88da3b..6757695 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -39,7 +39,7 @@
 
     bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
                                const bool useFramebufferCache) override;
-    void allocateBuffers(uint32_t width, uint32_t height);
+    void allocateBuffers(uint32_t width, uint32_t height, void* data = nullptr);
     EGLImageKHR getEGLImage() const { return mEGLImage; }
     uint32_t getTextureName() const { return mTextureName; }
     uint32_t getFramebufferName() const { return mFramebufferName; }
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
index db55d17..19f18c0 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -68,6 +68,8 @@
 status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
     ATRACE_NAME("BlurFilter::setAsDrawTarget");
     mRadius = radius;
+    mDisplayX = display.physicalDisplay.left;
+    mDisplayY = display.physicalDisplay.top;
 
     if (mDisplayWidth < display.physicalDisplay.width() ||
         mDisplayHeight < display.physicalDisplay.height()) {
@@ -182,8 +184,8 @@
     if (mix >= 1 || multiPass) {
         mLastDrawTarget->bindAsReadBuffer();
         glBlitFramebuffer(0, 0, mLastDrawTarget->getBufferWidth(),
-                          mLastDrawTarget->getBufferHeight(), 0, 0, mDisplayWidth, mDisplayHeight,
-                          GL_COLOR_BUFFER_BIT, GL_LINEAR);
+                          mLastDrawTarget->getBufferHeight(), mDisplayX, mDisplayY, mDisplayWidth,
+                          mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
         return NO_ERROR;
     }
 
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
index 9d3fc60..593a8fd 100644
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -68,6 +68,8 @@
     GLFramebuffer mPongFbo;
     uint32_t mDisplayWidth = 0;
     uint32_t mDisplayHeight = 0;
+    uint32_t mDisplayX = 0;
+    uint32_t mDisplayY = 0;
     // Buffer holding the final blur pass.
     GLFramebuffer* mLastDrawTarget;
 
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 4983cb3..45e19ee 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -50,6 +50,10 @@
 class Texture;
 struct RenderEngineCreationArgs;
 
+namespace threaded {
+class RenderEngineThreaded;
+}
+
 namespace impl {
 class RenderEngine;
 }
@@ -67,6 +71,11 @@
         HIGH = 3,
     };
 
+    enum class RenderEngineType {
+        GLES = 1,
+        THREADED = 2,
+    };
+
     static std::unique_ptr<impl::RenderEngine> create(const RenderEngineCreationArgs& args);
 
     virtual ~RenderEngine() = 0;
@@ -111,6 +120,14 @@
     // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
     virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
     virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0;
+    // Clean-up method that should be called on the main thread after the
+    // drawFence returned by drawLayers fires. This method will free up
+    // resources used by the most recently drawn frame. If the frame is still
+    // being drawn, then this call is silently ignored.
+    //
+    // Returns true if resources were cleaned up, and false if we didn't need to
+    // do any work.
+    virtual bool cleanupPostRender() = 0;
 
     // queries
     virtual size_t getMaxTextureSize() const = 0;
@@ -166,6 +183,7 @@
     // live longer than RenderEngine.
     virtual Framebuffer* getFramebufferForDrawing() = 0;
     friend class BindNativeBufferAsFramebuffer;
+    friend class threaded::RenderEngineThreaded;
 };
 
 struct RenderEngineCreationArgs {
@@ -176,26 +194,25 @@
     bool precacheToneMapperShaderOnly;
     bool supportsBackgroundBlur;
     RenderEngine::ContextPriority contextPriority;
+    RenderEngine::RenderEngineType renderEngineType;
 
     struct Builder;
 
 private:
     // must be created by Builder via constructor with full argument list
-    RenderEngineCreationArgs(
-            int _pixelFormat,
-            uint32_t _imageCacheSize,
-            bool _useColorManagement,
-            bool _enableProtectedContext,
-            bool _precacheToneMapperShaderOnly,
-            bool _supportsBackgroundBlur,
-            RenderEngine::ContextPriority _contextPriority)
-        : pixelFormat(_pixelFormat)
-        , imageCacheSize(_imageCacheSize)
-        , useColorManagement(_useColorManagement)
-        , enableProtectedContext(_enableProtectedContext)
-        , precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly)
-        , supportsBackgroundBlur(_supportsBackgroundBlur)
-        , contextPriority(_contextPriority) {}
+    RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _useColorManagement,
+                             bool _enableProtectedContext, bool _precacheToneMapperShaderOnly,
+                             bool _supportsBackgroundBlur,
+                             RenderEngine::ContextPriority _contextPriority,
+                             RenderEngine::RenderEngineType _renderEngineType)
+          : pixelFormat(_pixelFormat),
+            imageCacheSize(_imageCacheSize),
+            useColorManagement(_useColorManagement),
+            enableProtectedContext(_enableProtectedContext),
+            precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly),
+            supportsBackgroundBlur(_supportsBackgroundBlur),
+            contextPriority(_contextPriority),
+            renderEngineType(_renderEngineType) {}
     RenderEngineCreationArgs() = delete;
 };
 
@@ -230,10 +247,14 @@
         this->contextPriority = contextPriority;
         return *this;
     }
+    Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) {
+        this->renderEngineType = renderEngineType;
+        return *this;
+    }
     RenderEngineCreationArgs build() const {
         return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement,
                                         enableProtectedContext, precacheToneMapperShaderOnly,
-                                        supportsBackgroundBlur, contextPriority);
+                                        supportsBackgroundBlur, contextPriority, renderEngineType);
     }
 
 private:
@@ -245,6 +266,7 @@
     bool precacheToneMapperShaderOnly = false;
     bool supportsBackgroundBlur = false;
     RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
+    RenderEngine::RenderEngineType renderEngineType = RenderEngine::RenderEngineType::GLES;
 };
 
 class BindNativeBufferAsFramebuffer {
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index 7f20c8c..d0343ba 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -22,6 +22,7 @@
 #include <renderengine/Mesh.h>
 #include <renderengine/RenderEngine.h>
 #include <renderengine/Texture.h>
+#include <ui/Fence.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/Region.h>
 
@@ -55,6 +56,7 @@
     MOCK_CONST_METHOD0(isProtected, bool());
     MOCK_CONST_METHOD0(supportsProtectedContent, bool());
     MOCK_METHOD1(useProtectedContext, bool(bool));
+    MOCK_METHOD0(cleanupPostRender, bool());
     MOCK_METHOD6(drawLayers,
                  status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
                           const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index e98babc..bcf389b 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -18,10 +18,12 @@
     test_suites: ["device-tests"],
     srcs: [
         "RenderEngineTest.cpp",
+        "RenderEngineThreadedTest.cpp",
     ],
     static_libs: [
         "libgmock",
         "librenderengine",
+        "librenderengine_mocks",
     ],
     shared_libs: [
         "libbase",
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 2bf456b..77b6c0f 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -22,12 +22,13 @@
 #include <condition_variable>
 #include <fstream>
 
-#include <gtest/gtest.h>
 #include <cutils/properties.h>
+#include <gtest/gtest.h>
 #include <renderengine/RenderEngine.h>
 #include <sync/sync.h>
 #include <ui/PixelFormat.h>
 #include "../gl/GLESRenderEngine.h"
+#include "../threaded/RenderEngineThreaded.h"
 
 constexpr int DEFAULT_DISPLAY_WIDTH = 128;
 constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
@@ -40,14 +41,15 @@
     static void SetUpTestSuite() {
         sRE = renderengine::gl::GLESRenderEngine::create(
                 renderengine::RenderEngineCreationArgs::Builder()
-                    .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
-                    .setImageCacheSize(1)
-                    .setUseColorManagerment(false)
-                    .setEnableProtectedContext(false)
-                    .setPrecacheToneMapperShaderOnly(false)
-                    .setSupportsBackgroundBlur(true)
-                    .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
-        .build());
+                        .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+                        .setImageCacheSize(1)
+                        .setUseColorManagerment(false)
+                        .setEnableProtectedContext(false)
+                        .setPrecacheToneMapperShaderOnly(false)
+                        .setSupportsBackgroundBlur(true)
+                        .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+                        .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES)
+                        .build());
     }
 
     static void TearDownTestSuite() {
@@ -1239,12 +1241,12 @@
     EXPECT_EQ(NO_ERROR, barrier->result);
 }
 
-TEST_F(RenderEngineTest, drawLayers_bindExternalBufferWithNullBuffer) {
+TEST_F(RenderEngineTest, bindExternalBuffer_withNullBuffer) {
     status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr);
     ASSERT_EQ(BAD_VALUE, result);
 }
 
-TEST_F(RenderEngineTest, drawLayers_bindExternalBufferCachesImages) {
+TEST_F(RenderEngineTest, bindExternalBuffer_cachesImages) {
     sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
     uint32_t texName;
     sRE->genTextures(1, &texName);
@@ -1264,7 +1266,7 @@
     EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
 }
 
-TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferWithNullBuffer) {
+TEST_F(RenderEngineTest, cacheExternalBuffer_withNullBuffer) {
     std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
             sRE->cacheExternalTextureBufferForTesting(nullptr);
     std::lock_guard<std::mutex> lock(barrier->mutex);
@@ -1276,7 +1278,7 @@
     EXPECT_EQ(BAD_VALUE, barrier->result);
 }
 
-TEST_F(RenderEngineTest, drawLayers_cacheExternalBufferCachesImages) {
+TEST_F(RenderEngineTest, cacheExternalBuffer_cachesImages) {
     sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
     uint64_t bufferId = buf->getId();
     std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier =
@@ -1399,6 +1401,33 @@
                       backgroundColor.a);
 }
 
+TEST_F(RenderEngineTest, cleanupPostRender_cleansUpOnce) {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<const renderengine::LayerSettings*> layers;
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = fullscreenRect().toFloatRect();
+    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+    layer.alpha = 1.0;
+    layers.push_back(&layer);
+
+    base::unique_fd fenceOne;
+    sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne);
+    base::unique_fd fenceTwo;
+    sRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo);
+
+    const int fd = fenceTwo.get();
+    if (fd >= 0) {
+        sync_wait(fd, -1);
+    }
+
+    // Only cleanup the first time.
+    EXPECT_TRUE(sRE->cleanupPostRender());
+    EXPECT_FALSE(sRE->cleanupPostRender());
+}
+
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
new file mode 100644
index 0000000..117af0f
--- /dev/null
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -0,0 +1,222 @@
+/*
+ * 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 <cutils/properties.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
+#include "../threaded/RenderEngineThreaded.h"
+
+namespace android {
+
+using testing::_;
+using testing::Eq;
+using testing::Mock;
+using testing::Return;
+
+struct RenderEngineThreadedTest : public ::testing::Test {
+    RenderEngineThreadedTest() {
+        sThreadedRE->setRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+    }
+
+    ~RenderEngineThreadedTest() {}
+
+    static void SetUpTestSuite() {
+        sThreadedRE = renderengine::threaded::RenderEngineThreaded::create(
+                renderengine::RenderEngineCreationArgs::Builder()
+                        .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::THREADED)
+                        .build());
+    }
+
+    static void TearDownTestSuite() { sThreadedRE = nullptr; }
+
+    // To avoid creating RE on every instantiation of the test, it is kept as a static variable.
+    static std::unique_ptr<renderengine::threaded::RenderEngineThreaded> sThreadedRE;
+    renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+};
+
+std::unique_ptr<renderengine::threaded::RenderEngineThreaded>
+        RenderEngineThreadedTest::sThreadedRE = nullptr;
+
+TEST_F(RenderEngineThreadedTest, dump) {
+    std::string testString = "XYZ";
+    EXPECT_CALL(*mRenderEngine, dump(_));
+    sThreadedRE->dump(testString);
+}
+
+TEST_F(RenderEngineThreadedTest, primeCache) {
+    EXPECT_CALL(*mRenderEngine, primeCache());
+    sThreadedRE->primeCache();
+}
+
+TEST_F(RenderEngineThreadedTest, genTextures) {
+    uint32_t texName;
+    EXPECT_CALL(*mRenderEngine, genTextures(1, &texName));
+    sThreadedRE->genTextures(1, &texName);
+}
+
+TEST_F(RenderEngineThreadedTest, deleteTextures) {
+    uint32_t texName;
+    EXPECT_CALL(*mRenderEngine, deleteTextures(1, &texName));
+    sThreadedRE->deleteTextures(1, &texName);
+}
+
+TEST_F(RenderEngineThreadedTest, bindExternalBuffer_nullptrBuffer) {
+    EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, Eq(nullptr), Eq(nullptr)))
+            .WillOnce(Return(BAD_VALUE));
+    status_t result = sThreadedRE->bindExternalTextureBuffer(0, nullptr, nullptr);
+    ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineThreadedTest, bindExternalBuffer_withBuffer) {
+    sp<GraphicBuffer> buf = new GraphicBuffer();
+    EXPECT_CALL(*mRenderEngine, bindExternalTextureBuffer(0, buf, Eq(nullptr)))
+            .WillOnce(Return(NO_ERROR));
+    status_t result = sThreadedRE->bindExternalTextureBuffer(0, buf, nullptr);
+    ASSERT_EQ(NO_ERROR, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_nullptr) {
+    EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(Eq(nullptr)));
+    sThreadedRE->cacheExternalTextureBuffer(nullptr);
+}
+
+TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_withBuffer) {
+    sp<GraphicBuffer> buf = new GraphicBuffer();
+    EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(buf));
+    sThreadedRE->cacheExternalTextureBuffer(buf);
+}
+
+TEST_F(RenderEngineThreadedTest, unbindExternalTextureBuffer) {
+    EXPECT_CALL(*mRenderEngine, unbindExternalTextureBuffer(0x0));
+    sThreadedRE->unbindExternalTextureBuffer(0x0);
+}
+
+TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsBadValue) {
+    std::unique_ptr<renderengine::Framebuffer> framebuffer;
+    EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(BAD_VALUE));
+    status_t result = sThreadedRE->bindFrameBuffer(framebuffer.get());
+    ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineThreadedTest, bindFrameBuffer_returnsNoError) {
+    std::unique_ptr<renderengine::Framebuffer> framebuffer;
+    EXPECT_CALL(*mRenderEngine, bindFrameBuffer(framebuffer.get())).WillOnce(Return(NO_ERROR));
+    status_t result = sThreadedRE->bindFrameBuffer(framebuffer.get());
+    ASSERT_EQ(NO_ERROR, result);
+}
+
+TEST_F(RenderEngineThreadedTest, unbindFrameBuffer) {
+    std::unique_ptr<renderengine::Framebuffer> framebuffer;
+    EXPECT_CALL(*mRenderEngine, unbindFrameBuffer(framebuffer.get()));
+    sThreadedRE->unbindFrameBuffer(framebuffer.get());
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) {
+    size_t size = 20;
+    EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
+    size_t result = sThreadedRE->getMaxTextureSize();
+    ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns0) {
+    size_t size = 0;
+    EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
+    size_t result = sThreadedRE->getMaxTextureSize();
+    ASSERT_EQ(size, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns20) {
+    size_t dims = 20;
+    EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+    size_t result = sThreadedRE->getMaxViewportDims();
+    ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns0) {
+    size_t dims = 0;
+    EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims));
+    size_t result = sThreadedRE->getMaxViewportDims();
+    ASSERT_EQ(dims, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsFalse) {
+    EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false));
+    status_t result = sThreadedRE->isProtected();
+    ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, isProtected_returnsTrue) {
+    EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(true));
+    size_t result = sThreadedRE->isProtected();
+    ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsFalse) {
+    EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(false));
+    status_t result = sThreadedRE->supportsProtectedContent();
+    ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsTrue) {
+    EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true));
+    status_t result = sThreadedRE->supportsProtectedContent();
+    ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsFalse) {
+    EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(false));
+    status_t result = sThreadedRE->useProtectedContext(false);
+    ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsTrue) {
+    EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(true));
+    status_t result = sThreadedRE->useProtectedContext(false);
+    ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) {
+    EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(false));
+    status_t result = sThreadedRE->cleanupPostRender();
+    ASSERT_EQ(false, result);
+}
+
+TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) {
+    EXPECT_CALL(*mRenderEngine, cleanupPostRender()).WillOnce(Return(true));
+    status_t result = sThreadedRE->cleanupPostRender();
+    ASSERT_EQ(true, result);
+}
+
+TEST_F(RenderEngineThreadedTest, drawLayers) {
+    renderengine::DisplaySettings settings;
+    std::vector<const renderengine::LayerSettings*> layers;
+    sp<GraphicBuffer> buffer = new GraphicBuffer();
+    base::unique_fd bufferFence;
+    base::unique_fd drawFence;
+
+    EXPECT_CALL(*mRenderEngine, drawLayers)
+            .WillOnce([](const renderengine::DisplaySettings&,
+                         const std::vector<const renderengine::LayerSettings*>&,
+                         const sp<GraphicBuffer>&, const bool, base::unique_fd&&,
+                         base::unique_fd*) -> status_t { return NO_ERROR; });
+
+    status_t result = sThreadedRE->drawLayers(settings, layers, buffer, false,
+                                              std::move(bufferFence), &drawFence);
+    ASSERT_EQ(NO_ERROR, result);
+}
+
+} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
new file mode 100644
index 0000000..a883ad3
--- /dev/null
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -0,0 +1,394 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "RenderEngineThreaded.h"
+
+#include <sched.h>
+#include <chrono>
+#include <future>
+
+#include <android-base/stringprintf.h>
+#include <utils/Trace.h>
+
+#include "gl/GLESRenderEngine.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(
+        const RenderEngineCreationArgs& args) {
+    return std::make_unique<RenderEngineThreaded>(args);
+}
+
+void RenderEngineThreaded::setRenderEngine(
+        std::unique_ptr<renderengine::RenderEngine> renderEngine) {
+    ATRACE_CALL();
+    // In order to ensure this is a thread safe call, it also needs to be put on a stack.
+    std::promise<void> resultPromise;
+    std::future<void> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push(
+                [this, &resultPromise, &renderEngine](renderengine::RenderEngine& /*instance*/) {
+                    ATRACE_NAME("REThreaded::setRenderEngine");
+                    mRenderEngine = std::move(renderEngine);
+                    resultPromise.set_value();
+                });
+    }
+    mCondition.notify_one();
+    resultFuture.wait();
+}
+
+RenderEngineThreaded::RenderEngineThreaded(const RenderEngineCreationArgs& args)
+      : renderengine::impl::RenderEngine(args) {
+    ATRACE_CALL();
+
+    std::lock_guard lockThread(mThreadMutex);
+    mThread = std::thread(&RenderEngineThreaded::threadMain, this, args);
+}
+
+RenderEngineThreaded::~RenderEngineThreaded() {
+    {
+        std::lock_guard lock(mThreadMutex);
+        mRunning = false;
+        mCondition.notify_one();
+    }
+
+    if (mThread.joinable()) {
+        mThread.join();
+    }
+}
+
+// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations.
+void RenderEngineThreaded::threadMain(const RenderEngineCreationArgs& args)
+        NO_THREAD_SAFETY_ANALYSIS {
+    ATRACE_CALL();
+
+    struct sched_param param = {0};
+    param.sched_priority = 2;
+    if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
+        ALOGE("Couldn't set SCHED_FIFO");
+    }
+
+    mRenderEngine = renderengine::gl::GLESRenderEngine::create(args);
+
+    std::unique_lock<std::mutex> lock(mThreadMutex);
+    pthread_setname_np(pthread_self(), mThreadName);
+
+    while (mRunning) {
+        if (!mFunctionCalls.empty()) {
+            auto task = mFunctionCalls.front();
+            mFunctionCalls.pop();
+            task(*mRenderEngine);
+        }
+        mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) {
+            return !mRunning || !mFunctionCalls.empty();
+        });
+    }
+}
+
+void RenderEngineThreaded::primeCache() const {
+    std::promise<void> resultPromise;
+    std::future<void> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::primeCache");
+            instance.primeCache();
+            resultPromise.set_value();
+        });
+    }
+    mCondition.notify_one();
+    resultFuture.wait();
+}
+
+void RenderEngineThreaded::dump(std::string& result) {
+    std::promise<std::string> resultPromise;
+    std::future<std::string> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::dump");
+            std::string localResult = result;
+            instance.dump(localResult);
+            resultPromise.set_value(std::move(localResult));
+        });
+    }
+    mCondition.notify_one();
+    // Note: This is an rvalue.
+    result.assign(resultFuture.get());
+}
+
+void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
+    std::promise<void> resultPromise;
+    std::future<void> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::genTextures");
+            instance.genTextures(count, names);
+            resultPromise.set_value();
+        });
+    }
+    mCondition.notify_one();
+    resultFuture.wait();
+}
+
+void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
+    std::promise<void> resultPromise;
+    std::future<void> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::deleteTextures");
+            instance.deleteTextures(count, names);
+            resultPromise.set_value();
+        });
+    }
+    mCondition.notify_one();
+    resultFuture.wait();
+}
+
+void RenderEngineThreaded::bindExternalTextureImage(uint32_t texName, const Image& image) {
+    std::promise<void> resultPromise;
+    std::future<void> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push(
+                [&resultPromise, texName, &image](renderengine::RenderEngine& instance) {
+                    ATRACE_NAME("REThreaded::bindExternalTextureImage");
+                    instance.bindExternalTextureImage(texName, image);
+                    resultPromise.set_value();
+                });
+    }
+    mCondition.notify_one();
+    resultFuture.wait();
+}
+
+status_t RenderEngineThreaded::bindExternalTextureBuffer(uint32_t texName,
+                                                         const sp<GraphicBuffer>& buffer,
+                                                         const sp<Fence>& fence) {
+    std::promise<status_t> resultPromise;
+    std::future<status_t> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push(
+                [&resultPromise, texName, &buffer, &fence](renderengine::RenderEngine& instance) {
+                    ATRACE_NAME("REThreaded::bindExternalTextureBuffer");
+                    status_t status = instance.bindExternalTextureBuffer(texName, buffer, fence);
+                    resultPromise.set_value(status);
+                });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+void RenderEngineThreaded::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+    std::promise<void> resultPromise;
+    std::future<void> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, &buffer](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::cacheExternalTextureBuffer");
+            instance.cacheExternalTextureBuffer(buffer);
+            resultPromise.set_value();
+        });
+    }
+    mCondition.notify_one();
+    resultFuture.wait();
+}
+
+void RenderEngineThreaded::unbindExternalTextureBuffer(uint64_t bufferId) {
+    std::promise<void> resultPromise;
+    std::future<void> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, &bufferId](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::unbindExternalTextureBuffer");
+            instance.unbindExternalTextureBuffer(bufferId);
+            resultPromise.set_value();
+        });
+    }
+    mCondition.notify_one();
+    resultFuture.wait();
+}
+
+status_t RenderEngineThreaded::bindFrameBuffer(Framebuffer* framebuffer) {
+    std::promise<status_t> resultPromise;
+    std::future<status_t> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::bindFrameBuffer");
+            status_t status = instance.bindFrameBuffer(framebuffer);
+            resultPromise.set_value(status);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+void RenderEngineThreaded::unbindFrameBuffer(Framebuffer* framebuffer) {
+    std::promise<void> resultPromise;
+    std::future<void> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, &framebuffer](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::unbindFrameBuffer");
+            instance.unbindFrameBuffer(framebuffer);
+            resultPromise.set_value();
+        });
+    }
+    mCondition.notify_one();
+    resultFuture.wait();
+}
+
+size_t RenderEngineThreaded::getMaxTextureSize() const {
+    std::promise<size_t> resultPromise;
+    std::future<size_t> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::getMaxTextureSize");
+            size_t size = instance.getMaxTextureSize();
+            resultPromise.set_value(size);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+size_t RenderEngineThreaded::getMaxViewportDims() const {
+    std::promise<size_t> resultPromise;
+    std::future<size_t> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::getMaxViewportDims");
+            size_t size = instance.getMaxViewportDims();
+            resultPromise.set_value(size);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+bool RenderEngineThreaded::isProtected() const {
+    std::promise<bool> resultPromise;
+    std::future<bool> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::isProtected");
+            bool returnValue = instance.isProtected();
+            resultPromise.set_value(returnValue);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+bool RenderEngineThreaded::supportsProtectedContent() const {
+    std::promise<bool> resultPromise;
+    std::future<bool> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::supportsProtectedContent");
+            bool returnValue = instance.supportsProtectedContent();
+            resultPromise.set_value(returnValue);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+bool RenderEngineThreaded::useProtectedContext(bool useProtectedContext) {
+    std::promise<bool> resultPromise;
+    std::future<bool> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push(
+                [&resultPromise, useProtectedContext](renderengine::RenderEngine& instance) {
+                    ATRACE_NAME("REThreaded::useProtectedContext");
+                    bool returnValue = instance.useProtectedContext(useProtectedContext);
+                    resultPromise.set_value(returnValue);
+                });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+Framebuffer* RenderEngineThreaded::getFramebufferForDrawing() {
+    std::promise<Framebuffer*> resultPromise;
+    std::future<Framebuffer*> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::getFramebufferForDrawing");
+            Framebuffer* framebuffer = instance.getFramebufferForDrawing();
+            resultPromise.set_value(framebuffer);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+bool RenderEngineThreaded::cleanupPostRender() {
+    std::promise<bool> resultPromise;
+    std::future<bool> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::cleanupPostRender");
+            bool returnValue = instance.cleanupPostRender();
+            resultPromise.set_value(returnValue);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display,
+                                          const std::vector<const LayerSettings*>& layers,
+                                          const sp<GraphicBuffer>& buffer,
+                                          const bool useFramebufferCache,
+                                          base::unique_fd&& bufferFence,
+                                          base::unique_fd* drawFence) {
+    std::promise<status_t> resultPromise;
+    std::future<status_t> resultFuture = resultPromise.get_future();
+    {
+        std::lock_guard lock(mThreadMutex);
+        mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache,
+                             &bufferFence, &drawFence](renderengine::RenderEngine& instance) {
+            ATRACE_NAME("REThreaded::drawLayers");
+            status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache,
+                                                  std::move(bufferFence), drawFence);
+            resultPromise.set_value(status);
+        });
+    }
+    mCondition.notify_one();
+    return resultFuture.get();
+}
+
+} // namespace threaded
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
new file mode 100644
index 0000000..8e4431e
--- /dev/null
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -0,0 +1,94 @@
+/*
+ * 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 <android-base/thread_annotations.h>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+#include "renderengine/RenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace threaded {
+
+/**
+ * This class extends a basic RenderEngine class. It contains a thread. Each time a function of
+ * this class is called, we create a lambda function that is put on a queue. The main thread then
+ * executes the functions in order.
+ */
+class RenderEngineThreaded : public impl::RenderEngine {
+public:
+    static std::unique_ptr<RenderEngineThreaded> create(const RenderEngineCreationArgs& args);
+
+    RenderEngineThreaded(const RenderEngineCreationArgs& args);
+    ~RenderEngineThreaded() override;
+    void primeCache() const override;
+
+    void dump(std::string& result) override;
+
+    void genTextures(size_t count, uint32_t* names) override;
+    void deleteTextures(size_t count, uint32_t const* names) override;
+    void bindExternalTextureImage(uint32_t texName, const Image& image) override;
+    status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer,
+                                       const sp<Fence>& fence) override;
+    void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
+    void unbindExternalTextureBuffer(uint64_t bufferId) override;
+    status_t bindFrameBuffer(Framebuffer* framebuffer) override;
+    void unbindFrameBuffer(Framebuffer* framebuffer) override;
+    size_t getMaxTextureSize() const override;
+    size_t getMaxViewportDims() const override;
+
+    bool isProtected() const override;
+    bool supportsProtectedContent() const override;
+    bool useProtectedContext(bool useProtectedContext) override;
+    bool cleanupPostRender() override;
+
+    status_t drawLayers(const DisplaySettings& display,
+                        const std::vector<const LayerSettings*>& layers,
+                        const sp<GraphicBuffer>& buffer, const bool useFramebufferCache,
+                        base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
+    void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>);
+
+protected:
+    Framebuffer* getFramebufferForDrawing() override;
+
+private:
+    void threadMain(const RenderEngineCreationArgs& args);
+
+    /* ------------------------------------------------------------------------
+     * Threading
+     */
+    const char* const mThreadName = "RenderEngineThread";
+    // Protects the creation and destruction of mThread.
+    mutable std::mutex mThreadMutex;
+    std::thread mThread GUARDED_BY(mThreadMutex);
+    bool mRunning GUARDED_BY(mThreadMutex) = true;
+    mutable std::queue<std::function<void(renderengine::RenderEngine& instance)>> mFunctionCalls
+            GUARDED_BY(mThreadMutex);
+    mutable std::condition_variable mCondition;
+
+    /* ------------------------------------------------------------------------
+     * Render Engine
+     */
+    std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+};
+} // namespace threaded
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index b68604d..bb78895 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -199,6 +199,7 @@
         "libhardware_headers",
         "libui_headers",
     ],
+    min_sdk_version: "29",
 }
 
 cc_library_headers {
@@ -217,6 +218,7 @@
     export_header_lib_headers: [
         "libnativewindow_headers",
     ],
+    min_sdk_version: "29",
 }
 
 // defaults to enable VALIDATE_REGIONS traces
diff --git a/libs/ui/DeviceProductInfo.cpp b/libs/ui/DeviceProductInfo.cpp
index 7bced9b..4d6ce43 100644
--- a/libs/ui/DeviceProductInfo.cpp
+++ b/libs/ui/DeviceProductInfo.cpp
@@ -16,13 +16,17 @@
 
 #include <ui/DeviceProductInfo.h>
 
+#include <android-base/stringprintf.h>
 #include <ui/FlattenableHelpers.h>
+#include <utils/Log.h>
 
 #define RETURN_IF_ERROR(op) \
     if (const status_t status = (op); status != OK) return status;
 
 namespace android {
 
+using base::StringAppendF;
+
 size_t DeviceProductInfo::getFlattenedSize() const {
     return FlattenableHelpers::getFlattenedSize(name) +
             FlattenableHelpers::getFlattenedSize(manufacturerPnpId) +
@@ -52,4 +56,32 @@
     return OK;
 }
 
+void DeviceProductInfo::dump(std::string& result) const {
+    StringAppendF(&result, "{name=%s, ", name.c_str());
+    StringAppendF(&result, "manufacturerPnpId=%s, ", manufacturerPnpId.data());
+    StringAppendF(&result, "productId=%s, ", productId.c_str());
+
+    if (const auto* model = std::get_if<ModelYear>(&manufactureOrModelDate)) {
+        StringAppendF(&result, "modelYear=%u, ", model->year);
+    } else if (const auto* manufactureWeekAndYear =
+                       std::get_if<ManufactureWeekAndYear>(&manufactureOrModelDate)) {
+        StringAppendF(&result, "manufactureWeek=%u, ", manufactureWeekAndYear->week);
+        StringAppendF(&result, "manufactureYear=%d, ", manufactureWeekAndYear->year);
+    } else if (const auto* manufactureYear =
+                       std::get_if<ManufactureYear>(&manufactureOrModelDate)) {
+        StringAppendF(&result, "manufactureYear=%d, ", manufactureYear->year);
+    } else {
+        ALOGE("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate");
+    }
+
+    result.append("relativeAddress=[");
+    for (size_t i = 0; i < relativeAddress.size(); i++) {
+        if (i != 0) {
+            result.append(", ");
+        }
+        StringAppendF(&result, "%u", relativeAddress[i]);
+    }
+    result.append("]}");
+}
+
 } // namespace android
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index bf487c4..e01309b 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -67,19 +67,20 @@
 // ----------------------------------------------------------------------------
 
 Region::Region() {
-    mStorage.add(Rect(0,0));
+    mStorage.push_back(Rect(0, 0));
 }
 
 Region::Region(const Region& rhs)
-    : mStorage(rhs.mStorage)
 {
+    mStorage.clear();
+    mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end());
 #if defined(VALIDATE_REGIONS)
     validate(rhs, "rhs copy-ctor");
 #endif
 }
 
 Region::Region(const Rect& rhs) {
-    mStorage.add(rhs);
+    mStorage.push_back(rhs);
 }
 
 Region::~Region()
@@ -100,8 +101,8 @@
  * final, correctly ordered region buffer. Each rectangle will be compared with the span directly
  * above it, and subdivided to resolve any remaining T-junctions.
  */
-static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end,
-        Vector<Rect>& dst, int spanDirection) {
+static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, FatVector<Rect>& dst,
+                                           int spanDirection) {
     dst.clear();
 
     const Rect* current = end - 1;
@@ -109,7 +110,7 @@
 
     // add first span immediately
     do {
-        dst.add(*current);
+        dst.push_back(*current);
         current--;
     } while (current->top == lastTop && current >= begin);
 
@@ -147,12 +148,12 @@
                 if (prev.right <= left) break;
 
                 if (prev.right > left && prev.right < right) {
-                    dst.add(Rect(prev.right, top, right, bottom));
+                    dst.push_back(Rect(prev.right, top, right, bottom));
                     right = prev.right;
                 }
 
                 if (prev.left > left && prev.left < right) {
-                    dst.add(Rect(prev.left, top, right, bottom));
+                    dst.push_back(Rect(prev.left, top, right, bottom));
                     right = prev.left;
                 }
 
@@ -166,12 +167,12 @@
                 if (prev.left >= right) break;
 
                 if (prev.left > left && prev.left < right) {
-                    dst.add(Rect(left, top, prev.left, bottom));
+                    dst.push_back(Rect(left, top, prev.left, bottom));
                     left = prev.left;
                 }
 
                 if (prev.right > left && prev.right < right) {
-                    dst.add(Rect(left, top, prev.right, bottom));
+                    dst.push_back(Rect(left, top, prev.right, bottom));
                     left = prev.right;
                 }
                 // if an entry in the previous span is too far left, nothing further right in the
@@ -183,7 +184,7 @@
         }
 
         if (left < right) {
-            dst.add(Rect(left, top, right, bottom));
+            dst.push_back(Rect(left, top, right, bottom));
         }
 
         current--;
@@ -201,13 +202,14 @@
     if (r.isEmpty()) return r;
     if (r.isRect()) return r;
 
-    Vector<Rect> reversed;
+    FatVector<Rect> reversed;
     reverseRectsResolvingJunctions(r.begin(), r.end(), reversed, direction_RTL);
 
     Region outputRegion;
-    reverseRectsResolvingJunctions(reversed.begin(), reversed.end(),
-            outputRegion.mStorage, direction_LTR);
-    outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds
+    reverseRectsResolvingJunctions(reversed.data(), reversed.data() + reversed.size(),
+                                   outputRegion.mStorage, direction_LTR);
+    outputRegion.mStorage.push_back(
+            r.getBounds()); // to make region valid, mStorage must end with bounds
 
 #if defined(VALIDATE_REGIONS)
     validate(outputRegion, "T-Junction free region");
@@ -222,7 +224,13 @@
     validate(*this, "this->operator=");
     validate(rhs, "rhs.operator=");
 #endif
-    mStorage = rhs.mStorage;
+    if (this == &rhs) {
+        // Already equal to itself
+        return *this;
+    }
+
+    mStorage.clear();
+    mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end());
     return *this;
 }
 
@@ -231,7 +239,7 @@
     if (mStorage.size() >= 2) {
         const Rect bounds(getBounds());
         mStorage.clear();
-        mStorage.add(bounds);
+        mStorage.push_back(bounds);
     }
     return *this;
 }
@@ -255,25 +263,25 @@
 void Region::clear()
 {
     mStorage.clear();
-    mStorage.add(Rect(0,0));
+    mStorage.push_back(Rect(0, 0));
 }
 
 void Region::set(const Rect& r)
 {
     mStorage.clear();
-    mStorage.add(r);
+    mStorage.push_back(r);
 }
 
 void Region::set(int32_t w, int32_t h)
 {
     mStorage.clear();
-    mStorage.add(Rect(w, h));
+    mStorage.push_back(Rect(w, h));
 }
 
 void Region::set(uint32_t w, uint32_t h)
 {
     mStorage.clear();
-    mStorage.add(Rect(w, h));
+    mStorage.push_back(Rect(w, h));
 }
 
 bool Region::isTriviallyEqual(const Region& region) const {
@@ -299,13 +307,16 @@
 void Region::addRectUnchecked(int l, int t, int r, int b)
 {
     Rect rect(l,t,r,b);
-    size_t where = mStorage.size() - 1;
-    mStorage.insertAt(rect, where, 1);
+    mStorage.insert(mStorage.end() - 1, rect);
 }
 
 // ----------------------------------------------------------------------------
 
 Region& Region::orSelf(const Rect& r) {
+    if (isEmpty()) {
+        set(r);
+        return *this;
+    }
     return operationSelf(r, op_or);
 }
 Region& Region::xorSelf(const Rect& r) {
@@ -326,6 +337,10 @@
 // ----------------------------------------------------------------------------
 
 Region& Region::orSelf(const Region& rhs) {
+    if (isEmpty()) {
+        *this = rhs;
+        return *this;
+    }
     return operationSelf(rhs, op_or);
 }
 Region& Region::xorSelf(const Region& rhs) {
@@ -350,7 +365,7 @@
 
 Region& Region::scaleSelf(float sx, float sy) {
     size_t count = mStorage.size();
-    Rect* rects = mStorage.editArray();
+    Rect* rects = mStorage.data();
     while (count) {
         rects->left = static_cast<int32_t>(static_cast<float>(rects->left) * sx + 0.5f);
         rects->right = static_cast<int32_t>(static_cast<float>(rects->right) * sx + 0.5f);
@@ -455,10 +470,10 @@
 class Region::rasterizer : public region_operator<Rect>::region_rasterizer
 {
     Rect bounds;
-    Vector<Rect>& storage;
+    FatVector<Rect>& storage;
     Rect* head;
     Rect* tail;
-    Vector<Rect> span;
+    FatVector<Rect> span;
     Rect* cur;
 public:
     explicit rasterizer(Region& reg)
@@ -485,8 +500,8 @@
         flushSpan();
     }
     if (storage.size()) {
-        bounds.top = storage.itemAt(0).top;
-        bounds.bottom = storage.top().bottom;
+        bounds.top = storage.front().top;
+        bounds.bottom = storage.back().bottom;
         if (storage.size() == 1) {
             storage.clear();
         }
@@ -494,7 +509,7 @@
         bounds.left  = 0;
         bounds.right = 0;
     }
-    storage.add(bounds);
+    storage.push_back(bounds);
 }
 
 void Region::rasterizer::operator()(const Rect& rect)
@@ -509,15 +524,15 @@
             return;
         }
     }
-    span.add(rect);
-    cur = span.editArray() + (span.size() - 1);
+    span.push_back(rect);
+    cur = span.data() + (span.size() - 1);
 }
 
 void Region::rasterizer::flushSpan()
 {
     bool merge = false;
     if (tail-head == ssize_t(span.size())) {
-        Rect const* p = span.editArray();
+        Rect const* p = span.data();
         Rect const* q = head;
         if (p->top == q->bottom) {
             merge = true;
@@ -532,17 +547,17 @@
         }
     }
     if (merge) {
-        const int bottom = span[0].bottom;
+        const int bottom = span.front().bottom;
         Rect* r = head;
         while (r != tail) {
             r->bottom = bottom;
             r++;
         }
     } else {
-        bounds.left = min(span.itemAt(0).left, bounds.left);
-        bounds.right = max(span.top().right, bounds.right);
-        storage.appendVector(span);
-        tail = storage.editArray() + storage.size();
+        bounds.left = min(span.front().left, bounds.left);
+        bounds.right = max(span.back().right, bounds.right);
+        storage.insert(storage.end(), span.begin(), span.end());
+        tail = storage.data() + storage.size();
         head = tail - span.size();
     }
     span.clear();
@@ -550,7 +565,7 @@
 
 bool Region::validate(const Region& reg, const char* name, bool silent)
 {
-    if (reg.mStorage.isEmpty()) {
+    if (reg.mStorage.empty()) {
         ALOGE_IF(!silent, "%s: mStorage is empty, which is never valid", name);
         // return immediately as the code below assumes mStorage is non-empty
         return false;
@@ -689,9 +704,8 @@
     }
     sk_dst.op(sk_lhs, sk_rhs, sk_op);
 
-    if (sk_dst.isEmpty() && dst.isEmpty())
-        return;
-    
+    if (sk_dst.empty() && dst.empty()) return;
+
     bool same = true;
     Region::const_iterator head = dst.begin();
     Region::const_iterator const tail = dst.end();
@@ -786,7 +800,7 @@
         validate(reg, "translate (before)");
 #endif
         size_t count = reg.mStorage.size();
-        Rect* rects = reg.mStorage.editArray();
+        Rect* rects = reg.mStorage.data();
         while (count) {
             rects->offsetBy(dx, dy);
             rects++;
@@ -866,24 +880,25 @@
         ALOGE("Region::unflatten() failed, invalid region");
         return BAD_VALUE;
     }
-    mStorage = result.mStorage;
+    mStorage.clear();
+    mStorage.insert(mStorage.begin(), result.mStorage.begin(), result.mStorage.end());
     return NO_ERROR;
 }
 
 // ----------------------------------------------------------------------------
 
 Region::const_iterator Region::begin() const {
-    return mStorage.array();
+    return mStorage.data();
 }
 
 Region::const_iterator Region::end() const {
     // Workaround for b/77643177
     // mStorage should never be empty, but somehow it is and it's causing
     // an abort in ubsan
-    if (mStorage.isEmpty()) return mStorage.array();
+    if (mStorage.empty()) return mStorage.data();
 
     size_t numRects = isRect() ? 1 : mStorage.size() - 1;
-    return mStorage.array() + numRects;
+    return mStorage.data() + numRects;
 }
 
 Rect const* Region::getArray(size_t* count) const {
diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h
index 0bc1e5c..807a5d9 100644
--- a/libs/ui/include/ui/DeviceProductInfo.h
+++ b/libs/ui/include/ui/DeviceProductInfo.h
@@ -68,6 +68,8 @@
     size_t getFlattenedSize() const;
     status_t flatten(void* buffer, size_t size) const;
     status_t unflatten(void const* buffer, size_t size);
+
+    void dump(std::string& result) const;
 };
 
 } // namespace android
diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h
new file mode 100644
index 0000000..25fe3a0
--- /dev/null
+++ b/libs/ui/include/ui/FatVector.h
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_REGION_FAT_VECTOR_H
+#define ANDROID_REGION_FAT_VECTOR_H
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <utils/Log.h>
+#include <type_traits>
+
+#include <vector>
+
+namespace android {
+
+template <typename T, size_t SIZE = 4>
+class InlineStdAllocator {
+public:
+    struct Allocation {
+    private:
+        Allocation(const Allocation&) = delete;
+        void operator=(const Allocation&) = delete;
+
+    public:
+        Allocation() {}
+        // char array instead of T array, so memory is uninitialized, with no destructors run
+        char array[sizeof(T) * SIZE];
+        bool inUse = false;
+    };
+
+    typedef T value_type; // needed to implement std::allocator
+    typedef T* pointer;   // needed to implement std::allocator
+
+    explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {}
+    InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {}
+    ~InlineStdAllocator() {}
+
+    T* allocate(size_t num, const void* = 0) {
+        if (!mAllocation.inUse && num <= SIZE) {
+            mAllocation.inUse = true;
+            return static_cast<T*>(static_cast<void*>(mAllocation.array));
+        } else {
+            return static_cast<T*>(static_cast<void*>(malloc(num * sizeof(T))));
+        }
+    }
+
+    void deallocate(pointer p, size_t) {
+        if (p == static_cast<T*>(static_cast<void*>(mAllocation.array))) {
+            mAllocation.inUse = false;
+        } else {
+            // 'free' instead of delete here - destruction handled separately
+            free(p);
+        }
+    }
+    Allocation& mAllocation;
+};
+
+/**
+ * std::vector with SIZE elements preallocated into an internal buffer.
+ *
+ * Useful for avoiding the cost of malloc in cases where only SIZE or
+ * fewer elements are needed in the common case.
+ */
+template <typename T, size_t SIZE = 4>
+class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> {
+public:
+    FatVector()
+          : std::vector<T, InlineStdAllocator<T, SIZE>>(InlineStdAllocator<T, SIZE>(mAllocation)) {
+        this->reserve(SIZE);
+    }
+
+    explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); }
+
+private:
+    typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
+};
+
+} // namespace android
+
+#endif // ANDROID_REGION_FAT_VECTOR_H
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 2db3b10..6bb7b8d 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -21,13 +21,13 @@
 #include <sys/types.h>
 #include <ostream>
 
-#include <utils/Vector.h>
-
 #include <ui/Rect.h>
 #include <utils/Flattenable.h>
 
 #include <android-base/macros.h>
 
+#include "FatVector.h"
+
 #include <string>
 
 namespace android {
@@ -180,7 +180,7 @@
     // with an extra Rect as the last element which is set to the
     // bounds of the region. However, if the region is
     // a simple Rect then mStorage contains only that rect.
-    Vector<Rect> mStorage;
+    FatVector<Rect> mStorage;
 };
 
 
@@ -235,4 +235,3 @@
 }; // namespace android
 
 #endif // ANDROID_UI_REGION_H
-
diff --git a/libs/ui/include_private/ui/FlattenableHelpers.h b/libs/ui/include_private/ui/FlattenableHelpers.h
index 946a67f..8e316d8 100644
--- a/libs/ui/include_private/ui/FlattenableHelpers.h
+++ b/libs/ui/include_private/ui/FlattenableHelpers.h
@@ -50,18 +50,20 @@
     }
 
     // Helpers for reading and writing std::string
-    static size_t getFlattenedSize(const std::string& str) { return sizeof(size_t) + str.length(); }
+    static size_t getFlattenedSize(const std::string& str) {
+        return sizeof(uint64_t) + str.length();
+    }
 
     static status_t flatten(void** buffer, size_t* size, const std::string& str) {
         if (*size < getFlattenedSize(str)) return NO_MEMORY;
-        flatten(buffer, size, str.length());
+        flatten(buffer, size, (uint64_t)str.length());
         memcpy(reinterpret_cast<char*>(*buffer), str.c_str(), str.length());
         FlattenableUtils::advance(*buffer, *size, str.length());
         return OK;
     }
 
     static status_t unflatten(const void** buffer, size_t* size, std::string* str) {
-        size_t length;
+        uint64_t length;
         RETURN_IF_ERROR(unflatten(buffer, size, &length));
         if (*size < length) return NO_MEMORY;
         str->assign(reinterpret_cast<const char*>(*buffer), length);
@@ -122,7 +124,7 @@
     // Helpers for reading and writing std::vector
     template <class T>
     static size_t getFlattenedSize(const std::vector<T>& value) {
-        return std::accumulate(value.begin(), value.end(), sizeof(size_t),
+        return std::accumulate(value.begin(), value.end(), sizeof(uint64_t),
                                [](size_t sum, const T& element) {
                                    return sum + getFlattenedSize(element);
                                });
@@ -130,7 +132,7 @@
 
     template <class T>
     static status_t flatten(void** buffer, size_t* size, const std::vector<T>& value) {
-        RETURN_IF_ERROR(flatten(buffer, size, value.size()));
+        RETURN_IF_ERROR(flatten(buffer, size, (uint64_t)value.size()));
         for (const auto& element : value) {
             RETURN_IF_ERROR(flatten(buffer, size, element));
         }
@@ -139,7 +141,7 @@
 
     template <class T>
     static status_t unflatten(const void** buffer, size_t* size, std::vector<T>* value) {
-        size_t numElements;
+        uint64_t numElements;
         RETURN_IF_ERROR(unflatten(buffer, size, &numElements));
         // We don't need an extra size check since each iteration of the loop does that
         std::vector<T> elements;
diff --git a/libs/ui/include_vndk/ui/FatVector.h b/libs/ui/include_vndk/ui/FatVector.h
new file mode 120000
index 0000000..bf30166
--- /dev/null
+++ b/libs/ui/include_vndk/ui/FatVector.h
@@ -0,0 +1 @@
+../../include/ui/FatVector.h
\ No newline at end of file
diff --git a/libs/ui/tests/Region_test.cpp b/libs/ui/tests/Region_test.cpp
index b104a46..c6b826d 100644
--- a/libs/ui/tests/Region_test.cpp
+++ b/libs/ui/tests/Region_test.cpp
@@ -152,5 +152,20 @@
     }
 }
 
+TEST_F(RegionTest, EqualsToSelf) {
+    Region touchableRegion;
+    touchableRegion.orSelf(Rect(0, 0, 100, 100));
+
+    ASSERT_TRUE(touchableRegion.contains(50, 50));
+
+    // Compiler prevents us from directly calling 'touchableRegion = touchableRegion'
+    Region& referenceTouchableRegion = touchableRegion;
+    touchableRegion = referenceTouchableRegion;
+
+    ASSERT_FALSE(touchableRegion.isEmpty());
+
+    ASSERT_TRUE(touchableRegion.contains(50, 50));
+}
+
 }; // namespace android
 
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
index 23a4224..24ba830 100644
--- a/libs/vr/libpdx/Android.bp
+++ b/libs/vr/libpdx/Android.bp
@@ -2,6 +2,7 @@
     name: "libpdx_headers",
     export_include_dirs: ["private"],
     vendor_available: true,
+    min_sdk_version: "29",
 }
 
 cc_library_static {
diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp
index 410e234..7fafd3b 100644
--- a/libs/vr/libvrflinger/tests/Android.bp
+++ b/libs/vr/libvrflinger/tests/Android.bp
@@ -11,6 +11,7 @@
     "libutils",
     "libnativewindow",
     "libpdx_default_transport",
+    "libSurfaceFlingerProp",
 ]
 
 static_libs = [
@@ -32,5 +33,6 @@
         "-Wall",
         "-Werror",
     ],
+    header_libs: ["libsurfaceflinger_headers"],
     name: "vrflinger_test",
 }
diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
index 7075e88..efd6d1d 100644
--- a/libs/vr/libvrflinger/tests/vrflinger_test.cpp
+++ b/libs/vr/libvrflinger/tests/vrflinger_test.cpp
@@ -1,3 +1,4 @@
+#include <SurfaceFlingerProperties.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/types.h>
 #include <android/hardware_buffer.h>
@@ -147,9 +148,7 @@
   // Exit immediately if the device doesn't support vr flinger. This ConfigStore
   // check is the same mechanism used by surface flinger to decide if it should
   // initialize vr flinger.
-  bool vr_flinger_enabled =
-      getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::useVrFlinger>(
-          false);
+  bool vr_flinger_enabled = android::sysprop::use_vr_flinger(false);
   if (!vr_flinger_enabled) {
     return;
   }
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index e255b9d..3997134 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -75,7 +75,6 @@
         "bionic_libc_platform_headers",
         "gl_headers",
         "libsystem_headers",
-        "libhardware_headers",
         "libnativebase_headers",
     ],
     export_header_lib_headers: ["gl_headers"],
@@ -154,6 +153,7 @@
         "libnativebridge_lazy",
         "libnativeloader_lazy",
         "libutils",
+        "libSurfaceFlingerProp",
     ],
     static_libs: [
         "libEGL_getProcAddress",
@@ -165,6 +165,7 @@
         symbol_file: "libEGL.map.txt",
         versions: ["29"],
     },
+    header_libs: ["libsurfaceflinger_headers"],
 }
 
 cc_test {
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 39bf329..391a410 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -271,6 +271,12 @@
     if (!hnd) {
         android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL,
                                                             false, systemTime() - openTime);
+    } else {
+        // init_angle_backend will check if loaded driver is ANGLE or not,
+        // will set cnx->useAngle appropriately.
+        // Do this here so that we use ANGLE path when driver is ANGLE (e.g. loaded as native),
+        // not just loading ANGLE as option.
+        init_angle_backend(hnd->dso[2], cnx);
     }
 
     LOG_ALWAYS_FATAL_IF(!hnd,
@@ -311,11 +317,6 @@
     cnx->dso = nullptr;
 
     cnx->useAngle = false;
-
-    if (cnx->vendorEGL) {
-        dlclose(cnx->vendorEGL);
-        cnx->vendorEGL = nullptr;
-    }
 }
 
 void Loader::init_api(void* dso,
@@ -482,7 +483,7 @@
     return dso;
 }
 
-static void* load_angle_from_namespace(const char* kind, android_namespace_t* ns) {
+static void* load_angle(const char* kind, android_namespace_t* ns) {
     const android_dlextinfo dlextinfo = {
             .flags = ANDROID_DLEXT_USE_NAMESPACE,
             .library_namespace = ns,
@@ -502,61 +503,6 @@
     return nullptr;
 }
 
-static void* load_angle(const char* kind, android_namespace_t* ns, egl_connection_t* cnx) {
-    void* so = load_angle_from_namespace(kind, ns);
-
-    if (so) {
-        ALOGV("Loaded ANGLE %s library for '%s' (instead of native)", kind,
-            android::GraphicsEnv::getInstance().getAngleAppName().c_str());
-        cnx->useAngle = true;
-
-        char prop[PROPERTY_VALUE_MAX];
-
-        property_get("debug.hwui.renderer", prop, "UNSET");
-        ALOGV("Skia's renderer set to %s", prop);
-
-        EGLint angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
-        property_get("debug.angle.backend", prop, "0");
-        switch (atoi(prop)) {
-            case 1:
-                ALOGV("%s: Requesting OpenGLES back-end", __FUNCTION__);
-                angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE;
-                break;
-            case 2:
-                ALOGV("%s: Requesting Vulkan back-end", __FUNCTION__);
-                angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
-                break;
-            default:
-                break;
-        }
-
-        cnx->angleBackend = angleBackendDefault;
-        if (!cnx->vendorEGL && (cnx->angleBackend == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)) {
-            // Find and load vendor libEGL for ANGLE's GL back-end to use.
-            char prop[PROPERTY_VALUE_MAX + 1];
-            for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
-                if (property_get(key, prop, nullptr) <= 0) {
-                    continue;
-                }
-                void* dso = load_system_driver("EGL", prop, true);
-                if (dso) {
-                    cnx->vendorEGL = dso;
-                    break;
-                }
-            }
-            if (!cnx->vendorEGL) {
-                cnx->vendorEGL = load_system_driver("EGL", nullptr, true);
-            }
-        }
-    } else {
-        ALOGV("Loaded native %s library for '%s' (instead of ANGLE)", kind,
-            android::GraphicsEnv::getInstance().getAngleAppName().c_str());
-        cnx->useAngle = false;
-    }
-
-    return so;
-}
-
 static void* load_updated_driver(const char* kind, android_namespace_t* ns) {
     ATRACE_CALL();
     const android_dlextinfo dlextinfo = {
@@ -594,22 +540,33 @@
     driver_t* hnd = nullptr;
 
     // ANGLE doesn't ship with GLES library, and thus we skip GLES driver.
-    void* dso = load_angle("EGL", ns, cnx);
+    void* dso = load_angle("EGL", ns);
     if (dso) {
         initialize_api(dso, cnx, EGL);
         hnd = new driver_t(dso);
 
-        dso = load_angle("GLESv1_CM", ns, cnx);
+        dso = load_angle("GLESv1_CM", ns);
         initialize_api(dso, cnx, GLESv1_CM);
         hnd->set(dso, GLESv1_CM);
 
-        dso = load_angle("GLESv2", ns, cnx);
+        dso = load_angle("GLESv2", ns);
         initialize_api(dso, cnx, GLESv2);
         hnd->set(dso, GLESv2);
     }
     return hnd;
 }
 
+void Loader::init_angle_backend(void* dso, egl_connection_t* cnx) {
+    void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform");
+    if (pANGLEGetDisplayPlatform) {
+        ALOGV("ANGLE GLES library in use");
+        cnx->useAngle = true;
+    } else {
+        ALOGV("Native GLES library in use");
+        cnx->useAngle = false;
+    }
+}
+
 Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) {
     ATRACE_CALL();
 #ifndef __ANDROID_VNDK__
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
index 6f31ab4..7b2d7c9 100644
--- a/opengl/libs/EGL/Loader.h
+++ b/opengl/libs/EGL/Loader.h
@@ -60,6 +60,7 @@
     driver_t* attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix, const bool exact);
     void unload_system_driver(egl_connection_t* cnx);
     void initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask);
+    void init_angle_backend(void* dso, egl_connection_t* cnx);
 
     static __attribute__((noinline))
     void init_api(void* dso,
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 0581708..d5d57d7 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -16,8 +16,6 @@
 
 #include <stdlib.h>
 
-#include <hardware/gralloc.h>
-
 #include <EGL/egl.h>
 
 #include <cutils/properties.h>
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index a58e87c..6af7cd2 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -31,6 +31,7 @@
 #include "egl_object.h"
 #include "egl_tls.h"
 
+#include <SurfaceFlingerProperties.h>
 #include <android/dlext.h>
 #include <dlfcn.h>
 #include <graphicsenv/GraphicsEnv.h>
@@ -133,45 +134,6 @@
     return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list);
 }
 
-static bool addAnglePlatformAttributes(egl_connection_t* const cnx,
-                                       std::vector<EGLAttrib>& attrs) {
-    intptr_t vendorEGL = (intptr_t)cnx->vendorEGL;
-
-    attrs.reserve(4 * 2);
-
-    attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
-    attrs.push_back(cnx->angleBackend);
-
-    switch (cnx->angleBackend) {
-        case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
-            ALOGV("%s: Requesting Vulkan ANGLE back-end", __FUNCTION__);
-            char prop[PROPERTY_VALUE_MAX];
-            property_get("debug.angle.validation", prop, "0");
-            attrs.push_back(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE);
-            attrs.push_back(atoi(prop));
-            break;
-        case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
-            ALOGV("%s: Requesting Default ANGLE back-end", __FUNCTION__);
-            break;
-        case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
-            ALOGV("%s: Requesting OpenGL ES ANGLE back-end", __FUNCTION__);
-            // NOTE: This is only valid if the backend is OpenGL
-            attrs.push_back(EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE);
-            attrs.push_back(vendorEGL);
-
-            // Context virtualization is only available on GL back-end.
-            // Needed to support threading with GL back-end
-            attrs.push_back(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE);
-            attrs.push_back(EGL_FALSE);
-            break;
-        default:
-            ALOGE("%s: Requesting Unknown (%d) ANGLE back-end", __FUNCTION__, cnx->angleBackend);
-            break;
-    }
-
-    return true;
-}
-
 static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx,
                                           const EGLAttrib* attrib_list, EGLint* error) {
     EGLDisplay dpy = EGL_NO_DISPLAY;
@@ -186,11 +148,14 @@
             }
         }
 
-        if (!addAnglePlatformAttributes(cnx, attrs)) {
-            ALOGE("eglGetDisplay(%p) failed: Mismatch display request", display);
-            *error = EGL_BAD_PARAMETER;
-            return EGL_NO_DISPLAY;
-        }
+        attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
+        attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE);
+
+        char prop[PROPERTY_VALUE_MAX];
+        property_get("debug.angle.validation", prop, "0");
+        attrs.push_back(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE);
+        attrs.push_back(atoi(prop));
+
         attrs.push_back(EGL_NONE);
 
         dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
@@ -361,9 +326,7 @@
 
         // Note: CDD requires that devices supporting wide color and/or HDR color also support
         // the EGL_KHR_gl_colorspace extension.
-        bool wideColorBoardConfig =
-                getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(
-                        false);
+        bool wideColorBoardConfig = android::sysprop::has_wide_color_display(false);
 
         // Add wide-color extensions if device can support wide-color
         if (wideColorBoardConfig && hasColorSpaceSupport) {
@@ -373,8 +336,7 @@
                     "EGL_EXT_gl_colorspace_display_p3_passthrough ");
         }
 
-        bool hasHdrBoardConfig =
-                getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(false);
+        bool hasHdrBoardConfig = android::sysprop::has_HDR_display(false);
 
         if (hasHdrBoardConfig && hasColorSpaceSupport) {
             // hasHDRBoardConfig indicates the system is capable of supporting HDR content.
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index c8840f9..c976c60 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -23,8 +23,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <hardware/gralloc1.h>
-
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include <EGL/eglext_angle.h>
@@ -721,7 +719,7 @@
 
     // NOTE: When using Vulkan backend, the Vulkan runtime makes all the
     // native_window_* calls, so don't do them here.
-    if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+    if (!cnx->useAngle) {
         int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
         if (result < 0) {
             ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) "
@@ -740,14 +738,14 @@
     std::vector<AttrType> strippedAttribList;
     if (!processAttributes<AttrType>(dp, window, attrib_list, &colorSpace, &strippedAttribList)) {
         ALOGE("error invalid colorspace: %d", colorSpace);
-        if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+        if (!cnx->useAngle) {
             native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
         }
         return EGL_NO_SURFACE;
     }
     attrib_list = strippedAttribList.data();
 
-    if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+    if (!cnx->useAngle) {
         int err = native_window_set_buffers_format(window, format);
         if (err != 0) {
             ALOGE("error setting native window pixel format: %s (%d)", strerror(-err), err);
@@ -779,7 +777,7 @@
     }
 
     // EGLSurface creation failed
-    if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+    if (!cnx->useAngle) {
         native_window_set_buffers_format(window, 0);
         native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
     }
@@ -1419,7 +1417,7 @@
         }
     }
 
-    if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+    if (!s->cnx->useAngle) {
         if (!sendSurfaceMetadata(s)) {
             native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL);
             return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE);
@@ -1444,7 +1442,7 @@
         androidRect.bottom = y;
         androidRects.push_back(androidRect);
     }
-    if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) {
+    if (!s->cnx->useAngle) {
         native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(),
                                          androidRects.size());
     }
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
index 18a3949..5fbffbd 100644
--- a/opengl/libs/EGL/egldefs.h
+++ b/opengl/libs/EGL/egldefs.h
@@ -82,8 +82,6 @@
 
     bool                systemDriverUnloaded;
     bool                useAngle;       // Was ANGLE successfully loaded
-    EGLint              angleBackend;
-    void*               vendorEGL;
 };
 // clang-format on
 
diff --git a/opengl/tests/EGLTest/Android.bp b/opengl/tests/EGLTest/Android.bp
index 8bfe517..e3912a8 100644
--- a/opengl/tests/EGLTest/Android.bp
+++ b/opengl/tests/EGLTest/Android.bp
@@ -25,6 +25,7 @@
         "liblog",
         "libutils",
         "libnativewindow",
+        "libSurfaceFlingerProp",
     ],
 
     include_dirs: [
@@ -34,5 +35,6 @@
 
     header_libs: [
         "bionic_libc_platform_headers",
+        "libsurfaceflinger_headers",
     ],
 }
diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp
index cca91c3..510226d 100644
--- a/opengl/tests/EGLTest/EGL_test.cpp
+++ b/opengl/tests/EGLTest/EGL_test.cpp
@@ -16,6 +16,7 @@
 
 #include <gtest/gtest.h>
 
+#include <SurfaceFlingerProperties.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 
 #include <configstore/Utils.h>
@@ -52,11 +53,9 @@
 
 #define METADATA_SCALE(x) (static_cast<EGLint>(x * EGL_METADATA_SCALING_EXT))
 
-static bool hasWideColorDisplay =
-        getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false);
+static bool hasWideColorDisplay = android::sysprop::has_wide_color_display(false);
 
-static bool hasHdrDisplay =
-        getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(false);
+static bool hasHdrDisplay = android::sysprop::has_HDR_display(false);
 
 class EGLTest : public ::testing::Test {
 public:
diff --git a/services/gpuservice/bpfprogs/Android.bp b/services/gpuservice/bpfprogs/Android.bp
new file mode 100644
index 0000000..b875814
--- /dev/null
+++ b/services/gpuservice/bpfprogs/Android.bp
@@ -0,0 +1,22 @@
+// 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.
+
+bpf {
+    name: "gpu_mem.o",
+    srcs: ["gpu_mem.c"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/services/gpuservice/bpfprogs/gpu_mem.c b/services/gpuservice/bpfprogs/gpu_mem.c
new file mode 100644
index 0000000..c75213b
--- /dev/null
+++ b/services/gpuservice/bpfprogs/gpu_mem.c
@@ -0,0 +1,75 @@
+/*
+ * 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 <bpf_helpers.h>
+
+/*
+ * On Android the number of active processes using gpu is limited.
+ * So this is assumed to be true: SUM(num_procs_using_gpu[i]) <= 1024
+ */
+#define GPU_MEM_TOTAL_MAP_SIZE 1024
+
+/*
+ * This map maintains the global and per process gpu memory total counters.
+ *
+ * The KEY is ((gpu_id << 32) | pid) while VAL is the size in bytes.
+ * Use HASH type here since key is not int.
+ * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group.
+ */
+DEFINE_BPF_MAP_GRO(gpu_mem_total_map, HASH, uint64_t, uint64_t, GPU_MEM_TOTAL_MAP_SIZE,
+                   AID_GRAPHICS);
+
+/* This struct aligns with the fields offsets of the raw tracepoint format */
+struct gpu_mem_total_args {
+    uint64_t ignore;
+    /* Actual fields start at offset 8 */
+    uint32_t gpu_id;
+    uint32_t pid;
+    uint64_t size;
+};
+
+/*
+ * This program parses the gpu_mem/gpu_mem_total tracepoint's data into
+ * {KEY, VAL} pair used to update the corresponding bpf map.
+ *
+ * Pass AID_GRAPHICS as gid since gpuservice is in the graphics group.
+ * Upon seeing size 0, the corresponding KEY needs to be cleaned up.
+ */
+DEFINE_BPF_PROG("tracepoint/gpu_mem/gpu_mem_total", AID_ROOT, AID_GRAPHICS, tp_gpu_mem_total)
+(struct gpu_mem_total_args* args) {
+    uint64_t key = 0;
+    uint64_t cur_val = 0;
+    uint64_t* prev_val = NULL;
+
+    /* The upper 32 bits are for gpu_id while the lower is the pid */
+    key = ((uint64_t)args->gpu_id << 32) | args->pid;
+    cur_val = args->size;
+
+    if (!cur_val) {
+        bpf_gpu_mem_total_map_delete_elem(&key);
+        return 0;
+    }
+
+    prev_val = bpf_gpu_mem_total_map_lookup_elem(&key);
+    if (prev_val) {
+        *prev_val = cur_val;
+    } else {
+        bpf_gpu_mem_total_map_update_elem(&key, &cur_val, BPF_NOEXIST);
+    }
+    return 0;
+}
+
+char _license[] SEC("license") = "Apache 2.0";
diff --git a/services/gpuservice/gpumem/Android.bp b/services/gpuservice/gpumem/Android.bp
new file mode 100644
index 0000000..b2230b6
--- /dev/null
+++ b/services/gpuservice/gpumem/Android.bp
@@ -0,0 +1,41 @@
+// 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_library_shared {
+    name: "libgpumem",
+    srcs: [
+        "GpuMem.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbpf",
+        "libbpf_android",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+    export_include_dirs: ["include"],
+    export_shared_lib_headers: [
+        "libbase",
+        "libbpf_android",
+    ],
+    cppflags: [
+        "-Wall",
+        "-Werror",
+        "-Wformat",
+        "-Wthread-safety",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
new file mode 100644
index 0000000..1d4b524
--- /dev/null
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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 "GpuMem"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "gpumem/GpuMem.h"
+
+#include <android-base/stringprintf.h>
+#include <libbpf.h>
+#include <libbpf_android.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+
+using base::StringAppendF;
+
+GpuMem::~GpuMem() {
+    bpf_detach_tracepoint(kGpuMemTraceGroup, kGpuMemTotalTracepoint);
+}
+
+void GpuMem::initialize() {
+    // Make sure bpf programs are loaded
+    bpf::waitForProgsLoaded();
+
+    int fd = bpf::bpfFdGet(kGpuMemTotalProgPath, BPF_F_RDONLY);
+    if (fd < 0) {
+        ALOGE("Failed to retrieve pinned program from %s", kGpuMemTotalProgPath);
+        return;
+    }
+
+    // Attach the program to the tracepoint, and the tracepoint is automatically enabled here.
+    if (bpf_attach_tracepoint(fd, kGpuMemTraceGroup, kGpuMemTotalTracepoint) < 0) {
+        ALOGE("Failed to attach bpf program to %s/%s tracepoint", kGpuMemTraceGroup,
+              kGpuMemTotalTracepoint);
+        return;
+    }
+
+    // Use the read-only wrapper BpfMapRO to properly retrieve the read-only map.
+    auto map = bpf::BpfMapRO<uint64_t, uint64_t>(kGpuMemTotalMapPath);
+    if (!map.isValid()) {
+        ALOGE("Failed to create bpf map from %s", kGpuMemTotalMapPath);
+        return;
+    }
+    setGpuMemTotalMap(map);
+}
+
+void GpuMem::setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map) {
+    mGpuMemTotalMap = std::move(map);
+}
+
+// Dump the snapshots of global and per process memory usage on all gpus
+void GpuMem::dump(const Vector<String16>& /* args */, std::string* result) {
+    ATRACE_CALL();
+
+    if (!mGpuMemTotalMap.isValid()) {
+        result->append("Failed to initialize GPU memory eBPF\n");
+        return;
+    }
+
+    auto res = mGpuMemTotalMap.getFirstKey();
+    if (!res.ok()) {
+        result->append("GPU memory total usage map is empty\n");
+        return;
+    }
+    uint64_t key = res.value();
+    // unordered_map<gpu_id, vector<pair<pid, size>>>
+    std::unordered_map<uint32_t, std::vector<std::pair<uint32_t, uint64_t>>> dumpMap;
+    while (true) {
+        uint32_t gpu_id = key >> 32;
+        uint32_t pid = key;
+
+        res = mGpuMemTotalMap.readValue(key);
+        if (!res.ok()) break;
+        uint64_t size = res.value();
+
+        dumpMap[gpu_id].emplace_back(pid, size);
+
+        res = mGpuMemTotalMap.getNextKey(key);
+        if (!res.ok()) break;
+        key = res.value();
+    }
+
+    for (auto& gpu : dumpMap) {
+        if (gpu.second.empty()) continue;
+        StringAppendF(result, "Memory snapshot for GPU %u:\n", gpu.first);
+
+        std::sort(gpu.second.begin(), gpu.second.end(),
+                  [](auto& l, auto& r) { return l.first < r.first; });
+
+        int i = 0;
+        if (gpu.second[0].first != 0) {
+            StringAppendF(result, "Global total: N/A\n");
+        } else {
+            StringAppendF(result, "Global total: %" PRIu64 "\n", gpu.second[0].second);
+            i++;
+        }
+        for (; i < gpu.second.size(); i++) {
+            StringAppendF(result, "Proc %u total: %" PRIu64 "\n", gpu.second[i].first,
+                          gpu.second[i].second);
+        }
+    }
+}
+
+} // namespace android
diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
new file mode 100644
index 0000000..6d0322a
--- /dev/null
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
@@ -0,0 +1,56 @@
+/*
+ * 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 <bpf/BpfMap.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class GpuMem {
+public:
+    GpuMem() = default;
+    ~GpuMem();
+
+    // initialize eBPF program and map
+    void initialize();
+    // dumpsys interface
+    void dump(const Vector<String16>& args, std::string* result);
+
+private:
+    // Friend class for testing.
+    friend class TestableGpuMem;
+
+    // set gpu memory total map
+    void setGpuMemTotalMap(bpf::BpfMap<uint64_t, uint64_t>& map);
+
+    // bpf map for GPU memory total data
+    android::bpf::BpfMap<uint64_t, uint64_t> mGpuMemTotalMap;
+
+    // gpu memory tracepoint event category
+    static constexpr char kGpuMemTraceGroup[] = "gpu_mem";
+    // gpu memory total tracepoint
+    static constexpr char kGpuMemTotalTracepoint[] = "gpu_mem_total";
+    // pinned gpu memory total bpf c program path in bpf sysfs
+    static constexpr char kGpuMemTotalProgPath[] =
+            "/sys/fs/bpf/prog_gpu_mem_tracepoint_gpu_mem_gpu_mem_total";
+    // pinned gpu memory total bpf map path in bpf sysfs
+    static constexpr char kGpuMemTotalMapPath[] = "/sys/fs/bpf/map_gpu_mem_gpu_mem_total_map";
+};
+
+} // namespace android
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 7c5c9c5..5a14133 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -28,8 +28,8 @@
 static const int32_t INJECTOR_PID = 999;
 static const int32_t INJECTOR_UID = 1001;
 
-static const int32_t INJECT_EVENT_TIMEOUT = 5000;
-static const int32_t DISPATCHING_TIMEOUT = 100000;
+static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s;
+static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms;
 
 static nsecs_t now() {
     return systemTime(SYSTEM_TIME_MONOTONIC);
@@ -47,7 +47,7 @@
 private:
     virtual void notifyConfigurationChanged(nsecs_t) override {}
 
-    virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&, const sp<IBinder>&,
+    virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>&, const sp<IBinder>&,
                               const std::string& name) override {
         ALOGE("The window is not responding : %s", name.c_str());
         return 0;
@@ -98,7 +98,7 @@
     virtual ~FakeApplicationHandle() {}
 
     virtual bool updateInfo() {
-        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
         return true;
     }
 };
@@ -163,7 +163,7 @@
         mInfo.name = "FakeWindowHandle";
         mInfo.layoutParamsFlags = 0;
         mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
-        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
         mInfo.frameLeft = mFrame.left;
         mInfo.frameTop = mFrame.top;
         mInfo.frameRight = mFrame.right;
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 49630ad..a1eb007 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -37,6 +37,8 @@
             return "MOVE";
         case AMOTION_EVENT_ACTION_UP:
             return "UP";
+        case AMOTION_EVENT_ACTION_CANCEL:
+            return "CANCEL";
         case AMOTION_EVENT_ACTION_POINTER_DOWN:
             return "POINTER_DOWN";
         case AMOTION_EVENT_ACTION_POINTER_UP:
@@ -57,6 +59,7 @@
     }
     return StringPrintf("%" PRId32, action);
 }
+
 VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) {
     return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source,
              entry.displayId},
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 26b3be6..1d5870a 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -364,18 +364,16 @@
             break;
         }
     }
-    std::vector<uint8_t> data;
     const uint8_t* start = reinterpret_cast<const uint8_t*>(&event);
-    data.assign(start, start + size);
-    return sign(data);
+    return sign(start, size);
 }
 
-std::array<uint8_t, 32> HmacKeyManager::sign(const std::vector<uint8_t>& data) const {
+std::array<uint8_t, 32> HmacKeyManager::sign(const uint8_t* data, size_t size) const {
     // SHA256 always generates 32-bytes result
     std::array<uint8_t, 32> hash;
     unsigned int hashLen = 0;
-    uint8_t* result = HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data.data(), data.size(),
-                           hash.data(), &hashLen);
+    uint8_t* result =
+            HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data, size, hash.data(), &hashLen);
     if (result == nullptr) {
         ALOGE("Could not sign the data using HMAC");
         return INVALID_HMAC;
@@ -545,7 +543,7 @@
         }
 
         // Get ready to dispatch the event.
-        resetANRTimeoutsLocked();
+        resetAnrTimeoutsLocked();
     }
 
     // Now we have an event to dispatch.
@@ -895,7 +893,7 @@
 
 void InputDispatcher::releasePendingEventLocked() {
     if (mPendingEvent) {
-        resetANRTimeoutsLocked();
+        resetAnrTimeoutsLocked();
         releaseInboundEventLocked(mPendingEvent);
         mPendingEvent = nullptr;
     }
@@ -1159,14 +1157,16 @@
     }
 
     setInjectionResult(entry, injectionResult);
+    if (injectionResult == INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
+        ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent));
+        return true;
+    }
     if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
-        if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
-            CancelationOptions::Mode mode(isPointerEvent
-                                                  ? CancelationOptions::CANCEL_POINTER_EVENTS
-                                                  : CancelationOptions::CANCEL_NON_POINTER_EVENTS);
-            CancelationOptions options(mode, "input event injection failed");
-            synthesizeCancelationEventsForMonitorsLocked(options);
-        }
+        CancelationOptions::Mode mode(isPointerEvent
+                                              ? CancelationOptions::CANCEL_POINTER_EVENTS
+                                              : CancelationOptions::CANCEL_NON_POINTER_EVENTS);
+        CancelationOptions options(mode, "input event injection failed");
+        synthesizeCancelationEventsForMonitorsLocked(options);
         return true;
     }
 
@@ -1305,7 +1305,7 @@
     }
 
     if (currentTime >= mInputTargetWaitTimeoutTime) {
-        onANRLocked(currentTime, applicationHandle, windowHandle, entry.eventTime,
+        onAnrLocked(currentTime, applicationHandle, windowHandle, entry.eventTime,
                     mInputTargetWaitStartTime, reason);
 
         // Force poll loop to wake up immediately on next iteration once we get the
@@ -1351,14 +1351,7 @@
     }
 }
 
-nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) {
-    if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
-        return currentTime - mInputTargetWaitStartTime;
-    }
-    return 0;
-}
-
-void InputDispatcher::resetANRTimeoutsLocked() {
+void InputDispatcher::resetAnrTimeoutsLocked() {
     if (DEBUG_FOCUS) {
         ALOGD("Resetting ANR timeouts.");
     }
@@ -1400,7 +1393,6 @@
                                                         const EventEntry& entry,
                                                         std::vector<InputTarget>& inputTargets,
                                                         nsecs_t* nextWakeupTime) {
-    int32_t injectionResult;
     std::string reason;
 
     int32_t displayId = getTargetDisplayId(entry);
@@ -1413,54 +1405,38 @@
     // then drop the event.
     if (focusedWindowHandle == nullptr) {
         if (focusedApplicationHandle != nullptr) {
-            injectionResult =
-                    handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
-                                                nullptr, nextWakeupTime,
-                                                "Waiting because no window has focus but there is "
-                                                "a focused application that may eventually add a "
-                                                "window when it finishes starting up.");
-            goto Unresponsive;
+            return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
+                                               nullptr, nextWakeupTime,
+                                               "Waiting because no window has focus but there is "
+                                               "a focused application that may eventually add a "
+                                               "window when it finishes starting up.");
         }
 
         ALOGI("Dropping event because there is no focused window or focused application in display "
               "%" PRId32 ".",
               displayId);
-        injectionResult = INPUT_EVENT_INJECTION_FAILED;
-        goto Failed;
+        return INPUT_EVENT_INJECTION_FAILED;
     }
 
     // Check permissions.
     if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
-        injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-        goto Failed;
+        return INPUT_EVENT_INJECTION_PERMISSION_DENIED;
     }
 
     // Check whether the window is ready for more input.
     reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused");
     if (!reason.empty()) {
-        injectionResult =
-                handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
-                                            focusedWindowHandle, nextWakeupTime, reason.c_str());
-        goto Unresponsive;
+        return handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle,
+                                           focusedWindowHandle, nextWakeupTime, reason.c_str());
     }
 
     // Success!  Output targets.
-    injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
     addWindowTargetLocked(focusedWindowHandle,
                           InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS,
                           BitSet32(0), inputTargets);
 
     // Done.
-Failed:
-Unresponsive:
-    nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
-    updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
-    if (DEBUG_FOCUS) {
-        ALOGD("findFocusedWindow finished: injectionResult=%d, "
-              "timeSpentWaitingForApplication=%0.1fms",
-              injectionResult, timeSpentWaitingForApplication / 1000000.0);
-    }
-    return injectionResult;
+    return INPUT_EVENT_INJECTION_SUCCEEDED;
 }
 
 int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
@@ -1752,10 +1728,9 @@
                     checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle,
                                                        entry, "touched");
             if (!reason.empty()) {
-                injectionResult = handleTargetsNotReadyLocked(currentTime, entry, nullptr,
-                                                              touchedWindow.windowHandle,
-                                                              nextWakeupTime, reason.c_str());
-                goto Unresponsive;
+                return handleTargetsNotReadyLocked(currentTime, entry, nullptr,
+                                                   touchedWindow.windowHandle, nextWakeupTime,
+                                                   reason.c_str());
             }
         }
     }
@@ -1815,92 +1790,81 @@
         }
     }
 
+    if (injectionPermission != INJECTION_PERMISSION_GRANTED) {
+        return injectionResult;
+    }
+
     // Update final pieces of touch state if the injector had permission.
-    if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
-        if (!wrongDevice) {
-            if (switchedDevice) {
+    if (!wrongDevice) {
+        if (switchedDevice) {
+            if (DEBUG_FOCUS) {
+                ALOGD("Conflicting pointer actions: Switched to a different device.");
+            }
+            *outConflictingPointerActions = true;
+        }
+
+        if (isHoverAction) {
+            // Started hovering, therefore no longer down.
+            if (oldState && oldState->down) {
                 if (DEBUG_FOCUS) {
-                    ALOGD("Conflicting pointer actions: Switched to a different device.");
+                    ALOGD("Conflicting pointer actions: Hover received while pointer was "
+                          "down.");
                 }
                 *outConflictingPointerActions = true;
             }
+            tempTouchState.reset();
+            if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+                maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+                tempTouchState.deviceId = entry.deviceId;
+                tempTouchState.source = entry.source;
+                tempTouchState.displayId = displayId;
+            }
+        } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
+                   maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+            // All pointers up or canceled.
+            tempTouchState.reset();
+        } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+            // First pointer went down.
+            if (oldState && oldState->down) {
+                if (DEBUG_FOCUS) {
+                    ALOGD("Conflicting pointer actions: Down received while already down.");
+                }
+                *outConflictingPointerActions = true;
+            }
+        } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+            // One pointer went up.
+            if (isSplit) {
+                int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+                uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
 
-            if (isHoverAction) {
-                // Started hovering, therefore no longer down.
-                if (oldState && oldState->down) {
-                    if (DEBUG_FOCUS) {
-                        ALOGD("Conflicting pointer actions: Hover received while pointer was "
-                              "down.");
-                    }
-                    *outConflictingPointerActions = true;
-                }
-                tempTouchState.reset();
-                if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
-                    maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-                    tempTouchState.deviceId = entry.deviceId;
-                    tempTouchState.source = entry.source;
-                    tempTouchState.displayId = displayId;
-                }
-            } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
-                       maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
-                // All pointers up or canceled.
-                tempTouchState.reset();
-            } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
-                // First pointer went down.
-                if (oldState && oldState->down) {
-                    if (DEBUG_FOCUS) {
-                        ALOGD("Conflicting pointer actions: Down received while already down.");
-                    }
-                    *outConflictingPointerActions = true;
-                }
-            } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
-                // One pointer went up.
-                if (isSplit) {
-                    int32_t pointerIndex = getMotionEventActionPointerIndex(action);
-                    uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
-
-                    for (size_t i = 0; i < tempTouchState.windows.size();) {
-                        TouchedWindow& touchedWindow = tempTouchState.windows[i];
-                        if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
-                            touchedWindow.pointerIds.clearBit(pointerId);
-                            if (touchedWindow.pointerIds.isEmpty()) {
-                                tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
-                                continue;
-                            }
+                for (size_t i = 0; i < tempTouchState.windows.size();) {
+                    TouchedWindow& touchedWindow = tempTouchState.windows[i];
+                    if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
+                        touchedWindow.pointerIds.clearBit(pointerId);
+                        if (touchedWindow.pointerIds.isEmpty()) {
+                            tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
+                            continue;
                         }
-                        i += 1;
                     }
+                    i += 1;
                 }
             }
+        }
 
-            // Save changes unless the action was scroll in which case the temporary touch
-            // state was only valid for this one action.
-            if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
-                if (tempTouchState.displayId >= 0) {
-                    mTouchStatesByDisplay[displayId] = tempTouchState;
-                } else {
-                    mTouchStatesByDisplay.erase(displayId);
-                }
+        // Save changes unless the action was scroll in which case the temporary touch
+        // state was only valid for this one action.
+        if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
+            if (tempTouchState.displayId >= 0) {
+                mTouchStatesByDisplay[displayId] = tempTouchState;
+            } else {
+                mTouchStatesByDisplay.erase(displayId);
             }
+        }
 
-            // Update hover state.
-            mLastHoverWindowHandle = newHoverWindowHandle;
-        }
-    } else {
-        if (DEBUG_FOCUS) {
-            ALOGD("Not updating touch focus because injection was denied.");
-        }
+        // Update hover state.
+        mLastHoverWindowHandle = newHoverWindowHandle;
     }
 
-Unresponsive:
-
-    nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
-    updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
-    if (DEBUG_FOCUS) {
-        ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
-              "timeSpentWaitingForApplication=%0.1fms",
-              injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
-    }
     return injectionResult;
 }
 
@@ -1981,17 +1945,48 @@
     return true;
 }
 
+/**
+ * Indicate whether one window handle should be considered as obscuring
+ * another window handle. We only check a few preconditions. Actually
+ * checking the bounds is left to the caller.
+ */
+static bool canBeObscuredBy(const sp<InputWindowHandle>& windowHandle,
+                            const sp<InputWindowHandle>& otherHandle) {
+    // Compare by token so cloned layers aren't counted
+    if (haveSameToken(windowHandle, otherHandle)) {
+        return false;
+    }
+    auto info = windowHandle->getInfo();
+    auto otherInfo = otherHandle->getInfo();
+    if (!otherInfo->visible) {
+        return false;
+    } else if (info->ownerPid == otherInfo->ownerPid && otherHandle->getToken() == nullptr) {
+      // In general, if ownerPid is the same we don't want to generate occlusion
+      // events. This line is now necessary since we are including all Surfaces
+      // in occlusion calculation, so if we didn't check PID like this SurfaceView
+      // would occlude their parents. On the other hand before we started including
+      // all surfaces in occlusion calculation and had this line, we would count
+      // windows with an input channel from the same PID as occluding, and so we
+      // preserve this behavior with the getToken() == null check.
+        return false;
+    } else if (otherInfo->isTrustedOverlay()) {
+        return false;
+    } else if (otherInfo->displayId != info->displayId) {
+        return false;
+    }
+    return true;
+}
+
 bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
                                                     int32_t x, int32_t y) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
     const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
-        if (haveSameToken(otherHandle, windowHandle)) {
-            break;
+        if (windowHandle == otherHandle) {
+            break; // All future windows are below us. Exit early.
         }
-
         const InputWindowInfo* otherInfo = otherHandle->getInfo();
-        if (otherInfo->visible && !otherInfo->isTrustedOverlay() &&
+        if (canBeObscuredBy(windowHandle, otherHandle) &&
             otherInfo->frameContainsPoint(x, y)) {
             return true;
         }
@@ -2004,12 +1999,11 @@
     const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
     const InputWindowInfo* windowInfo = windowHandle->getInfo();
     for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
-        if (haveSameToken(otherHandle, windowHandle)) {
-            break;
+        if (windowHandle == otherHandle) {
+            break; // All future windows are below us. Exit early.
         }
-
         const InputWindowInfo* otherInfo = otherHandle->getInfo();
-        if (otherInfo->visible && !otherInfo->isTrustedOverlay() &&
+        if (canBeObscuredBy(windowHandle, otherHandle) &&
             otherInfo->overlaps(windowInfo)) {
             return true;
         }
@@ -2444,11 +2438,8 @@
         EventEntry* eventEntry = dispatchEntry->eventEntry;
         switch (eventEntry->type) {
             case EventEntry::Type::KEY: {
-                KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
-                VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEntry(*keyEntry);
-                verifiedEvent.flags = dispatchEntry->resolvedFlags & VERIFIED_KEY_EVENT_FLAGS;
-                verifiedEvent.action = dispatchEntry->resolvedAction;
-                std::array<uint8_t, 32> hmac = mHmacKeyManager.sign(verifiedEvent);
+                const KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
+                std::array<uint8_t, 32> hmac = getSignature(*keyEntry, *dispatchEntry);
 
                 // Publish the key event.
                 status =
@@ -2500,12 +2491,8 @@
                         usingCoords = scaledCoords;
                     }
                 }
-                VerifiedMotionEvent verifiedEvent =
-                        verifiedMotionEventFromMotionEntry(*motionEntry);
-                verifiedEvent.actionMasked =
-                        dispatchEntry->resolvedAction & AMOTION_EVENT_ACTION_MASK;
-                verifiedEvent.flags = dispatchEntry->resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS;
-                std::array<uint8_t, 32> hmac = mHmacKeyManager.sign(verifiedEvent);
+
+                std::array<uint8_t, 32> hmac = getSignature(*motionEntry, *dispatchEntry);
 
                 // Publish the motion event.
                 status = connection->inputPublisher
@@ -2585,6 +2572,28 @@
     }
 }
 
+const std::array<uint8_t, 32> InputDispatcher::getSignature(
+        const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const {
+    int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK;
+    if ((actionMasked == AMOTION_EVENT_ACTION_UP) || (actionMasked == AMOTION_EVENT_ACTION_DOWN)) {
+        // Only sign events up and down events as the purely move events
+        // are tied to their up/down counterparts so signing would be redundant.
+        VerifiedMotionEvent verifiedEvent = verifiedMotionEventFromMotionEntry(motionEntry);
+        verifiedEvent.actionMasked = actionMasked;
+        verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS;
+        return mHmacKeyManager.sign(verifiedEvent);
+    }
+    return INVALID_HMAC;
+}
+
+const std::array<uint8_t, 32> InputDispatcher::getSignature(
+        const KeyEntry& keyEntry, const DispatchEntry& dispatchEntry) const {
+    VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEntry(keyEntry);
+    verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_KEY_EVENT_FLAGS;
+    verifiedEvent.action = dispatchEntry.resolvedAction;
+    return mHmacKeyManager.sign(verifiedEvent);
+}
+
 void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime,
                                                 const sp<Connection>& connection, uint32_t seq,
                                                 bool handled) {
@@ -2697,7 +2706,10 @@
             // Monitor channels are never explicitly unregistered.
             // We do it automatically when the remote endpoint is closed so don't warn
             // about them.
-            notify = !connection->monitor;
+            const bool stillHaveWindowHandle =
+                    d->getWindowHandleLocked(connection->inputChannel->getConnectionToken()) !=
+                    nullptr;
+            notify = !connection->monitor && stillHaveWindowHandle;
             if (notify) {
                 ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  "
                       "events=0x%x",
@@ -3222,14 +3234,14 @@
 
 int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injectorPid,
                                           int32_t injectorUid, int32_t syncMode,
-                                          int32_t timeoutMillis, uint32_t policyFlags) {
+                                          std::chrono::milliseconds timeout, uint32_t policyFlags) {
 #if DEBUG_INBOUND_EVENT_DETAILS
     ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, "
-          "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x",
-          event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags);
+          "syncMode=%d, timeout=%lld, policyFlags=0x%08x",
+          event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags);
 #endif
 
-    nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis);
+    nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
 
     policyFlags |= POLICY_FLAG_INJECTED;
     if (hasInjectionPermission(injectorPid, injectorUid)) {
@@ -3416,8 +3428,7 @@
     } // release lock
 
 #if DEBUG_INJECTION
-    ALOGD("injectInputEvent - Finished with result %d.  "
-          "injectorPid=%d, injectorUid=%d",
+    ALOGD("injectInputEvent - Finished with result %d. injectorPid=%d, injectorUid=%d",
           injectionResult, injectorPid, injectorUid);
 #endif
 
@@ -3750,12 +3761,12 @@
         if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) {
             if (oldFocusedApplicationHandle != inputApplicationHandle) {
                 if (oldFocusedApplicationHandle != nullptr) {
-                    resetANRTimeoutsLocked();
+                    resetAnrTimeoutsLocked();
                 }
                 mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
             }
         } else if (oldFocusedApplicationHandle != nullptr) {
-            resetANRTimeoutsLocked();
+            resetAnrTimeoutsLocked();
             oldFocusedApplicationHandle.clear();
             mFocusedApplicationHandlesByDisplay.erase(displayId);
         }
@@ -3836,7 +3847,7 @@
 
         if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
             if (mDispatchFrozen && !frozen) {
-                resetANRTimeoutsLocked();
+                resetAnrTimeoutsLocked();
             }
 
             if (mDispatchEnabled && !enabled) {
@@ -3976,7 +3987,7 @@
     resetKeyRepeatLocked();
     releasePendingEventLocked();
     drainInboundQueueLocked();
-    resetANRTimeoutsLocked();
+    resetAnrTimeoutsLocked();
 
     mTouchStatesByDisplay.clear();
     mLastHoverWindowHandle.clear();
@@ -4480,7 +4491,7 @@
     postCommandLocked(std::move(commandEntry));
 }
 
-void InputDispatcher::onANRLocked(nsecs_t currentTime,
+void InputDispatcher::onAnrLocked(nsecs_t currentTime,
                                   const sp<InputApplicationHandle>& applicationHandle,
                                   const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
                                   nsecs_t waitStartTime, const char* reason) {
@@ -4497,19 +4508,19 @@
     localtime_r(&t, &tm);
     char timestr[64];
     strftime(timestr, sizeof(timestr), "%F %T", &tm);
-    mLastANRState.clear();
-    mLastANRState += INDENT "ANR:\n";
-    mLastANRState += StringPrintf(INDENT2 "Time: %s\n", timestr);
-    mLastANRState +=
+    mLastAnrState.clear();
+    mLastAnrState += INDENT "ANR:\n";
+    mLastAnrState += StringPrintf(INDENT2 "Time: %s\n", timestr);
+    mLastAnrState +=
             StringPrintf(INDENT2 "Window: %s\n",
                          getApplicationWindowLabel(applicationHandle, windowHandle).c_str());
-    mLastANRState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
-    mLastANRState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
-    mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason);
-    dumpDispatchStateLocked(mLastANRState);
+    mLastAnrState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency);
+    mLastAnrState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration);
+    mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason);
+    dumpDispatchStateLocked(mLastAnrState);
 
     std::unique_ptr<CommandEntry> commandEntry =
-            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyANRLockedInterruptible);
+            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
     commandEntry->inputApplicationHandle = applicationHandle;
     commandEntry->inputChannel =
             windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr;
@@ -4545,13 +4556,13 @@
     mLock.lock();
 }
 
-void InputDispatcher::doNotifyANRLockedInterruptible(CommandEntry* commandEntry) {
+void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) {
     sp<IBinder> token =
             commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
     mLock.unlock();
 
     nsecs_t newTimeout =
-            mPolicy->notifyANR(commandEntry->inputApplicationHandle, token, commandEntry->reason);
+            mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
 
     mLock.lock();
 
@@ -4616,6 +4627,7 @@
         dispatchEntry->eventEntry->appendDescription(msg);
         ALOGI("%s", msg.c_str());
     }
+    reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled);
 
     bool restartEvent;
     if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
@@ -4850,9 +4862,8 @@
     return event;
 }
 
-void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry,
-                                               int32_t injectionResult,
-                                               nsecs_t timeSpentWaitingForApplication) {
+void InputDispatcher::reportDispatchStatistics(std::chrono::nanoseconds eventDuration,
+                                               const Connection& connection, bool handled) {
     // TODO Write some statistics about how long we spend waiting.
 }
 
@@ -4912,9 +4923,9 @@
     dump += "Input Dispatcher State:\n";
     dumpDispatchStateLocked(dump);
 
-    if (!mLastANRState.empty()) {
+    if (!mLastAnrState.empty()) {
         dump += "\nInput Dispatcher State at time of last ANR:\n";
-        dump += mLastANRState;
+        dump += mLastAnrState;
     }
 }
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 4707bee..e87a306 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -62,7 +62,7 @@
     std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
 
 private:
-    std::array<uint8_t, 32> sign(const std::vector<uint8_t>& data) const;
+    std::array<uint8_t, 32> sign(const uint8_t* data, size_t size) const;
     const std::array<uint8_t, 128> mHmacKey;
 };
 
@@ -103,7 +103,8 @@
     virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
 
     virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
-                                     int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
+                                     int32_t injectorUid, int32_t syncMode,
+                                     std::chrono::milliseconds timeout,
                                      uint32_t policyFlags) override;
 
     virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
@@ -220,7 +221,11 @@
     // the pointer stream in order to claim it for a system gesture.
     std::unordered_map<int32_t, std::vector<Monitor>> mGestureMonitorsByDisplay GUARDED_BY(mLock);
 
-    HmacKeyManager mHmacKeyManager;
+    const HmacKeyManager mHmacKeyManager;
+    const std::array<uint8_t, 32> getSignature(const MotionEntry& motionEntry,
+                                               const DispatchEntry& dispatchEntry) const;
+    const std::array<uint8_t, 32> getSignature(const KeyEntry& keyEntry,
+                                               const DispatchEntry& dispatchEntry) const;
 
     // Event injection and synchronization.
     std::condition_variable mInjectionResultAvailable;
@@ -312,7 +317,7 @@
     int32_t mFocusedDisplayId GUARDED_BY(mLock);
 
     // Dispatcher state at time of last ANR.
-    std::string mLastANRState GUARDED_BY(mLock);
+    std::string mLastAnrState GUARDED_BY(mLock);
 
     // Dispatch inbound events.
     bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry)
@@ -358,7 +363,7 @@
                                                  const sp<IBinder>& inputConnectionToken)
             REQUIRES(mLock);
     nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
-    void resetANRTimeoutsLocked() REQUIRES(mLock);
+    void resetAnrTimeoutsLocked() REQUIRES(mLock);
 
     int32_t getTargetDisplayId(const EventEntry& entry);
     int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry,
@@ -463,7 +468,7 @@
             REQUIRES(mLock);
     void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
                               const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
-    void onANRLocked(nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
+    void onAnrLocked(nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
                      const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
                      nsecs_t waitStartTime, const char* reason) REQUIRES(mLock);
 
@@ -472,7 +477,7 @@
             REQUIRES(mLock);
     void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void doNotifyANRLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
+    void doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
     void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry)
             REQUIRES(mLock);
     void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
@@ -491,8 +496,8 @@
     LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD};
 
     void reportTouchEventForStatistics(const MotionEntry& entry);
-    void updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry,
-                                  int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
+    void reportDispatchStatistics(std::chrono::nanoseconds eventDuration,
+                                  const Connection& connection, bool handled);
     void traceInboundQueueLengthLocked() REQUIRES(mLock);
     void traceOutboundQueueLength(const sp<Connection>& connection);
     void traceWaitQueueLength(const sp<Connection>& connection);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 09dc92c..9b002f4 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -90,8 +90,8 @@
      * This method may be called on any thread (usually by the input manager).
      */
     virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid,
-                                     int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
-                                     uint32_t policyFlags) = 0;
+                                     int32_t injectorUid, int32_t syncMode,
+                                     std::chrono::milliseconds timeout, uint32_t policyFlags) = 0;
 
     /*
      * Check whether InputEvent actually happened by checking the signature of the event.
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 4214488..667af9b 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -47,7 +47,7 @@
 
     /* Notifies the system that an application is not responding.
      * Returns a new timeout to continue waiting, or 0 to abort dispatch. */
-    virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
+    virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
                               const sp<IBinder>& token, const std::string& reason) = 0;
 
     /* Notifies the system that an input channel is unrecoverably broken. */
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index f8d0150..8317b05 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -22,7 +22,6 @@
 #include <input/Input.h>
 #include <input/TouchVideoFrame.h>
 #include <utils/RefBase.h>
-#include <utils/Vector.h>
 
 namespace android {
 
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index d77c8c8..43bd9f1 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -255,6 +255,7 @@
                       getDeviceName().c_str());
                 cancelTouch(when);
             }
+            continue;
         }
 
         if (outCount >= MAX_POINTERS) {
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index e0d32a0..f33cc65 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -83,9 +83,13 @@
                                         args.displayId);
     }
 
-    void assertFilterInputEventWasNotCalled() { ASSERT_EQ(nullptr, mFilteredEvent); }
+    void assertFilterInputEventWasNotCalled() {
+        std::scoped_lock lock(mLock);
+        ASSERT_EQ(nullptr, mFilteredEvent);
+    }
 
     void assertNotifyConfigurationChangedWasCalled(nsecs_t when) {
+        std::scoped_lock lock(mLock);
         ASSERT_TRUE(mConfigurationChangedTime)
                 << "Timed out waiting for configuration changed call";
         ASSERT_EQ(*mConfigurationChangedTime, when);
@@ -93,6 +97,7 @@
     }
 
     void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) {
+        std::scoped_lock lock(mLock);
         ASSERT_TRUE(mLastNotifySwitch);
         // We do not check id because it is not exposed to the policy
         EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime);
@@ -103,11 +108,13 @@
     }
 
     void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
+        std::scoped_lock lock(mLock);
         ASSERT_EQ(touchedToken, mOnPointerDownToken);
         mOnPointerDownToken.clear();
     }
 
     void assertOnPointerDownWasNotCalled() {
+        std::scoped_lock lock(mLock);
         ASSERT_TRUE(mOnPointerDownToken == nullptr)
                 << "Expected onPointerDownOutsideFocus to not have been called";
     }
@@ -118,32 +125,32 @@
     }
 
 private:
-    std::unique_ptr<InputEvent> mFilteredEvent;
-    std::optional<nsecs_t> mConfigurationChangedTime;
-    sp<IBinder> mOnPointerDownToken;
-    std::optional<NotifySwitchArgs> mLastNotifySwitch;
+    std::mutex mLock;
+    std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
+    std::optional<nsecs_t> mConfigurationChangedTime GUARDED_BY(mLock);
+    sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock);
+    std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
 
     virtual void notifyConfigurationChanged(nsecs_t when) override {
+        std::scoped_lock lock(mLock);
         mConfigurationChangedTime = when;
     }
 
-    virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&,
-            const sp<IBinder>&,
-            const std::string&) {
+    virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>&, const sp<IBinder>&,
+                              const std::string&) override {
         return 0;
     }
 
-    virtual void notifyInputChannelBroken(const sp<IBinder>&) {
-    }
+    virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
 
-    virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) {
-    }
+    virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
 
-    virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
+    virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
         *outConfig = mConfig;
     }
 
     virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+        std::scoped_lock lock(mLock);
         switch (inputEvent->getType()) {
             case AINPUT_EVENT_TYPE_KEY: {
                 const KeyEvent* keyEvent = static_cast<const KeyEvent*>(inputEvent);
@@ -160,43 +167,43 @@
         return true;
     }
 
-    virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) {
-    }
+    virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
 
-    virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) {
-    }
+    virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
 
-    virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&,
-            const KeyEvent*, uint32_t) {
+    virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*,
+                                                  uint32_t) override {
         return 0;
     }
 
-    virtual bool dispatchUnhandledKey(const sp<IBinder>&,
-            const KeyEvent*, uint32_t, KeyEvent*) {
+    virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t,
+                                      KeyEvent*) override {
         return false;
     }
 
     virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
                               uint32_t policyFlags) override {
+        std::scoped_lock lock(mLock);
         /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
          * essentially a passthrough for notifySwitch.
          */
         mLastNotifySwitch = NotifySwitchArgs(1 /*id*/, when, policyFlags, switchValues, switchMask);
     }
 
-    virtual void pokeUserActivity(nsecs_t, int32_t) {
-    }
+    virtual void pokeUserActivity(nsecs_t, int32_t) override {}
 
-    virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) {
+    virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
         return false;
     }
 
-    virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) {
+    virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
+        std::scoped_lock lock(mLock);
         mOnPointerDownToken = newToken;
     }
 
     void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
                                          int32_t displayId) {
+        std::scoped_lock lock(mLock);
         ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
         ASSERT_EQ(mFilteredEvent->getType(), type);
 
@@ -313,18 +320,18 @@
                      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))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject key events with undefined action.";
 
     // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
     event.initialize(InputEvent::nextId(), 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))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject key events with ACTION_MULTIPLE.";
 }
 
@@ -350,9 +357,9 @@
                      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))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with undefined action.";
 
     // Rejects pointer down with invalid index.
@@ -363,9 +370,9 @@
                      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))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with pointer down index too large.";
 
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
@@ -375,9 +382,9 @@
                      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))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with pointer down index too small.";
 
     // Rejects pointer up with invalid index.
@@ -388,9 +395,9 @@
                      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))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with pointer up index too large.";
 
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
@@ -400,9 +407,9 @@
                      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))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with pointer up index too small.";
 
     // Rejects motion events with invalid number of pointers.
@@ -412,9 +419,9 @@
                      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))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with 0 pointers.";
 
     event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
@@ -423,9 +430,9 @@
                      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,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with more than MAX_POINTERS pointers.";
 
     // Rejects motion events with invalid pointer ids.
@@ -436,9 +443,9 @@
                      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))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with pointer ids less than 0.";
 
     pointerProperties[0].id = MAX_POINTER_ID + 1;
@@ -448,9 +455,9 @@
                      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))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
 
     // Rejects motion events with duplicate pointer ids.
@@ -462,9 +469,9 @@
                      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,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with duplicate pointer ids.";
 }
 
@@ -490,16 +497,16 @@
 }
 
 // --- InputDispatcherTest SetInputWindowTest ---
-static constexpr int32_t INJECT_EVENT_TIMEOUT = 500;
-static constexpr nsecs_t DISPATCHING_TIMEOUT = seconds_to_nanoseconds(5);
+static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms;
+static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
 
 class FakeApplicationHandle : public InputApplicationHandle {
 public:
     FakeApplicationHandle() {}
     virtual ~FakeApplicationHandle() {}
 
-    virtual bool updateInfo() {
-        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+    virtual bool updateInfo() override {
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
         return true;
     }
 };
@@ -634,11 +641,11 @@
         mInfo.applicationInfo = *inputApplicationHandle->getInfo();
 
         mInfo.token = token;
-        mInfo.id = 0;
+        mInfo.id = sId++;
         mInfo.name = name;
         mInfo.layoutParamsFlags = 0;
         mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
-        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
         mInfo.frameLeft = 0;
         mInfo.frameTop = 0;
         mInfo.frameRight = WIDTH;
@@ -672,8 +679,6 @@
 
     void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; }
 
-    void setId(int32_t id) { mInfo.id = id; }
-
     void setWindowScale(float xScale, float yScale) {
         mInfo.windowXScale = xScale;
         mInfo.windowYScale = yScale;
@@ -755,8 +760,11 @@
 private:
     const std::string mName;
     std::unique_ptr<FakeInputReceiver> mInputReceiver;
+    static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
 };
 
+std::atomic<int32_t> FakeWindowHandle::sId{1};
+
 static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
         int32_t displayId = ADISPLAY_ID_NONE) {
     KeyEvent event;
@@ -810,13 +818,15 @@
 }
 
 static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
-                                int32_t displayId, int32_t x = 100, int32_t y = 200) {
-    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, x, y);
+                                int32_t displayId, const PointF& location = {100, 200}) {
+    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location.x,
+                             location.y);
 }
 
 static int32_t injectMotionUp(const sp<InputDispatcher>& dispatcher, int32_t source,
-                              int32_t displayId, int32_t x = 100, int32_t y = 200) {
-    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, x, y);
+                              int32_t displayId, const PointF& location = {100, 200}) {
+    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location.x,
+                             location.y);
 }
 
 static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
@@ -880,6 +890,55 @@
     window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
 }
 
+/**
+ * Calling setInputWindows once with FLAG_NOT_TOUCH_MODAL should not cause any issues.
+ * To ensure that window receives only events that were directly inside of it, add
+ * FLAG_NOT_TOUCH_MODAL. This will enforce using the touchableRegion of the input
+ * when finding touched windows.
+ * This test serves as a sanity check for the next test, where setInputWindows is
+ * called twice.
+ */
+TEST_F(InputDispatcherTest, SetInputWindowOnce_SingleWindowTouch) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+    window->setFrame(Rect(0, 0, 100, 100));
+    window->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {50, 50}))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+    // Window should receive motion event.
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
+/**
+ * Calling setInputWindows twice, with the same info, should not cause any issues.
+ * To ensure that window receives only events that were directly inside of it, add
+ * FLAG_NOT_TOUCH_MODAL. This will enforce using the touchableRegion of the input
+ * when finding touched windows.
+ */
+TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+    window->setFrame(Rect(0, 0, 100, 100));
+    window->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {50, 50}))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+
+    // Window should receive motion event.
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
 // The foreground window should receive the first touch down event.
 TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) {
     sp<FakeApplicationHandle> application = new FakeApplicationHandle();
@@ -1821,7 +1880,6 @@
                 new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
         mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
         mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
-        mFocusedWindowTouchPoint = 60;
 
         // Set focused application.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
@@ -1842,15 +1900,16 @@
 protected:
     sp<FakeWindowHandle> mUnfocusedWindow;
     sp<FakeWindowHandle> mFocusedWindow;
-    int32_t mFocusedWindowTouchPoint;
+    static constexpr PointF FOCUSED_WINDOW_TOUCH_POINT = {60, 60};
 };
 
 // Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
 // DOWN on the window that doesn't have focus. Ensure the window that didn't have focus received
 // the onPointerDownOutsideFocus callback.
 TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {20, 20}))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     mUnfocusedWindow->consumeMotionDown();
 
@@ -1862,8 +1921,8 @@
 // DOWN on the window that doesn't have focus. Ensure no window received the
 // onPointerDownOutsideFocus callback.
 TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
-            AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, {20, 20}))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     mFocusedWindow->consumeMotionDown();
 
@@ -1889,7 +1948,7 @@
         OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
               injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
-                               mFocusedWindowTouchPoint, mFocusedWindowTouchPoint))
+                               FOCUSED_WINDOW_TOUCH_POINT))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     mFocusedWindow->consumeMotionDown();
 
@@ -1910,14 +1969,12 @@
         // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows.
         mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
                                       InputWindowInfo::FLAG_SPLIT_TOUCH);
-        mWindow1->setId(0);
         mWindow1->setFrame(Rect(0, 0, 100, 100));
 
         mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2",
                                         ADISPLAY_ID_DEFAULT, mWindow1->getToken());
         mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
                                       InputWindowInfo::FLAG_SPLIT_TOUCH);
-        mWindow2->setId(1);
         mWindow2->setFrame(Rect(100, 100, 200, 200));
 
         mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 675b77b..109edfe 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -7082,6 +7082,76 @@
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
 }
 
+/**
+ * Test multi-touch should be canceled when received the MT_TOOL_PALM event from some finger,
+ * and could be allowed again after all non-MT_TOOL_PALM is release and the new point is
+ * MT_TOOL_FINGER.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType2) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    NotifyMotionArgs motionArgs;
+
+    // default tool type is finger
+    constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
+    processId(mapper, 1);
+    processPosition(mapper, x1, y1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // Second finger down.
+    processSlot(mapper, 1);
+    processPosition(mapper, x2, y2);
+    processId(mapper, 2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // If the tool type of the first pointer changes to MT_TOOL_PALM,
+    // the entire gesture should be aborted, so we expect to receive ACTION_CANCEL.
+    processSlot(mapper, 0);
+    processId(mapper, 1);
+    processToolType(mapper, MT_TOOL_PALM);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+
+    // Ignore the following MOVE and UP events if had detect a palm event.
+    processSlot(mapper, 1);
+    processId(mapper, 2);
+    processPosition(mapper, x3, y3);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // second finger up.
+    processId(mapper, -1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // first finger move, but still in palm
+    processSlot(mapper, 0);
+    processId(mapper, 1);
+    processPosition(mapper, x1 - 1, y1 - 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // second finger down, expect as new finger down.
+    processSlot(mapper, 1);
+    processId(mapper, 2);
+    processPosition(mapper, x2, y2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+}
+
 // --- MultiTouchInputMapperTest_ExternalDevice ---
 
 class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index eaf7fa8..ec3dfc8 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -4,6 +4,7 @@
     srcs: [
         "BatterySaverPolicyConfig.cpp",
         "CoolingDevice.cpp",
+        "PowerHalController.cpp",
         "PowerHalLoader.cpp",
         "PowerHalWrapper.cpp",
         "PowerSaveState.cpp",
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
new file mode 100644
index 0000000..d26d582
--- /dev/null
+++ b/services/powermanager/PowerHalController.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "PowerHalController"
+#include <utils/Log.h>
+
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+
+#include <powermanager/PowerHalController.h>
+#include <powermanager/PowerHalLoader.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+
+namespace android {
+
+// -------------------------------------------------------------------------------------------------
+
+std::unique_ptr<PowerHalWrapper> PowerHalConnector::connect() {
+    sp<IPowerAidl> halAidl = PowerHalLoader::loadAidl();
+    if (halAidl) {
+        return std::make_unique<AidlPowerHalWrapper>(halAidl);
+    }
+    sp<IPowerV1_0> halHidlV1_0 = PowerHalLoader::loadHidlV1_0();
+    sp<IPowerV1_1> halHidlV1_1 = PowerHalLoader::loadHidlV1_1();
+    if (halHidlV1_1) {
+        return std::make_unique<HidlPowerHalWrapperV1_1>(halHidlV1_0, halHidlV1_1);
+    }
+    if (halHidlV1_0) {
+        return std::make_unique<HidlPowerHalWrapperV1_0>(halHidlV1_0);
+    }
+    return nullptr;
+}
+
+void PowerHalConnector::reset() {
+    PowerHalLoader::unloadAll();
+}
+
+// -------------------------------------------------------------------------------------------------
+
+void PowerHalController::init() {
+    initHal();
+}
+
+// Check validity of current handle to the power HAL service, and create a new one if necessary.
+std::shared_ptr<PowerHalWrapper> PowerHalController::initHal() {
+    std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+    if (mConnectedHal == nullptr) {
+        mConnectedHal = mHalConnector->connect();
+        if (mConnectedHal == nullptr) {
+            // Unable to connect to Power HAL service. Fallback to default.
+            return mDefaultHal;
+        }
+    }
+    return mConnectedHal;
+}
+
+// Check if a call to Power HAL function failed; if so, log the failure and invalidate the
+// current Power HAL handle.
+PowerHalResult PowerHalController::processHalResult(PowerHalResult result, const char* fnName) {
+    if (result == PowerHalResult::FAILED) {
+        ALOGE("%s() failed: power HAL service not available.", fnName);
+        std::lock_guard<std::mutex> lock(mConnectedHalMutex);
+        // Drop Power HAL handle. This will force future api calls to reconnect.
+        mConnectedHal = nullptr;
+        mHalConnector->reset();
+    }
+    return result;
+}
+
+PowerHalResult PowerHalController::setBoost(Boost boost, int32_t durationMs) {
+    std::shared_ptr<PowerHalWrapper> handle = initHal();
+    auto result = handle->setBoost(boost, durationMs);
+    return processHalResult(result, "setBoost");
+}
+
+PowerHalResult PowerHalController::setMode(Mode mode, bool enabled) {
+    std::shared_ptr<PowerHalWrapper> handle = initHal();
+    auto result = handle->setMode(mode, enabled);
+    return processHalResult(result, "setMode");
+}
+
+}; // namespace android
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 1cf170e..70837cf 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -12,11 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-
 cc_test {
     name: "powermanager_test",
     test_suites: ["device-tests"],
     srcs: [
+        "PowerHalControllerTest.cpp",
         "PowerHalLoaderTest.cpp",
         "PowerHalWrapperAidlTest.cpp",
         "PowerHalWrapperHidlV1_0Test.cpp",
diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp
new file mode 100644
index 0000000..ac1e19a
--- /dev/null
+++ b/services/powermanager/tests/PowerHalControllerTest.cpp
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "PowerHalControllerTest"
+
+#include <android/hardware/power/Boost.h>
+#include <android/hardware/power/IPower.h>
+#include <android/hardware/power/Mode.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <powermanager/PowerHalWrapper.h>
+#include <powermanager/PowerHalController.h>
+
+#include <thread>
+#include <utils/Log.h>
+
+using android::hardware::power::Boost;
+using android::hardware::power::Mode;
+using android::hardware::power::V1_0::Feature;
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::PowerHint;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+// -------------------------------------------------------------------------------------------------
+
+class MockIPowerV1_0 : public IPower {
+public:
+    MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override));
+    MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override));
+    MOCK_METHOD(
+        hardware::Return<void>, setFeature, (Feature feature, bool activate), (override));
+    MOCK_METHOD(
+        hardware::Return<void>, getPlatformLowPowerStats,
+        (getPlatformLowPowerStats_cb _hidl_cb), (override));
+};
+
+class TestPowerHalConnector : public PowerHalConnector {
+public:
+    TestPowerHalConnector(sp<IPower> powerHal) : mHal(std::move(powerHal)) {}
+    virtual ~TestPowerHalConnector() = default;
+
+    virtual std::unique_ptr<PowerHalWrapper> connect() override {
+        mCountMutex.lock();
+        ++mConnectedCount;
+        mCountMutex.unlock();
+        return std::make_unique<HidlPowerHalWrapperV1_0>(mHal);
+    }
+
+    void reset() override {
+        mCountMutex.lock();
+        ++mResetCount;
+        mCountMutex.unlock();
+    }
+
+    int getConnectCount() {
+        return mConnectedCount;
+    }
+
+    int getResetCount() {
+        return mResetCount;
+    }
+
+private:
+    sp<IPower> mHal = nullptr;
+    std::mutex mCountMutex;
+    int mConnectedCount = 0;
+    int mResetCount = 0;
+};
+
+class AlwaysFailingTestPowerHalConnector : public TestPowerHalConnector {
+public:
+    AlwaysFailingTestPowerHalConnector() : TestPowerHalConnector(nullptr) {}
+
+    std::unique_ptr<PowerHalWrapper> connect() override {
+        // Call parent to update counter, but ignore connected PowerHalWrapper.
+        TestPowerHalConnector::connect();
+        return nullptr;
+    }
+};
+
+// -------------------------------------------------------------------------------------------------
+
+class PowerHalControllerTest : public Test {
+public:
+    void SetUp() override {
+        mMockHal = new StrictMock<MockIPowerV1_0>();
+        std::unique_ptr<TestPowerHalConnector> halConnector =
+            std::make_unique<TestPowerHalConnector>(mMockHal);
+        mHalConnector = halConnector.get();
+        mHalController = std::make_unique<PowerHalController>(std::move(halConnector));
+    }
+
+protected:
+    sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr;
+    TestPowerHalConnector* mHalConnector = nullptr;
+    std::unique_ptr<PowerHalController> mHalController = nullptr;
+};
+
+// -------------------------------------------------------------------------------------------------
+
+TEST_F(PowerHalControllerTest, TestInitConnectsToPowerHalOnlyOnce) {
+    int powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 0);
+
+    mHalController->init();
+    mHalController->init();
+
+    // PowerHalConnector was called only once and never reset.
+    powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 1);
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestUnableToConnectToPowerHalIgnoresAllApiCalls) {
+    std::unique_ptr<AlwaysFailingTestPowerHalConnector> halConnector =
+        std::make_unique<AlwaysFailingTestPowerHalConnector>();
+    AlwaysFailingTestPowerHalConnector* failingHalConnector = halConnector.get();
+    PowerHalController halController(std::move(halConnector));
+
+    int powerHalConnectCount = failingHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 0);
+
+    // Still works with EmptyPowerHalWrapper as fallback ignoring every api call and logging.
+    auto result = halController.setBoost(Boost::INTERACTION, 1000);
+    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    result = halController.setMode(Mode::LAUNCH, true);
+    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+
+    // PowerHalConnector was called every time to attempt to reconnect with underlying service.
+    powerHalConnectCount = failingHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 2);
+    // PowerHalConnector was never reset.
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestAllApiCallsDelegatedToConnectedPowerHal) {
+    int powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 0);
+
+    {
+        InSequence seg;
+        EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(100)))
+            .Times(Exactly(1));
+        EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1)))
+            .Times(Exactly(1));
+    }
+
+    auto result = mHalController->setBoost(Boost::INTERACTION, 100);
+    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    result = mHalController->setMode(Mode::LAUNCH, true);
+    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+
+    // PowerHalConnector was called only once and never reset.
+    powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 1);
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestPowerHalRecoversFromFailureByRecreatingPowerHal) {
+    int powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 0);
+
+    ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
+        .WillByDefault([](PowerHint, int32_t) {
+            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+        });
+
+    EXPECT_CALL(*mMockHal.get(), powerHint(_, _))
+        .Times(Exactly(4));
+
+    auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    result = mHalController->setMode(Mode::LAUNCH, true);
+    ASSERT_EQ(PowerHalResult::FAILED, result);
+    result = mHalController->setMode(Mode::VR, false);
+    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+    result = mHalController->setMode(Mode::LOW_POWER, true);
+    ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+
+    // PowerHalConnector was called only twice: on first api call and after failed call.
+    powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 2);
+    // PowerHalConnector was reset once after failed call.
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_EQ(powerHalResetCount, 1);
+}
+
+TEST_F(PowerHalControllerTest, TestPowerHalDoesNotTryToRecoverFromFailureOnUnsupportedCalls) {
+    int powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 0);
+
+    auto result = mHalController->setBoost(Boost::CAMERA_LAUNCH, 1000);
+    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+    result = mHalController->setMode(Mode::CAMERA_STREAMING_HIGH, true);
+    ASSERT_EQ(PowerHalResult::UNSUPPORTED, result);
+
+    // PowerHalConnector was called only once and never reset.
+    powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 1);
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestMultiThreadConnectsOnlyOnce) {
+    int powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 0);
+
+    EXPECT_CALL(*mMockHal.get(), powerHint(_, _))
+        .Times(Exactly(10));
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+            ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+        }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+    // PowerHalConnector was called only by the first thread to use the api and never reset.
+    powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 1);
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_EQ(powerHalResetCount, 0);
+}
+
+TEST_F(PowerHalControllerTest, TestMultiThreadWithFailureReconnectIsThreadSafe) {
+    int powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_EQ(powerHalConnectCount, 0);
+
+    ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _))
+        .WillByDefault([](PowerHint, int32_t) {
+            return hardware::Return<void>(hardware::Status::fromExceptionCode(-1));
+        });
+
+    EXPECT_CALL(*mMockHal.get(), powerHint(_, _))
+        .Times(Exactly(40));
+
+    std::vector<std::thread> threads;
+    for (int i = 0; i < 10; i++) {
+        threads.push_back(std::thread([&]() {
+            auto result = mHalController->setBoost(Boost::INTERACTION, 1000);
+            ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+        }));
+        threads.push_back(std::thread([&]() {
+            auto result = mHalController->setMode(Mode::LAUNCH, true);
+            ASSERT_EQ(PowerHalResult::FAILED, result);
+        }));
+        threads.push_back(std::thread([&]() {
+            auto result = mHalController->setMode(Mode::LOW_POWER, false);
+            ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+        }));
+        threads.push_back(std::thread([&]() {
+            auto result = mHalController->setMode(Mode::VR, true);
+            ASSERT_EQ(PowerHalResult::SUCCESSFUL, result);
+        }));
+    }
+    std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); });
+
+    // PowerHalConnector was called at least once by the first thread.
+    // Reset and reconnect calls were made at most 10 times, once after each failure.
+    powerHalConnectCount = mHalConnector->getConnectCount();
+    EXPECT_THAT(powerHalConnectCount, AllOf(Ge(1), Le(11)));
+    int powerHalResetCount = mHalConnector->getResetCount();
+    EXPECT_THAT(powerHalResetCount, Le(10));
+}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index fce0971..0182937 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -286,6 +286,7 @@
     export_shared_lib_headers: [
         "android.hardware.graphics.common@1.2",
         "libhidlbase",
+        "libui",
     ],
     export_static_lib_headers: [
         "SurfaceFlingerProperties",
diff --git a/services/surfaceflinger/Barrier.h b/services/surfaceflinger/Barrier.h
deleted file mode 100644
index 97028a8..0000000
--- a/services/surfaceflinger/Barrier.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2007 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_BARRIER_H
-#define ANDROID_BARRIER_H
-
-#include <stdint.h>
-#include <condition_variable>
-#include <mutex>
-
-namespace android {
-
-class Barrier
-{
-public:
-    // Release any threads waiting at the Barrier.
-    // Provides release semantics: preceding loads and stores will be visible
-    // to other threads before they wake up.
-    void open() {
-        std::lock_guard<std::mutex> lock(mMutex);
-        mIsOpen = true;
-        mCondition.notify_all();
-    }
-
-    // Reset the Barrier, so wait() will block until open() has been called.
-    void close() {
-        std::lock_guard<std::mutex> lock(mMutex);
-        mIsOpen = false;
-    }
-
-    // Wait until the Barrier is OPEN.
-    // Provides acquire semantics: no subsequent loads or stores will occur
-    // until wait() returns.
-    void wait() const {
-        std::unique_lock<std::mutex> lock(mMutex);
-        mCondition.wait(lock, [this]() NO_THREAD_SAFETY_ANALYSIS { return mIsOpen; });
-    }
-private:
-    mutable std::mutex mMutex;
-    mutable std::condition_variable mCondition;
-    int mIsOpen GUARDED_BY(mMutex){false};
-};
-
-}; // namespace android
-
-#endif // ANDROID_BARRIER_H
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 5b28384..f0b0200 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -319,7 +319,7 @@
     return hasReadyFrame();
 }
 
-bool BufferLayer::onPostComposition(sp<const DisplayDevice> displayDevice,
+bool BufferLayer::onPostComposition(const DisplayDevice* display,
                                     const std::shared_ptr<FenceTime>& glDoneFence,
                                     const std::shared_ptr<FenceTime>& presentFence,
                                     const CompositorTiming& compositorTiming) {
@@ -342,7 +342,7 @@
     const int32_t layerId = getSequence();
     mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
 
-    const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+    const auto outputLayer = findOutputLayerForDisplay(display);
     if (outputLayer && outputLayer->requiresClientComposition()) {
         nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
         mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
@@ -359,13 +359,15 @@
         mFrameTracker.setFrameReadyTime(desiredPresentTime);
     }
 
-    const auto displayId = displayDevice->getId();
     if (presentFence->isValid()) {
         mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence);
         mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                            presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
         mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
-    } else if (displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
+    } else if (!display) {
+        // Do nothing.
+    } else if (const auto displayId = display->getId();
+               displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
         // The HWC doesn't support present fences, so use the refresh
         // timestamp instead.
         const nsecs_t actualPresentTime = mFlinger->getHwComposer().getRefreshTimestamp(*displayId);
@@ -600,14 +602,8 @@
     return true;
 }
 
-bool BufferLayer::needsFiltering(const sp<const DisplayDevice>& displayDevice) const {
-    // If we are not capturing based on the state of a known display device,
-    // just return false.
-    if (displayDevice == nullptr) {
-        return false;
-    }
-
-    const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+bool BufferLayer::needsFiltering(const DisplayDevice* display) const {
+    const auto outputLayer = findOutputLayerForDisplay(display);
     if (outputLayer == nullptr) {
         return false;
     }
@@ -621,15 +617,9 @@
             sourceCrop.getWidth() != displayFrame.getWidth();
 }
 
-bool BufferLayer::needsFilteringForScreenshots(const sp<const DisplayDevice>& displayDevice,
+bool BufferLayer::needsFilteringForScreenshots(const DisplayDevice* display,
                                                const ui::Transform& inverseParentTransform) const {
-    // If we are not capturing based on the state of a known display device,
-    // just return false.
-    if (displayDevice == nullptr) {
-        return false;
-    }
-
-    const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+    const auto outputLayer = findOutputLayerForDisplay(display);
     if (outputLayer == nullptr) {
         return false;
     }
@@ -637,7 +627,7 @@
     // We need filtering if the sourceCrop rectangle size does not match the
     // viewport rectangle size (not a 1:1 render)
     const auto& compositionState = outputLayer->getState();
-    const ui::Transform& displayTransform = displayDevice->getTransform();
+    const ui::Transform& displayTransform = display->getTransform();
     const ui::Transform inverseTransform = inverseParentTransform * displayTransform.inverse();
     // Undo the transformation of the displayFrame so that we're back into
     // layer-stack space.
@@ -843,6 +833,13 @@
     mDrawingState.inputInfo = tmpInputInfo;
 }
 
+void BufferLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
+    mTransformHint = getFixedTransformHint();
+    if (mTransformHint == ui::Transform::ROT_INVALID) {
+        mTransformHint = displayTransformHint;
+    }
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 56bab1b..97ffe6f 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -79,10 +79,9 @@
 
     bool isHdrY410() const override;
 
-    bool onPostComposition(sp<const DisplayDevice> displayDevice,
-                           const std::shared_ptr<FenceTime>& glDoneFence,
+    bool onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& glDoneFence,
                            const std::shared_ptr<FenceTime>& presentFence,
-                           const CompositorTiming& compositorTiming) override;
+                           const CompositorTiming&) override;
 
     // latchBuffer - called each time the screen is redrawn and returns whether
     // the visible regions need to be recomputed (this is a fairly heavy
@@ -117,7 +116,7 @@
 
     sp<GraphicBuffer> getBuffer() const override;
 
-    // -----------------------------------------------------------------------
+    ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
 
     // -----------------------------------------------------------------------
     // Functions that must be implemented by derived classes
@@ -205,10 +204,16 @@
 
     virtual uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
 
+    void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
+
+    // Transform hint provided to the producer. This must be accessed holding
+    /// the mStateLock.
+    ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
+
 private:
     // Returns true if this layer requires filtering
-    bool needsFiltering(const sp<const DisplayDevice>& displayDevice) const override;
-    bool needsFilteringForScreenshots(const sp<const DisplayDevice>& displayDevice,
+    bool needsFiltering(const DisplayDevice*) const override;
+    bool needsFilteringForScreenshots(const DisplayDevice*,
                                       const ui::Transform& inverseParentTransform) const override;
 
     // BufferStateLayers can return Rect::INVALID_RECT if the layer does not have a display frame
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index e50a909..648d129 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -407,8 +407,17 @@
 }
 
 Rect BufferLayerConsumer::getCurrentCropLocked() const {
+    uint32_t width = mDefaultWidth;
+    uint32_t height = mDefaultHeight;
+    // If the buffer comes with a rotated bit for 90 (or 270) degrees, switch width/height in order
+    // to scale and crop correctly.
+    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+        width = mDefaultHeight;
+        height = mDefaultWidth;
+    }
+
     return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
-            ? GLConsumer::scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight)
+            ? GLConsumer::scaleDownCrop(mCurrentCrop, width, height)
             : mCurrentCrop;
 }
 
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index f4e630e..e5b94e4 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -58,8 +58,9 @@
     }
 }
 
-void BufferQueueLayer::setTransformHint(uint32_t orientation) const {
-    mConsumer->setTransformHint(orientation);
+void BufferQueueLayer::setTransformHint(ui::Transform::RotationFlags displayTransformHint) {
+    BufferLayer::setTransformHint(displayTransformHint);
+    mConsumer->setTransformHint(mTransformHint);
 }
 
 std::vector<OccupancyTracker::Segment> BufferQueueLayer::getOccupancyHistory(bool forceFlush) {
@@ -493,10 +494,6 @@
     if (!mFlinger->isLayerTripleBufferingDisabled()) {
         mProducer->setMaxDequeuedBufferCount(2);
     }
-
-    if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) {
-        updateTransformHint(display);
-    }
 }
 
 status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format) {
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 16b4b6e..5ebc22d 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -43,8 +43,6 @@
 
     void onLayerDisplayed(const sp<Fence>& releaseFence) override;
 
-    void setTransformHint(uint32_t orientation) const override;
-
     std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override;
 
     // If a buffer was replaced this frame, release the former buffer
@@ -72,6 +70,7 @@
     bool getSidebandStreamChanged() const override;
 
     bool latchSidebandStream(bool& recomputeVisibleRegions) override;
+    void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
 
     bool hasFrameUpdate() const override;
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index a121ce0..a1ed6d7 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -51,9 +51,6 @@
       : BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) {
     mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
     mCurrentState.dataspace = ui::Dataspace::V0_SRGB;
-    if (const auto display = args.displayDevice) {
-        updateTransformHint(display);
-    }
 }
 
 BufferStateLayer::~BufferStateLayer() {
@@ -108,10 +105,6 @@
     }
 }
 
-void BufferStateLayer::setTransformHint(uint32_t orientation) const {
-    mTransformHint = orientation;
-}
-
 void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
     for (const auto& handle : mDrawingState.callbackHandles) {
         handle->transformHint = mTransformHint;
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 5873a73..00fa7f7 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -42,7 +42,6 @@
     const char* getType() const override { return "BufferStateLayer"; }
 
     void onLayerDisplayed(const sp<Fence>& releaseFence) override;
-    void setTransformHint(uint32_t orientation) const override;
     void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
 
     void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
@@ -68,7 +67,6 @@
     }
     Rect getCrop(const Layer::State& s) const;
 
-    uint32_t getTransformHint() const { return mTransformHint; }
     bool setTransform(uint32_t transform) override;
     bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
     bool setCrop(const Rect& crop) override;
@@ -164,8 +162,6 @@
     bool mReleasePreviousBuffer = false;
     nsecs_t mCallbackHandleAcquireTime = -1;
 
-    mutable uint32_t mTransformHint = 0;
-
     // TODO(marissaw): support sticky transform for LEGACY camera mode
 
     class HwcSlotGenerator : public ClientCache::ErasedRecipient {
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index fb72ab8..78bbcba 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -93,11 +93,6 @@
                                          uint32_t* outTransformHint) {
     if (mFlinger->authenticateSurfaceTexture(parent) == false) {
         ALOGE("failed to authenticate surface texture");
-        // The extra parent layer check below before returning is to help with debugging
-        // b/134888387. Once the bug is fixed the check can be deleted.
-        if ((static_cast<MonitoredProducer*>(parent.get()))->getLayer() == nullptr) {
-            ALOGE("failed to find parent layer");
-        }
         return BAD_VALUE;
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index d3712d9..a0606b4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -24,6 +24,7 @@
 #include <compositionengine/LayerFE.h>
 #include <compositionengine/OutputColorSetting.h>
 #include <math/mat4.h>
+#include <ui/Transform.h>
 
 namespace android::compositionengine {
 
@@ -57,6 +58,9 @@
     // Forces a color mode on the outputs being refreshed
     ui::ColorMode forceOutputColorMode{ui::ColorMode::NATIVE};
 
+    // Used to correctly apply an inverse-display buffer transform if applicable
+    ui::Transform::RotationFlags internalDisplayRotationFlags{ui::Transform::ROT_0};
+
     // If true, GPU clocks will be increased when rendering blurs
     bool blursAreExpensive{false};
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index cf77738..aa70ef8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -19,6 +19,7 @@
 #include <optional>
 #include <string>
 
+#include <ui/Transform.h>
 #include <utils/StrongPointer.h>
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
@@ -77,7 +78,12 @@
 
     // Recalculates the state of the output layer from the output-independent
     // layer. If includeGeometry is false, the geometry state can be skipped.
-    virtual void updateCompositionState(bool includeGeometry, bool forceClientComposition) = 0;
+    // internalDisplayRotationFlags must be set to the rotation flags for the
+    // internal display, and is used to properly compute the inverse-display
+    // transform, if needed.
+    virtual void updateCompositionState(
+            bool includeGeometry, bool forceClientComposition,
+            ui::Transform::RotationFlags internalDisplayRotationFlags) = 0;
 
     // Writes the geometry state to the HWC, or does nothing if this layer does
     // not use the HWC. If includeGeometry is false, the geometry state can be
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 79df9b2..8cb5ae8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -39,7 +39,8 @@
 
     void setHwcLayer(std::shared_ptr<HWC2::Layer>) override;
 
-    void updateCompositionState(bool includeGeometry, bool forceClientComposition) override;
+    void updateCompositionState(bool includeGeometry, bool forceClientComposition,
+                                ui::Transform::RotationFlags) override;
     void writeStateToHWC(bool) override;
     void writeCursorPositionToHWC() const override;
 
@@ -55,7 +56,8 @@
 
     virtual FloatRect calculateOutputSourceCrop() const;
     virtual Rect calculateOutputDisplayFrame() const;
-    virtual uint32_t calculateOutputRelativeBufferTransform() const;
+    virtual uint32_t calculateOutputRelativeBufferTransform(
+            uint32_t internalDisplayRotationFlags) const;
 
 protected:
     // Implemented by the final implementation for the final state it uses.
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 2ecbad8..81e1fc7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -38,7 +38,7 @@
     MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&());
     MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
 
-    MOCK_METHOD2(updateCompositionState, void(bool, bool));
+    MOCK_METHOD3(updateCompositionState, void(bool, bool, ui::Transform::RotationFlags));
     MOCK_METHOD1(writeStateToHWC, void(bool));
     MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
 
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index b7ea089..b07c904 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -588,7 +588,8 @@
     for (auto* layer : getOutputLayersOrderedByZ()) {
         layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame,
                                       refreshArgs.devOptForceClientComposition ||
-                                              forceClientComposition);
+                                              forceClientComposition,
+                                      refreshArgs.internalDisplayRotationFlags);
 
         if (mLayerRequestingBackgroundBlur == layer) {
             forceClientComposition = false;
@@ -815,6 +816,43 @@
     OutputCompositionState& outputCompositionState = editState();
     const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
                                                       outputState.usesClientComposition};
+
+    auto& renderEngine = getCompositionEngine().getRenderEngine();
+    const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
+
+    // If we the display is secure, protected content support is enabled, and at
+    // least one layer has protected content, we need to use a secure back
+    // buffer.
+    if (outputState.isSecure && supportsProtectedContent) {
+        auto layers = getOutputLayersOrderedByZ();
+        bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) {
+            return layer->getLayerFE().getCompositionState()->hasProtectedContent;
+        });
+        if (needsProtected != renderEngine.isProtected()) {
+            renderEngine.useProtectedContext(needsProtected);
+        }
+        if (needsProtected != mRenderSurface->isProtected() &&
+            needsProtected == renderEngine.isProtected()) {
+            mRenderSurface->setProtected(needsProtected);
+        }
+    }
+
+    base::unique_fd fd;
+    sp<GraphicBuffer> buf;
+
+    // If we aren't doing client composition on this output, but do have a
+    // flipClientTarget request for this frame on this output, we still need to
+    // dequeue a buffer.
+    if (hasClientComposition || outputState.flipClientTarget) {
+        buf = mRenderSurface->dequeueBuffer(&fd);
+        if (buf == nullptr) {
+            ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
+                  "client composition for this frame",
+                  mName.c_str());
+            return {};
+        }
+    }
+
     base::unique_fd readyFence;
     if (!hasClientComposition) {
         setExpensiveRenderingExpected(false);
@@ -823,9 +861,6 @@
 
     ALOGV("hasClientComposition");
 
-    auto& renderEngine = getCompositionEngine().getRenderEngine();
-    const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
-
     renderengine::DisplaySettings clientCompositionDisplay;
     clientCompositionDisplay.physicalDisplay = outputState.destinationClip;
     clientCompositionDisplay.clip = outputState.sourceClip;
@@ -851,32 +886,6 @@
                                               clientCompositionDisplay.outputDataspace);
     appendRegionFlashRequests(debugRegion, clientCompositionLayers);
 
-    // If we the display is secure, protected content support is enabled, and at
-    // least one layer has protected content, we need to use a secure back
-    // buffer.
-    if (outputState.isSecure && supportsProtectedContent) {
-        auto layers = getOutputLayersOrderedByZ();
-        bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) {
-            return layer->getLayerFE().getCompositionState()->hasProtectedContent;
-        });
-        if (needsProtected != renderEngine.isProtected()) {
-            renderEngine.useProtectedContext(needsProtected);
-        }
-        if (needsProtected != mRenderSurface->isProtected() &&
-            needsProtected == renderEngine.isProtected()) {
-            mRenderSurface->setProtected(needsProtected);
-        }
-    }
-
-    base::unique_fd fd;
-    sp<GraphicBuffer> buf = mRenderSurface->dequeueBuffer(&fd);
-    if (buf == nullptr) {
-        ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
-              "client composition for this frame",
-              mName.c_str());
-        return std::nullopt;
-    }
-
     // Check if the client composition requests were rendered into the provided graphic buffer. If
     // so, we can reuse the buffer and avoid client composition.
     if (mClientCompositionRequestCache) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index c9a070d..81f2dd1 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -223,7 +223,8 @@
     return displayTransform.transform(frame);
 }
 
-uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const {
+uint32_t OutputLayer::calculateOutputRelativeBufferTransform(
+        uint32_t internalDisplayRotationFlags) const {
     const auto& layerState = *getLayerFE().getCompositionState();
     const auto& outputState = getOutput().getState();
 
@@ -241,10 +242,11 @@
 
     if (layerState.geomBufferUsesDisplayInverseTransform) {
         /*
-         * the code below applies the primary display's inverse transform to the
-         * buffer
+         * We must apply the internal display's inverse transform to the buffer
+         * transform, and not the one for the output this layer is on.
          */
-        uint32_t invTransform = outputState.orientation;
+        uint32_t invTransform = internalDisplayRotationFlags;
+
         // calculate the inverse transform
         if (invTransform & HAL_TRANSFORM_ROT_90) {
             invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
@@ -261,9 +263,11 @@
 
     // this gives us only the "orientation" component of the transform
     return transform.getOrientation();
-} // namespace impl
+}
 
-void OutputLayer::updateCompositionState(bool includeGeometry, bool forceClientComposition) {
+void OutputLayer::updateCompositionState(
+        bool includeGeometry, bool forceClientComposition,
+        ui::Transform::RotationFlags internalDisplayRotationFlags) {
     const auto* layerFEState = getLayerFE().getCompositionState();
     if (!layerFEState) {
         return;
@@ -283,8 +287,8 @@
 
         state.displayFrame = calculateOutputDisplayFrame();
         state.sourceCrop = calculateOutputSourceCrop();
-        state.bufferTransform =
-                static_cast<Hwc2::Transform>(calculateOutputRelativeBufferTransform());
+        state.bufferTransform = static_cast<Hwc2::Transform>(
+                calculateOutputRelativeBufferTransform(internalDisplayRotationFlags));
 
         if ((layerFEState->isSecure && !outputState.isSecure) ||
             (state.bufferTransform & ui::Transform::ROT_INVALID)) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 59889b6..62977a4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -30,6 +30,7 @@
 #include <compositionengine/mock/OutputLayer.h>
 #include <compositionengine/mock/RenderSurface.h>
 #include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
 #include <ui/DisplayInfo.h>
 #include <ui/Rect.h>
 
@@ -156,6 +157,8 @@
 
     DisplayTestCommon() {
         EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+        EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
+        EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
     }
 
     DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() {
@@ -182,6 +185,7 @@
 
     StrictMock<android::mock::HWComposer> mHwComposer;
     StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+    StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
     StrictMock<mock::CompositionEngine> mCompositionEngine;
     sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
 };
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 27266b7..75a4fec 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -74,7 +74,7 @@
     MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(DisplayId, bool, uint8_t, uint64_t));
     MOCK_METHOD4(getDisplayedContentSample,
                  status_t(DisplayId, uint64_t, uint64_t, DisplayedFrameStats*));
-    MOCK_METHOD2(setDisplayBrightness, status_t(DisplayId, float));
+    MOCK_METHOD2(setDisplayBrightness, std::future<status_t>(DisplayId, float));
     MOCK_METHOD2(getDisplayBrightnessSupport, status_t(DisplayId, bool*));
 
     MOCK_METHOD2(onHotplug,
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index bdacb22..020f93a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -361,7 +361,7 @@
         mOutputState.orientation = entry.display;
         mOutputState.transform = ui::Transform{entry.display};
 
-        auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
+        const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.display);
         EXPECT_EQ(entry.expected, actual) << "entry " << i;
     }
 }
@@ -371,56 +371,109 @@
     mLayerFEState.geomBufferUsesDisplayInverseTransform = true;
 
     struct Entry {
-        uint32_t layer;
+        uint32_t layer; /* shouldn't affect the result, so we just use arbitrary values */
         uint32_t buffer;
         uint32_t display;
+        uint32_t internal;
         uint32_t expected;
     };
-    // Not an exhaustive list of cases, but hopefully enough.
-    const std::array<Entry, 24> testData = {
+    const std::array<Entry, 64> testData = {
             // clang-format off
-            //             layer       buffer      display     expected
-            /*  0 */ Entry{TR_IDENT,   TR_IDENT,   TR_IDENT,   TR_IDENT},
-            /*  1 */ Entry{TR_IDENT,   TR_IDENT,   TR_ROT_90,  TR_IDENT},
-            /*  2 */ Entry{TR_IDENT,   TR_IDENT,   TR_ROT_180, TR_IDENT},
-            /*  3 */ Entry{TR_IDENT,   TR_IDENT,   TR_ROT_270, TR_IDENT},
+            //    layer       buffer      display     internal    expected
+            Entry{TR_IDENT,   TR_IDENT,   TR_IDENT,   TR_IDENT,   TR_IDENT},
+            Entry{TR_IDENT,   TR_IDENT,   TR_IDENT,   TR_ROT_90,  TR_ROT_270},
+            Entry{TR_IDENT,   TR_IDENT,   TR_IDENT,   TR_ROT_180, TR_ROT_180},
+            Entry{TR_IDENT,   TR_IDENT,   TR_IDENT,   TR_ROT_270, TR_ROT_90},
 
-            /*  4 */ Entry{TR_IDENT,   TR_FLP_H,   TR_IDENT,   TR_FLP_H},
-            /*  5 */ Entry{TR_IDENT,   TR_FLP_H,   TR_ROT_90,  TR_FLP_H},
-            /*  6 */ Entry{TR_IDENT,   TR_FLP_H,   TR_ROT_180, TR_FLP_H},
-            /*  7 */ Entry{TR_IDENT,   TR_FLP_H,   TR_ROT_270, TR_FLP_H},
+            Entry{TR_IDENT,   TR_IDENT,   TR_ROT_90,  TR_IDENT,   TR_ROT_90},
+            Entry{TR_ROT_90,  TR_IDENT,   TR_ROT_90,  TR_ROT_90,  TR_IDENT},
+            Entry{TR_ROT_180, TR_IDENT,   TR_ROT_90,  TR_ROT_180, TR_ROT_270},
+            Entry{TR_ROT_90,  TR_IDENT,   TR_ROT_90,  TR_ROT_270, TR_ROT_180},
 
-            /*  8 */ Entry{TR_IDENT,   TR_FLP_V,   TR_IDENT,   TR_FLP_V},
-            /*  9 */ Entry{TR_IDENT,   TR_ROT_90,  TR_ROT_90,  TR_ROT_90},
-            /* 10 */ Entry{TR_IDENT,   TR_ROT_180, TR_ROT_180, TR_ROT_180},
-            /* 11 */ Entry{TR_IDENT,   TR_ROT_270, TR_ROT_270, TR_ROT_270},
+            Entry{TR_ROT_180, TR_IDENT,   TR_ROT_180, TR_IDENT,   TR_ROT_180},
+            Entry{TR_ROT_90,  TR_IDENT,   TR_ROT_180, TR_ROT_90,  TR_ROT_90},
+            Entry{TR_ROT_180, TR_IDENT,   TR_ROT_180, TR_ROT_180, TR_IDENT},
+            Entry{TR_ROT_270, TR_IDENT,   TR_ROT_180, TR_ROT_270, TR_ROT_270},
 
-            /* 12 */ Entry{TR_ROT_90,  TR_IDENT,   TR_IDENT,   TR_IDENT},
-            /* 13 */ Entry{TR_ROT_90,  TR_FLP_H,   TR_ROT_90,  TR_FLP_H},
-            /* 14 */ Entry{TR_ROT_90,  TR_IDENT,   TR_ROT_180, TR_IDENT},
-            /* 15 */ Entry{TR_ROT_90,  TR_FLP_H,   TR_ROT_270, TR_FLP_H},
+            Entry{TR_ROT_270, TR_IDENT,   TR_ROT_270, TR_IDENT,   TR_ROT_270},
+            Entry{TR_ROT_270, TR_IDENT,   TR_ROT_270, TR_ROT_90,  TR_ROT_180},
+            Entry{TR_ROT_180, TR_IDENT,   TR_ROT_270, TR_ROT_180, TR_ROT_90},
+            Entry{TR_IDENT,   TR_IDENT,   TR_ROT_270, TR_ROT_270, TR_IDENT},
 
-            /* 16 */ Entry{TR_ROT_180, TR_FLP_H,   TR_IDENT,   TR_FLP_H},
-            /* 17 */ Entry{TR_ROT_180, TR_IDENT,   TR_ROT_90,  TR_IDENT},
-            /* 18 */ Entry{TR_ROT_180, TR_FLP_H,   TR_ROT_180, TR_FLP_H},
-            /* 19 */ Entry{TR_ROT_180, TR_IDENT,   TR_ROT_270, TR_IDENT},
+            //    layer       buffer      display     internal    expected
+            Entry{TR_IDENT,   TR_ROT_90,  TR_IDENT,   TR_IDENT,   TR_ROT_90},
+            Entry{TR_ROT_90,  TR_ROT_90,  TR_IDENT,   TR_ROT_90,  TR_IDENT},
+            Entry{TR_ROT_180, TR_ROT_90,  TR_IDENT,   TR_ROT_180, TR_ROT_270},
+            Entry{TR_ROT_270, TR_ROT_90,  TR_IDENT,   TR_ROT_270, TR_ROT_180},
 
-            /* 20 */ Entry{TR_ROT_270, TR_IDENT,   TR_IDENT,   TR_IDENT},
-            /* 21 */ Entry{TR_ROT_270, TR_FLP_H,   TR_ROT_90,  TR_FLP_H},
-            /* 22 */ Entry{TR_ROT_270, TR_FLP_H,   TR_ROT_180, TR_FLP_H},
-            /* 23 */ Entry{TR_ROT_270, TR_IDENT,   TR_ROT_270, TR_IDENT},
+            Entry{TR_ROT_90,  TR_ROT_90,  TR_ROT_90,  TR_IDENT,   TR_ROT_180},
+            Entry{TR_ROT_90,  TR_ROT_90,  TR_ROT_90,  TR_ROT_90,  TR_ROT_90},
+            Entry{TR_ROT_90,  TR_ROT_90,  TR_ROT_90,  TR_ROT_180, TR_IDENT},
+            Entry{TR_ROT_270, TR_ROT_90,  TR_ROT_90,  TR_ROT_270, TR_ROT_270},
+
+            Entry{TR_IDENT,   TR_ROT_90,  TR_ROT_180, TR_IDENT,   TR_ROT_270},
+            Entry{TR_ROT_90,  TR_ROT_90,  TR_ROT_180, TR_ROT_90,  TR_ROT_180},
+            Entry{TR_ROT_180, TR_ROT_90,  TR_ROT_180, TR_ROT_180, TR_ROT_90},
+            Entry{TR_ROT_90,  TR_ROT_90,  TR_ROT_180, TR_ROT_270, TR_IDENT},
+
+            Entry{TR_IDENT,   TR_ROT_90,  TR_ROT_270, TR_IDENT,   TR_IDENT},
+            Entry{TR_ROT_270, TR_ROT_90,  TR_ROT_270, TR_ROT_90,  TR_ROT_270},
+            Entry{TR_ROT_180, TR_ROT_90,  TR_ROT_270, TR_ROT_180, TR_ROT_180},
+            Entry{TR_ROT_270, TR_ROT_90,  TR_ROT_270, TR_ROT_270, TR_ROT_90},
+
+            //    layer       buffer      display     internal    expected
+            Entry{TR_IDENT,   TR_ROT_180, TR_IDENT,   TR_IDENT,   TR_ROT_180},
+            Entry{TR_IDENT,   TR_ROT_180, TR_IDENT,   TR_ROT_90,  TR_ROT_90},
+            Entry{TR_ROT_180, TR_ROT_180, TR_IDENT,   TR_ROT_180, TR_IDENT},
+            Entry{TR_ROT_270, TR_ROT_180, TR_IDENT,   TR_ROT_270, TR_ROT_270},
+
+            Entry{TR_IDENT,   TR_ROT_180, TR_ROT_90,  TR_IDENT,   TR_ROT_270},
+            Entry{TR_ROT_90,  TR_ROT_180, TR_ROT_90,  TR_ROT_90,  TR_ROT_180},
+            Entry{TR_ROT_180, TR_ROT_180, TR_ROT_90,  TR_ROT_180, TR_ROT_90},
+            Entry{TR_ROT_180, TR_ROT_180, TR_ROT_90,  TR_ROT_270, TR_IDENT},
+
+            Entry{TR_IDENT,   TR_ROT_180, TR_ROT_180, TR_IDENT,   TR_IDENT},
+            Entry{TR_ROT_180, TR_ROT_180, TR_ROT_180, TR_ROT_90,  TR_ROT_270},
+            Entry{TR_ROT_180, TR_ROT_180, TR_ROT_180, TR_ROT_180, TR_ROT_180},
+            Entry{TR_ROT_270, TR_ROT_180, TR_ROT_180, TR_ROT_270, TR_ROT_90},
+
+            Entry{TR_ROT_270, TR_ROT_180, TR_ROT_270, TR_IDENT,   TR_ROT_90},
+            Entry{TR_ROT_180, TR_ROT_180, TR_ROT_270, TR_ROT_90,  TR_IDENT},
+            Entry{TR_ROT_180, TR_ROT_180, TR_ROT_270, TR_ROT_180, TR_ROT_270},
+            Entry{TR_ROT_270, TR_ROT_180, TR_ROT_270, TR_ROT_270, TR_ROT_180},
+
+            //    layer       buffer      display     internal    expected
+            Entry{TR_IDENT,   TR_ROT_270, TR_IDENT,   TR_IDENT,   TR_ROT_270},
+            Entry{TR_ROT_90,  TR_ROT_270, TR_IDENT,   TR_ROT_90,  TR_ROT_180},
+            Entry{TR_ROT_270, TR_ROT_270, TR_IDENT,   TR_ROT_180, TR_ROT_90},
+            Entry{TR_IDENT,   TR_ROT_270, TR_IDENT,   TR_ROT_270, TR_IDENT},
+
+            Entry{TR_ROT_270, TR_ROT_270, TR_ROT_90,  TR_IDENT,   TR_IDENT},
+            Entry{TR_ROT_90,  TR_ROT_270, TR_ROT_90,  TR_ROT_90,  TR_ROT_270},
+            Entry{TR_ROT_180, TR_ROT_270, TR_ROT_90,  TR_ROT_180, TR_ROT_180},
+            Entry{TR_ROT_90,  TR_ROT_270, TR_ROT_90,  TR_ROT_270, TR_ROT_90},
+
+            Entry{TR_IDENT,   TR_ROT_270, TR_ROT_180, TR_IDENT,   TR_ROT_90},
+            Entry{TR_ROT_270, TR_ROT_270, TR_ROT_180, TR_ROT_90,  TR_IDENT},
+            Entry{TR_ROT_180, TR_ROT_270, TR_ROT_180, TR_ROT_180, TR_ROT_270},
+            Entry{TR_ROT_270, TR_ROT_270, TR_ROT_180, TR_ROT_270, TR_ROT_180},
+
+            Entry{TR_IDENT,   TR_ROT_270, TR_ROT_270, TR_IDENT,   TR_ROT_180},
+            Entry{TR_ROT_90,  TR_ROT_270, TR_ROT_270, TR_ROT_90,  TR_ROT_90},
+            Entry{TR_ROT_270, TR_ROT_270, TR_ROT_270, TR_ROT_180, TR_IDENT},
+            Entry{TR_ROT_270, TR_ROT_270, TR_ROT_270, TR_ROT_270, TR_ROT_270},
             // clang-format on
     };
 
     for (size_t i = 0; i < testData.size(); i++) {
         const auto& entry = testData[i];
 
-        mLayerFEState.geomLayerTransform = ui::Transform{entry.layer};
+        mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
         mLayerFEState.geomBufferTransform = entry.buffer;
         mOutputState.orientation = entry.display;
         mOutputState.transform = ui::Transform{entry.display};
 
-        auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
+        const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.internal);
         EXPECT_EQ(entry.expected, actual) << "entry " << i;
     }
 }
@@ -436,7 +489,7 @@
     // Mock everything called by updateCompositionState to simplify testing it.
     MOCK_CONST_METHOD0(calculateOutputSourceCrop, FloatRect());
     MOCK_CONST_METHOD0(calculateOutputDisplayFrame, Rect());
-    MOCK_CONST_METHOD0(calculateOutputRelativeBufferTransform, uint32_t());
+    MOCK_CONST_METHOD1(calculateOutputRelativeBufferTransform, uint32_t(uint32_t));
 
     // compositionengine::OutputLayer overrides
     const compositionengine::Output& getOutput() const override { return mOutput; }
@@ -463,10 +516,11 @@
 
     ~OutputLayerUpdateCompositionStateTest() = default;
 
-    void setupGeometryChildCallValues() {
+    void setupGeometryChildCallValues(ui::Transform::RotationFlags internalDisplayRotationFlags) {
         EXPECT_CALL(mOutputLayer, calculateOutputSourceCrop()).WillOnce(Return(kSourceCrop));
         EXPECT_CALL(mOutputLayer, calculateOutputDisplayFrame()).WillOnce(Return(kDisplayFrame));
-        EXPECT_CALL(mOutputLayer, calculateOutputRelativeBufferTransform())
+        EXPECT_CALL(mOutputLayer,
+                    calculateOutputRelativeBufferTransform(internalDisplayRotationFlags))
                 .WillOnce(Return(mBufferTransform));
     }
 
@@ -489,7 +543,7 @@
 TEST_F(OutputLayerUpdateCompositionStateTest, doesNothingIfNoFECompositionState) {
     EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
 
-    mOutputLayer.updateCompositionState(true, false);
+    mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_90);
 }
 
 TEST_F(OutputLayerUpdateCompositionStateTest, setsStateNormally) {
@@ -497,9 +551,9 @@
     mOutputState.isSecure = true;
     mOutputLayer.editState().forceClientComposition = true;
 
-    setupGeometryChildCallValues();
+    setupGeometryChildCallValues(ui::Transform::RotationFlags::ROT_90);
 
-    mOutputLayer.updateCompositionState(true, false);
+    mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_90);
 
     validateComputedGeometryState();
 
@@ -511,9 +565,9 @@
     mLayerFEState.isSecure = true;
     mOutputState.isSecure = false;
 
-    setupGeometryChildCallValues();
+    setupGeometryChildCallValues(ui::Transform::RotationFlags::ROT_0);
 
-    mOutputLayer.updateCompositionState(true, false);
+    mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_0);
 
     validateComputedGeometryState();
 
@@ -527,9 +581,9 @@
 
     mBufferTransform = ui::Transform::ROT_INVALID;
 
-    setupGeometryChildCallValues();
+    setupGeometryChildCallValues(ui::Transform::RotationFlags::ROT_0);
 
-    mOutputLayer.updateCompositionState(true, false);
+    mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_0);
 
     validateComputedGeometryState();
 
@@ -544,7 +598,7 @@
     // should use the layers requested colorspace.
     mLayerFEState.isColorspaceAgnostic = false;
 
-    mOutputLayer.updateCompositionState(false, false);
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
 
     EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutputLayer.getState().dataspace);
 
@@ -552,7 +606,7 @@
     // should use the colorspace chosen for the whole output.
     mLayerFEState.isColorspaceAgnostic = true;
 
-    mOutputLayer.updateCompositionState(false, false);
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
 
     EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
 }
@@ -560,7 +614,7 @@
 TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
     mOutputLayer.editState().forceClientComposition = false;
 
-    mOutputLayer.updateCompositionState(false, false);
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
 
     EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition);
 }
@@ -569,7 +623,7 @@
        doesNotClearForceClientCompositionIfNotDoingGeometry) {
     mOutputLayer.editState().forceClientComposition = true;
 
-    mOutputLayer.updateCompositionState(false, false);
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
 
     EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
 }
@@ -578,7 +632,7 @@
     mLayerFEState.forceClientComposition = true;
     mOutputLayer.editState().forceClientComposition = false;
 
-    mOutputLayer.updateCompositionState(false, false);
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
 
     EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
 }
@@ -588,7 +642,7 @@
     mOutputLayer.editState().forceClientComposition = false;
     EXPECT_CALL(mDisplayColorProfile, isDataspaceSupported(_)).WillRepeatedly(Return(false));
 
-    mOutputLayer.updateCompositionState(false, false);
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
 
     EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
 }
@@ -597,15 +651,15 @@
     mLayerFEState.forceClientComposition = false;
     mOutputLayer.editState().forceClientComposition = false;
 
-    mOutputLayer.updateCompositionState(false, true);
+    mOutputLayer.updateCompositionState(false, true, ui::Transform::RotationFlags::ROT_0);
 
     EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
 
     mOutputLayer.editState().forceClientComposition = false;
 
-    setupGeometryChildCallValues();
+    setupGeometryChildCallValues(ui::Transform::RotationFlags::ROT_0);
 
-    mOutputLayer.updateCompositionState(true, true);
+    mOutputLayer.updateCompositionState(true, true, ui::Transform::RotationFlags::ROT_0);
 
     EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
 }
@@ -810,7 +864,7 @@
     // geomBufferTransform is set to the inverse transform.
     mLayerFEState.geomBufferTransform = TR_ROT_270;
 
-    EXPECT_EQ(TR_IDENT, mOutputLayer.calculateOutputRelativeBufferTransform());
+    EXPECT_EQ(TR_IDENT, mOutputLayer.calculateOutputRelativeBufferTransform(ui::Transform::ROT_90));
 }
 
 TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSolidColor) {
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 1c9cd9c..59ed72e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -696,11 +696,11 @@
     InjectedLayer layer2;
     InjectedLayer layer3;
 
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
     EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
     EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
     EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
 
     injectOutputLayer(layer1);
@@ -712,6 +712,7 @@
     CompositionRefreshArgs args;
     args.updatingGeometryThisFrame = false;
     args.devOptForceClientComposition = false;
+    args.internalDisplayRotationFlags = ui::Transform::ROT_180;
     mOutput->updateAndWriteCompositionState(args);
 }
 
@@ -720,11 +721,11 @@
     InjectedLayer layer2;
     InjectedLayer layer3;
 
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(true));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(true));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(true));
 
     injectOutputLayer(layer1);
@@ -744,11 +745,11 @@
     InjectedLayer layer2;
     InjectedLayer layer3;
 
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
 
     injectOutputLayer(layer1);
@@ -2797,6 +2798,7 @@
         mOutput.mState.usesClientComposition = true;
         mOutput.mState.usesDeviceComposition = false;
         mOutput.mState.reusedClientComposition = false;
+        mOutput.mState.flipClientTarget = false;
 
         EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
         EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
@@ -2868,19 +2870,40 @@
 TEST_F(OutputComposeSurfacesTest, doesNothingButSignalNoExpensiveRenderingIfNoClientComposition) {
     mOutput.mState.usesClientComposition = false;
 
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+
     EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
 
     verify().execute().expectAFenceWasReturned();
 }
 
-TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFails) {
-    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
-    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+TEST_F(OutputComposeSurfacesTest,
+       dequeuesABufferIfNoClientCompositionButFlipClientTargetRequested) {
+    mOutput.mState.usesClientComposition = false;
+    mOutput.mState.flipClientTarget = true;
+
     EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
-            .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
-    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
-            .WillRepeatedly(Return());
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer));
+    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
+
+    verify().execute().expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFailsForClientComposition) {
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
+
+    verify().execute().expectNoFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest,
+       doesMinimalWorkIfDequeueBufferFailsForNoClientCompositionButFlipClientTargetRequested) {
+    mOutput.mState.usesClientComposition = false;
+    mOutput.mState.flipClientTarget = true;
+
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
 
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
 
@@ -3315,7 +3338,7 @@
         mLayer.layerFEState.backgroundBlurRadius = 10;
         mOutput.editState().isEnabled = true;
 
-        EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true));
+        EXPECT_CALL(mLayer.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
         EXPECT_CALL(mLayer.outputLayer, writeStateToHWC(false));
         EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
                 .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
@@ -3894,11 +3917,11 @@
     InjectedLayer layer3;
 
     // Layer requesting blur, or below, should request client composition.
-    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true));
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
-    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
     EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
-    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
     EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
 
     layer2.layerFEState.backgroundBlurRadius = 10;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 9af9cad..2cebecb 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -116,6 +116,10 @@
     }
 }
 
+void DisplayDevice::setDeviceProductInfo(std::optional<DeviceProductInfo> info) {
+    mDeviceProductInfo = std::move(info);
+}
+
 uint32_t DisplayDevice::getPageFlipCount() const {
     return mCompositionDisplay->getRenderSurface()->getPageFlipCount();
 }
@@ -267,6 +271,12 @@
     StringAppendF(&result, "powerMode=%s (%d), ", to_string(mPowerMode).c_str(),
                   static_cast<int32_t>(mPowerMode));
     StringAppendF(&result, "activeConfig=%d, ", mActiveConfig.value());
+    StringAppendF(&result, "deviceProductInfo=");
+    if (mDeviceProductInfo) {
+        mDeviceProductInfo->dump(result);
+    } else {
+        result.append("{}");
+    }
     getCompositionDisplay()->dump(result);
 }
 
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 8c86153..4dabd2b 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -93,6 +93,10 @@
 
     static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags();
 
+    ui::Transform::RotationFlags getTransformHint() const {
+        return static_cast<ui::Transform::RotationFlags>(getTransform().getOrientation());
+    }
+
     const ui::Transform& getTransform() const;
     const Rect& getViewport() const;
     const Rect& getFrame() const;
@@ -132,6 +136,11 @@
     void setDisplayName(const std::string& displayName);
     const std::string& getDisplayName() const { return mDisplayName; }
 
+    void setDeviceProductInfo(std::optional<DeviceProductInfo> info);
+    const std::optional<DeviceProductInfo>& getDeviceProductInfo() const {
+        return mDeviceProductInfo;
+    }
+
     /* ------------------------------------------------------------------------
      * Display power mode management.
      */
@@ -178,6 +187,8 @@
 
     // TODO(b/74619554): Remove special cases for primary display.
     const bool mIsPrimary;
+
+    std::optional<DeviceProductInfo> mDeviceProductInfo;
 };
 
 struct DisplayDeviceState {
@@ -185,7 +196,7 @@
         DisplayId id;
         DisplayConnectionType type;
         hardware::graphics::composer::hal::HWDisplayId hwcDisplayId;
-
+        std::optional<DeviceProductInfo> deviceProductInfo;
         bool operator==(const Physical& other) const {
             return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId;
         }
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index d6dbd57..fb82033 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -25,7 +25,6 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "HWC2.h"
-#include "ComposerHal.h"
 
 #include <ui/Fence.h>
 #include <ui/FloatRect.h>
@@ -38,6 +37,9 @@
 #include <iterator>
 #include <set>
 
+#include "../Promise.h"
+#include "ComposerHal.h"
+
 namespace android {
 
 using android::Fence;
@@ -347,7 +349,7 @@
 
 Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests,
                            std::unordered_map<HWC2::Layer*, LayerRequest>* outLayerRequests) {
-    uint32_t intDisplayRequests;
+    uint32_t intDisplayRequests = 0;
     std::vector<Hwc2::Layer> layerIds;
     std::vector<uint32_t> layerRequests;
     auto intError = mComposer.getDisplayRequests(
@@ -640,12 +642,14 @@
     return error;
 }
 
-Error Display::setDisplayBrightness(float brightness) const {
-    const auto intError = mComposer.setDisplayBrightness(mId, brightness);
-    return static_cast<Error>(intError);
+std::future<Error> Display::setDisplayBrightness(float brightness) {
+    return promise::defer([composer = &mComposer, id = mId, brightness] {
+        const auto intError = composer->setDisplayBrightness(id, brightness);
+        return static_cast<Error>(intError);
+    });
 }
 
-Error Display::setAutoLowLatencyMode(bool on) const {
+Error Display::setAutoLowLatencyMode(bool on) {
     auto intError = mComposer.setAutoLowLatencyMode(mId, on);
     return static_cast<Error>(intError);
 }
@@ -659,7 +663,7 @@
     return static_cast<Error>(intError);
 }
 
-Error Display::setContentType(ContentType contentType) const {
+Error Display::setContentType(ContentType contentType) {
     auto intError = mComposer.setContentType(mId, contentType);
     return static_cast<Error>(intError);
 }
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index af31df8..f4c7fdd 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -27,6 +27,7 @@
 #include <utils/Timers.h>
 
 #include <functional>
+#include <future>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
@@ -221,18 +222,18 @@
     [[clang::warn_unused_result]] virtual hal::Error presentOrValidate(
             uint32_t* outNumTypes, uint32_t* outNumRequests,
             android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0;
-    [[clang::warn_unused_result]] virtual hal::Error setDisplayBrightness(
-            float brightness) const = 0;
+    [[clang::warn_unused_result]] virtual std::future<hal::Error> setDisplayBrightness(
+            float brightness) = 0;
     [[clang::warn_unused_result]] virtual hal::Error getDisplayVsyncPeriod(
             nsecs_t* outVsyncPeriod) const = 0;
     [[clang::warn_unused_result]] virtual hal::Error setActiveConfigWithConstraints(
             const std::shared_ptr<const HWC2::Display::Config>& config,
             const hal::VsyncPeriodChangeConstraints& constraints,
             hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
-    [[clang::warn_unused_result]] virtual hal::Error setAutoLowLatencyMode(bool on) const = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setAutoLowLatencyMode(bool on) = 0;
     [[clang::warn_unused_result]] virtual hal::Error getSupportedContentTypes(
             std::vector<hal::ContentType>*) const = 0;
-    [[clang::warn_unused_result]] virtual hal::Error setContentType(hal::ContentType) const = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setContentType(hal::ContentType) = 0;
 };
 
 namespace impl {
@@ -294,16 +295,16 @@
     hal::Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
                                  android::sp<android::Fence>* outPresentFence,
                                  uint32_t* state) override;
-    hal::Error setDisplayBrightness(float brightness) const override;
+    std::future<hal::Error> setDisplayBrightness(float brightness) override;
     hal::Error getDisplayVsyncPeriod(nsecs_t* outVsyncPeriod) const override;
     hal::Error setActiveConfigWithConstraints(
             const std::shared_ptr<const HWC2::Display::Config>& config,
             const hal::VsyncPeriodChangeConstraints& constraints,
             hal::VsyncPeriodChangeTimeline* outTimeline) override;
-    hal::Error setAutoLowLatencyMode(bool on) const override;
+    hal::Error setAutoLowLatencyMode(bool on) override;
     hal::Error getSupportedContentTypes(
             std::vector<hal::ContentType>* outSupportedContentTypes) const override;
-    hal::Error setContentType(hal::ContentType contentType) const override;
+    hal::Error setContentType(hal::ContentType) override;
     // Other Display methods
     hal::HWDisplayId getId() const override { return mId; }
     bool isConnected() const override { return mIsConnected; }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 4344a8d..038cec4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -36,6 +36,7 @@
 #include <utils/Trace.h>
 
 #include "../Layer.h" // needed only for debugging
+#include "../Promise.h"
 #include "../SurfaceFlinger.h"
 #include "ComposerHal.h"
 #include "HWC2.h"
@@ -794,17 +795,21 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setDisplayBrightness(DisplayId displayId, float brightness) {
-    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
-    const auto error = mDisplayData[displayId].hwcDisplay->setDisplayBrightness(brightness);
-    if (error == hal::Error::UNSUPPORTED) {
-        RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
-    }
-    if (error == hal::Error::BAD_PARAMETER) {
-        RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
-    }
-    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
-    return NO_ERROR;
+std::future<status_t> HWComposer::setDisplayBrightness(DisplayId displayId, float brightness) {
+    RETURN_IF_INVALID_DISPLAY(displayId, promise::yield<status_t>(BAD_INDEX));
+    auto& display = mDisplayData[displayId].hwcDisplay;
+
+    return promise::chain(display->setDisplayBrightness(brightness))
+            .then([displayId](hal::Error error) -> status_t {
+                if (error == hal::Error::UNSUPPORTED) {
+                    RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+                }
+                if (error == hal::Error::BAD_PARAMETER) {
+                    RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+                }
+                RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+                return NO_ERROR;
+            });
 }
 
 bool HWComposer::isUsingVrComposer() const {
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index cfa2193..b7e9f3a 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -18,6 +18,7 @@
 #define ANDROID_SF_HWCOMPOSER_H
 
 #include <cstdint>
+#include <future>
 #include <memory>
 #include <mutex>
 #include <optional>
@@ -163,7 +164,7 @@
                                                DisplayedFrameStats* outStats) = 0;
 
     // Sets the brightness of a display.
-    virtual status_t setDisplayBrightness(DisplayId displayId, float brightness) = 0;
+    virtual std::future<status_t> setDisplayBrightness(DisplayId displayId, float brightness) = 0;
 
     // Events handling ---------------------------------------------------------
 
@@ -305,7 +306,7 @@
                                               uint8_t componentMask, uint64_t maxFrames) override;
     status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames, uint64_t timestamp,
                                        DisplayedFrameStats* outStats) override;
-    status_t setDisplayBrightness(DisplayId displayId, float brightness) override;
+    std::future<status_t> setDisplayBrightness(DisplayId displayId, float brightness) override;
 
     // Events handling ---------------------------------------------------------
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 8452957..a19b5cb 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -117,6 +117,7 @@
     mCurrentState.metadata = args.metadata;
     mCurrentState.shadowRadius = 0.f;
     mCurrentState.treeHasFrameRateVote = false;
+    mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
 
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
@@ -144,11 +145,10 @@
     mFlinger->onLayerDestroyed(this);
 }
 
-LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client> client,
-                                     std::string name, uint32_t w, uint32_t h, uint32_t flags,
-                                     LayerMetadata metadata)
+LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
+                                     uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata)
       : flinger(flinger),
-        client(client),
+        client(std::move(client)),
         name(std::move(name)),
         w(w),
         h(h),
@@ -717,9 +717,8 @@
     return {*shadowSettings};
 }
 
-Hwc2::IComposerClient::Composition Layer::getCompositionType(
-        const sp<const DisplayDevice>& display) const {
-    const auto outputLayer = findOutputLayerForDisplay(display);
+Hwc2::IComposerClient::Composition Layer::getCompositionType(const DisplayDevice& display) const {
+    const auto outputLayer = findOutputLayerForDisplay(&display);
     if (outputLayer == nullptr) {
         return Hwc2::IComposerClient::Composition::INVALID;
     }
@@ -730,12 +729,6 @@
     }
 }
 
-bool Layer::getClearClientTarget(const sp<const DisplayDevice>& display) const {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    return outputLayer->getState().clearClientTarget;
-}
-
 bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) {
     if (point->getFrameNumber() <= mCurrentFrameNumber) {
         // Don't bother with a SyncPoint, since we've already latched the
@@ -1333,6 +1326,18 @@
     return true;
 }
 
+bool Layer::setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint) {
+    if (mCurrentState.fixedTransformHint == fixedTransformHint) {
+        return false;
+    }
+
+    mCurrentState.sequence++;
+    mCurrentState.fixedTransformHint = fixedTransformHint;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 void Layer::updateTreeHasFrameRateVote() {
     const auto traverseTree = [&](const LayerVector::Visitor& visitor) {
         auto parent = getParent();
@@ -1459,20 +1464,12 @@
     return usage;
 }
 
-void Layer::updateTransformHint(const sp<const DisplayDevice>& display) const {
-    uint32_t orientation = 0;
-    // Disable setting transform hint if the debug flag is set.
-    if (!mFlinger->mDebugDisableTransformHint) {
-        // The transform hint is used to improve performance, but we can
-        // only have a single transform hint, it cannot
-        // apply to all displays.
-        const ui::Transform& planeTransform = display->getTransform();
-        orientation = planeTransform.getOrientation();
-        if (orientation & ui::Transform::ROT_INVALID) {
-            orientation = 0;
-        }
+void Layer::updateTransformHint(ui::Transform::RotationFlags transformHint) {
+    if (mFlinger->mDebugDisableTransformHint || transformHint & ui::Transform::ROT_INVALID) {
+        transformHint = ui::Transform::ROT_0;
     }
-    setTransformHint(orientation);
+
+    setTransformHint(transformHint);
 }
 
 // ----------------------------------------------------------------------------
@@ -1480,7 +1477,7 @@
 // ----------------------------------------------------------------------------
 
 // TODO(marissaw): add new layer state info to layer debugging
-LayerDebugInfo Layer::getLayerDebugInfo() const {
+LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const {
     using namespace std::string_literals;
 
     LayerDebugInfo info;
@@ -1491,7 +1488,7 @@
     info.mType = getType();
     info.mTransparentRegion = ds.activeTransparentRegion_legacy;
 
-    info.mVisibleRegion = debugGetVisibleRegionOnDefaultDisplay();
+    info.mVisibleRegion = getVisibleRegion(display);
     info.mSurfaceDamageRegion = surfaceDamageRegion;
     info.mLayerStack = getLayerStack();
     info.mX = ds.active_legacy.transform.tx();
@@ -1532,21 +1529,37 @@
 void Layer::miniDumpHeader(std::string& result) {
     result.append("-------------------------------");
     result.append("-------------------------------");
-    result.append("-----------------------------\n");
+    result.append("-------------------------------");
+    result.append("-------------------------------");
+    result.append("---------\n");
     result.append(" Layer name\n");
     result.append("           Z | ");
     result.append(" Window Type | ");
     result.append(" Comp Type | ");
     result.append(" Transform | ");
     result.append("  Disp Frame (LTRB) | ");
-    result.append("         Source Crop (LTRB)\n");
+    result.append("         Source Crop (LTRB) | ");
+    result.append("    Frame Rate (Explicit)\n");
     result.append("-------------------------------");
     result.append("-------------------------------");
-    result.append("-----------------------------\n");
+    result.append("-------------------------------");
+    result.append("-------------------------------");
+    result.append("---------\n");
 }
 
-void Layer::miniDump(std::string& result, const sp<DisplayDevice>& displayDevice) const {
-    auto outputLayer = findOutputLayerForDisplay(displayDevice);
+std::string Layer::frameRateCompatibilityString(Layer::FrameRateCompatibility compatibility) {
+    switch (compatibility) {
+        case FrameRateCompatibility::Default:
+            return "Default";
+        case FrameRateCompatibility::ExactOrMultiple:
+            return "ExactOrMultiple";
+        case FrameRateCompatibility::NoVote:
+            return "NoVote";
+    }
+}
+
+void Layer::miniDump(std::string& result, const DisplayDevice& display) const {
+    const auto outputLayer = findOutputLayerForDisplay(&display);
     if (!outputLayer) {
         return;
     }
@@ -1573,17 +1586,26 @@
         StringAppendF(&result, "  %10d | ", layerState.z);
     }
     StringAppendF(&result, "  %10d | ", mWindowType);
-    StringAppendF(&result, "%10s | ", toString(getCompositionType(displayDevice)).c_str());
+    StringAppendF(&result, "%10s | ", toString(getCompositionType(display)).c_str());
     StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str());
     const Rect& frame = outputLayerState.displayFrame;
     StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
     const FloatRect& crop = outputLayerState.sourceCrop;
-    StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top, crop.right,
+    StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right,
                   crop.bottom);
+    if (layerState.frameRate.rate != 0 ||
+        layerState.frameRate.type != FrameRateCompatibility::Default) {
+        StringAppendF(&result, "% 6.2ffps %15s\n", layerState.frameRate.rate,
+                      frameRateCompatibilityString(layerState.frameRate.type).c_str());
+    } else {
+        StringAppendF(&result, "\n");
+    }
 
-    result.append("- - - - - - - - - - - - - - - -");
-    result.append("- - - - - - - - - - - - - - - -");
-    result.append("- - - - - - - - - - - - - - -\n");
+    result.append("- - - - - - - - - - - - - - - - ");
+    result.append("- - - - - - - - - - - - - - - - ");
+    result.append("- - - - - - - - - - - - - - - - ");
+    result.append("- - - - - - - - - - - - - - - - ");
+    result.append("- - -\n");
 }
 
 void Layer::dumpFrameStats(std::string& result) const {
@@ -2051,6 +2073,16 @@
     return parentAlpha * getDrawingState().color.a;
 }
 
+ui::Transform::RotationFlags Layer::getFixedTransformHint() const {
+    ui::Transform::RotationFlags fixedTransformHint = mCurrentState.fixedTransformHint;
+    if (fixedTransformHint != ui::Transform::ROT_INVALID) {
+        return fixedTransformHint;
+    }
+    const auto& p = mCurrentParent.promote();
+    if (!p) return fixedTransformHint;
+    return p->getFixedTransformHint();
+}
+
 half4 Layer::getColor() const {
     const half4 color(getDrawingState().color);
     return half4(color.r, color.g, color.b, getAlpha());
@@ -2127,27 +2159,28 @@
 }
 
 LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags,
-                                const sp<const DisplayDevice>& device) const {
+                                const DisplayDevice* display) const {
     LayerProto* layerProto = layersProto.add_layers();
-    writeToProtoDrawingState(layerProto, traceFlags);
+    writeToProtoDrawingState(layerProto, traceFlags, display);
     writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
 
     if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
         // Only populate for the primary display.
-        if (device) {
-            const Hwc2::IComposerClient::Composition compositionType = getCompositionType(device);
+        if (display) {
+            const Hwc2::IComposerClient::Composition compositionType = getCompositionType(*display);
             layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
         }
     }
 
     for (const sp<Layer>& layer : mDrawingChildren) {
-        layer->writeToProto(layersProto, traceFlags, device);
+        layer->writeToProto(layersProto, traceFlags, display);
     }
 
     return layerProto;
 }
 
-void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags) const {
+void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
+                                     const DisplayDevice* display) const {
     ui::Transform transform = getTransform();
 
     if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
@@ -2176,12 +2209,13 @@
         layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
 
         layerInfo->set_corner_radius(getRoundedCornerState().radius);
+        layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
         LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform());
         LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
                                                [&]() { return layerInfo->mutable_position(); });
         LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
         if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
-            LayerProtoHelper::writeToProto(debugGetVisibleRegionOnDefaultDisplay(),
+            LayerProtoHelper::writeToProto(getVisibleRegion(display),
                                            [&]() { return layerInfo->mutable_visible_region(); });
         }
         LayerProtoHelper::writeToProto(surfaceDamageRegion,
@@ -2380,22 +2414,14 @@
 }
 
 compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
-        const sp<const DisplayDevice>& display) const {
+        const DisplayDevice* display) const {
+    if (!display) return nullptr;
     return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionEngineLayerFE());
 }
 
-Region Layer::debugGetVisibleRegionOnDefaultDisplay() const {
-    sp<DisplayDevice> displayDevice = mFlinger->getDefaultDisplayDeviceLocked();
-    if (displayDevice == nullptr) {
-        return {};
-    }
-
-    auto outputLayer = findOutputLayerForDisplay(displayDevice);
-    if (outputLayer == nullptr) {
-        return {};
-    }
-
-    return outputLayer->getState().visibleRegion;
+Region Layer::getVisibleRegion(const DisplayDevice* display) const {
+    const auto outputLayer = findOutputLayerForDisplay(display);
+    return outputLayer ? outputLayer->getState().visibleRegion : Region();
 }
 
 void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 6636b5e..3fa935f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_LAYER_H
-#define ANDROID_LAYER_H
+#pragma once
 
 #include <compositionengine/LayerFE.h>
 #include <gui/BufferQueue.h>
@@ -77,8 +76,8 @@
 // ---------------------------------------------------------------------------
 
 struct LayerCreationArgs {
-    LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client> client, std::string name,
-                      uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata);
+    LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h,
+                      uint32_t flags, LayerMetadata);
 
     SurfaceFlinger* flinger;
     const sp<Client> client;
@@ -87,9 +86,9 @@
     uint32_t h;
     uint32_t flags;
     LayerMetadata metadata;
+
     pid_t callingPid;
     uid_t callingUid;
-    sp<const DisplayDevice> displayDevice;
     uint32_t textureName;
 };
 
@@ -262,6 +261,15 @@
 
         // Indicates whether parents / children of this layer had set FrameRate
         bool treeHasFrameRateVote;
+
+        // Set by window manager indicating the layer and all its children are
+        // in a different orientation than the display. The hint suggests that
+        // the graphic producers should receive a transform hint as if the
+        // display was in this orientation. When the display changes to match
+        // the layer orientation, the graphic producer may not need to allocate
+        // a buffer of a different size. ui::Transform::ROT_INVALID means the
+        // a fixed transform hint is not set.
+        ui::Transform::RotationFlags fixedTransformHint;
     };
 
     explicit Layer(const LayerCreationArgs& args);
@@ -388,6 +396,7 @@
     virtual bool setColorSpaceAgnostic(const bool agnostic);
     bool setShadowRadius(float shadowRadius);
     virtual bool setFrameRateSelectionPriority(int32_t priority);
+    virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
     //  If the variable is not set on the layer, it traverses up the tree to inherit the frame
     //  rate priority from its parent.
     virtual int32_t getFrameRateSelectionPriority() const;
@@ -509,15 +518,14 @@
 
     bool isRemovedFromCurrentState() const;
 
-    LayerProto* writeToProto(LayersProto& layersProto,
-                             uint32_t traceFlags = SurfaceTracing::TRACE_ALL,
-                             const sp<const DisplayDevice>& device = nullptr) const;
+    LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags,
+                             const DisplayDevice*) const;
 
     // Write states that are modified by the main thread. This includes drawing
     // state as well as buffer data. This should be called in the main or tracing
     // thread.
-    void writeToProtoDrawingState(LayerProto* layerInfo,
-                                  uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+    void writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
+                                  const DisplayDevice*) const;
     // Write drawing or current state. If writing current state, the caller should hold the
     // external mStateLock. If writing drawing state, this function should be called on the
     // main or tracing thread.
@@ -534,7 +542,7 @@
         return s.activeTransparentRegion_legacy;
     }
     virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
-    virtual bool needsFiltering(const sp<const DisplayDevice>&) const { return false; }
+    virtual bool needsFiltering(const DisplayDevice*) const { return false; }
     // True if this layer requires filtering
     // This method is distinct from needsFiltering() in how the filter
     // requirement is computed. needsFiltering() compares displayFrame and crop,
@@ -544,8 +552,7 @@
     // different.
     // If the parent transform needs to be undone when capturing the layer, then
     // the inverse parent transform is also required.
-    virtual bool needsFilteringForScreenshots(const sp<const DisplayDevice>&,
-                                              const ui::Transform&) const {
+    virtual bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const {
         return false;
     }
 
@@ -606,21 +613,16 @@
 
     virtual bool isHdrY410() const { return false; }
 
-    Hwc2::IComposerClient::Composition getCompositionType(
-            const sp<const DisplayDevice>& display) const;
-    bool getClearClientTarget(const sp<const DisplayDevice>& display) const;
-
     virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
-    virtual void setTransformHint(uint32_t /*orientation*/) const { }
 
     /*
      * called after composition.
      * returns true if the layer latched a new buffer this frame.
      */
-    virtual bool onPostComposition(sp<const DisplayDevice> /*displayDevice*/,
+    virtual bool onPostComposition(const DisplayDevice*,
                                    const std::shared_ptr<FenceTime>& /*glDoneFence*/,
                                    const std::shared_ptr<FenceTime>& /*presentFence*/,
-                                   const CompositorTiming& /*compositorTiming*/) {
+                                   const CompositorTiming&) {
         return false;
     }
 
@@ -672,9 +674,10 @@
      */
     void addToCurrentState();
 
-    // Updates the transform hint in our SurfaceFlingerConsumer to match
-    // the current orientation of the display device.
-    void updateTransformHint(const sp<const DisplayDevice>& display) const;
+    /*
+     * Sets display transform hint on BufferLayerConsumer.
+     */
+    void updateTransformHint(ui::Transform::RotationFlags);
 
     /*
      * returns the rectangle that crops the content of the layer and scales it
@@ -689,6 +692,8 @@
 
     virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
 
+    virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; }
+
     /*
      * Returns if a frame is ready
      */
@@ -701,11 +706,10 @@
     inline const State& getCurrentState() const { return mCurrentState; }
     inline State& getCurrentState() { return mCurrentState; }
 
-    LayerDebugInfo getLayerDebugInfo() const;
+    LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const;
 
-    /* always call base class first */
     static void miniDumpHeader(std::string& result);
-    void miniDump(std::string& result, const sp<DisplayDevice>& display) const;
+    void miniDump(std::string& result, const DisplayDevice&) const;
     void dumpFrameStats(std::string& result) const;
     void dumpFrameEvents(std::string& result);
     void dumpCallingUidPid(std::string& result) const;
@@ -733,6 +737,12 @@
     int32_t getBackgroundBlurRadius() const;
     bool drawShadows() const { return mEffectiveShadowRadius > 0.f; };
 
+    // Returns the transform hint set by Window Manager on the layer or one of its parents.
+    // This traverses the current state because the data is needed when creating
+    // the layer(off drawing thread) and the hint should be available before the producer
+    // is ready to acquire a buffer.
+    ui::Transform::RotationFlags getFixedTransformHint() const;
+
     // Returns how rounded corners should be drawn for this layer.
     // This will traverse the hierarchy until it reaches its root, finding topmost rounded
     // corner definition and converting it into current layer's coordinates.
@@ -804,11 +814,6 @@
         return parentBounds;
     }
 
-    compositionengine::OutputLayer* findOutputLayerForDisplay(
-            const sp<const DisplayDevice>& display) const;
-
-    Region debugGetVisibleRegionOnDefaultDisplay() const;
-
     /**
      * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
      * INVALID_RECT if the layer has no buffer and no crop.
@@ -819,6 +824,7 @@
 
     bool setFrameRate(FrameRate frameRate);
     virtual FrameRate getFrameRateForLayerTree() const;
+    static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
 
 protected:
     // constant
@@ -944,7 +950,8 @@
     bool hasInput() const;
 
 protected:
-    // -----------------------------------------------------------------------
+    compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
+
     bool usingRelativeZ(LayerVector::StateSet stateSet) const;
 
     bool mPremultipliedAlpha{true};
@@ -1016,6 +1023,11 @@
     const int mWindowType;
 
 private:
+    virtual void setTransformHint(ui::Transform::RotationFlags) {}
+
+    Hwc2::IComposerClient::Composition getCompositionType(const DisplayDevice&) const;
+    Region getVisibleRegion(const DisplayDevice*) const;
+
     /**
      * Returns an unsorted vector of all layers that are part of this tree.
      * That includes the current layer and all its descendants.
@@ -1079,14 +1091,3 @@
 };
 
 } // namespace android
-
-#define RETURN_IF_NO_HWC_LAYER(displayDevice, ...)                                     \
-    do {                                                                               \
-        if (!hasHwcLayer(displayDevice)) {                                             \
-            ALOGE("[%s] %s failed: no HWC layer found for display %s", mName.string(), \
-                  __FUNCTION__, displayDevice->getDebugName().c_str());                \
-            return __VA_ARGS__;                                                        \
-        }                                                                              \
-    } while (false)
-
-#endif // ANDROID_LAYER_H
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 5009e10..18a2891 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -38,13 +38,7 @@
     // because we don't know where this destructor is called from. It could be
     // called with the mStateLock held, leading to a dead-lock (it actually
     // happens).
-    sp<LambdaMessage> cleanUpListMessage =
-            new LambdaMessage([flinger = mFlinger, asBinder = wp<IBinder>(onAsBinder())]() {
-                Mutex::Autolock lock(flinger->mStateLock);
-                flinger->mGraphicBufferProducerList.erase(asBinder);
-            });
-
-    mFlinger->postMessageAsync(cleanUpListMessage);
+    mFlinger->removeGraphicBufferProducerAsync(onAsBinder());
 }
 
 status_t MonitoredProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 6bb999c..c5a4689 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -7,3 +7,4 @@
 steventhomas@google.com
 stoza@google.com
 vhau@google.com
+vishnun@google.com
diff --git a/services/surfaceflinger/Promise.h b/services/surfaceflinger/Promise.h
new file mode 100644
index 0000000..a80d441
--- /dev/null
+++ b/services/surfaceflinger/Promise.h
@@ -0,0 +1,80 @@
+/*
+ * 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 <future>
+#include <type_traits>
+#include <utility>
+
+namespace android::promise {
+namespace impl {
+
+template <typename T>
+struct FutureResult {
+    using Type = T;
+};
+
+template <typename T>
+struct FutureResult<std::future<T>> {
+    using Type = T;
+};
+
+} // namespace impl
+
+template <typename T>
+using FutureResult = typename impl::FutureResult<T>::Type;
+
+template <typename... Args>
+inline auto defer(Args... args) {
+    return std::async(std::launch::deferred, std::forward<Args>(args)...);
+}
+
+template <typename T>
+inline std::future<T> yield(T&& v) {
+    return defer([](T&& v) { return std::forward<T>(v); }, std::forward<T>(v));
+}
+
+template <typename T>
+struct Chain {
+    Chain(std::future<T>&& f) : future(std::move(f)) {}
+    operator std::future<T>&&() && { return std::move(future); }
+
+    T get() && { return future.get(); }
+
+    template <typename F, typename R = std::invoke_result_t<F, T>>
+    auto then(F&& op) && -> Chain<FutureResult<R>> {
+        return defer(
+                [](auto&& f, F&& op) {
+                    R r = op(f.get());
+                    if constexpr (std::is_same_v<R, FutureResult<R>>) {
+                        return r;
+                    } else {
+                        return r.get();
+                    }
+                },
+                std::move(future), std::forward<F>(op));
+    }
+
+    std::future<T> future;
+};
+
+template <typename T>
+inline Chain<T> chain(std::future<T>&& f) {
+    return std::move(f);
+}
+
+} // namespace android::promise
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 2e7fbc1..f602412 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -200,26 +200,22 @@
     }
 }
 
-void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
-    const auto display = mFlinger.getDefaultDisplayDeviceLocked();
-    if (!display) {
-        return;
-    }
-
-    const int32_t left = display->getWidth() / 32;
-    const int32_t top = display->getHeight() / 32;
-    const int32_t right = left + display->getWidth() / 8;
-    const int32_t buttom = top + display->getHeight() / 32;
-
-    auto buffer = mBufferCache[refreshRate.getFps()];
-    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
-
-    mLayer->setFrame(Rect(left, top, right, buttom));
+void RefreshRateOverlay::setViewport(ui::Size viewport) {
+    Rect frame(viewport.width >> 3, viewport.height >> 5);
+    frame.offsetBy(viewport.width >> 5, viewport.height >> 4);
+    mLayer->setFrame(frame);
 
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
 
-}; // namespace android
+void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
+    auto buffer = mBufferCache[refreshRate.getFps()];
+    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
+
+    mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+}
+
+} // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 6d34df2..35c8020 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -13,19 +13,35 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
-#include "SurfaceFlinger.h"
+#include <unordered_map>
+
+#include <math/vec4.h>
+#include <ui/Rect.h>
+#include <ui/Size.h>
+#include <utils/StrongPointer.h>
+
+#include "Scheduler/RefreshRateConfigs.h"
 
 namespace android {
 
+class Client;
+class GraphicBuffer;
+class IBinder;
+class IGraphicBufferProducer;
+class Layer;
+class SurfaceFlinger;
+
 using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
 
 class RefreshRateOverlay {
 public:
-    RefreshRateOverlay(SurfaceFlinger& flinger);
+    explicit RefreshRateOverlay(SurfaceFlinger&);
 
-    void changeRefreshRate(const RefreshRate& refreshRate);
+    void setViewport(ui::Size);
+    void changeRefreshRate(const RefreshRate&);
 
 private:
     class SevenSegmentDrawer {
@@ -56,7 +72,7 @@
     void primeCache();
 
     SurfaceFlinger& mFlinger;
-    sp<Client> mClient;
+    const sp<Client> mClient;
     sp<Layer> mLayer;
     sp<IBinder> mIBinder;
     sp<IGraphicBufferProducer> mGbp;
@@ -68,4 +84,4 @@
     const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f);
 };
 
-}; // namespace android
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 5dedb6a..cee36a1 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -152,16 +152,9 @@
     mEventThread->requestNextVsync(this);
 }
 
-void EventThreadConnection::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) {
-    ATRACE_NAME("enableConfigEvents");
-    mConfigChanged = configChangeFlag;
-
-    // In principle it's possible for rapidly toggling config events to drop an
-    // event here, but it's unlikely in practice.
-    if (configChangeFlag == ISurfaceComposer::eConfigChangedDispatch) {
-        mForcedConfigChangeDispatch = true;
-        mEventThread->requestLatestConfig();
-    }
+void EventThreadConnection::requestLatestConfig() {
+    ATRACE_NAME("requestLatestConfig");
+    mEventThread->requestLatestConfig(this);
 }
 
 status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
@@ -276,8 +269,12 @@
     }
 }
 
-void EventThread::requestLatestConfig() {
+void EventThread::requestLatestConfig(const sp<EventThreadConnection>& connection) {
     std::lock_guard<std::mutex> lock(mMutex);
+    if (connection->mForcedConfigChangeDispatch) {
+        return;
+    }
+    connection->mForcedConfigChangeDispatch = true;
     auto pendingConfigChange =
             std::find_if(std::begin(mPendingEvents), std::end(mPendingEvents),
                          [&](const DisplayEventReceiver::Event& event) {
@@ -384,6 +381,10 @@
                 vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
 
                 if (event && shouldConsumeEvent(*event, connection)) {
+                    if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED &&
+                        connection->mForcedConfigChangeDispatch) {
+                        connection->mForcedConfigChangeDispatch = false;
+                    }
                     consumers.push_back(connection);
                 }
 
@@ -459,8 +460,8 @@
             return true;
 
         case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: {
-            const bool forcedDispatch = connection->mForcedConfigChangeDispatch.exchange(false);
-            return forcedDispatch ||
+            const bool oneTimeDispatch = connection->mForcedConfigChangeDispatch;
+            return oneTimeDispatch ||
                     connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch;
         }
 
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 9e7086e..64acbd7 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -81,19 +81,19 @@
     status_t stealReceiveChannel(gui::BitTube* outChannel) override;
     status_t setVsyncRate(uint32_t rate) override;
     void requestNextVsync() override; // asynchronous
-    void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override;
+    void requestLatestConfig() override; // asynchronous
 
     // Called in response to requestNextVsync.
     const ResyncCallback resyncCallback;
 
     VSyncRequest vsyncRequest = VSyncRequest::None;
-    std::atomic<ISurfaceComposer::ConfigChanged> mConfigChanged =
+    ISurfaceComposer::ConfigChanged mConfigChanged =
             ISurfaceComposer::ConfigChanged::eConfigChangedSuppress;
     // Store whether we need to force dispatching a config change separately -
     // if mConfigChanged ever changes before the config change is dispatched
     // then we still need to propagate an initial config to the app if we
     // haven't already.
-    std::atomic<bool> mForcedConfigChangeDispatch = false;
+    bool mForcedConfigChangeDispatch = false;
 
 private:
     virtual void onFirstRef();
@@ -129,11 +129,10 @@
     virtual void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) = 0;
     // Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
     virtual void requestNextVsync(const sp<EventThreadConnection>& connection) = 0;
-
     // Dispatches the most recent configuration
     // Usage of this method assumes that only the primary internal display
     // supports multiple display configurations.
-    virtual void requestLatestConfig() = 0;
+    virtual void requestLatestConfig(const sp<EventThreadConnection>& connection) = 0;
 
     // Retrieves the number of event connections tracked by this EventThread.
     virtual size_t getEventThreadConnectionCount() = 0;
@@ -154,7 +153,7 @@
     status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
     void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
     void requestNextVsync(const sp<EventThreadConnection>& connection) override;
-    void requestLatestConfig() override;
+    void requestLatestConfig(const sp<EventThreadConnection>& connection) override;
 
     // called before the screen is turned off from main thread
     void onScreenReleased() override;
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index b067466..e6c5cc9 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -174,8 +174,10 @@
                         return LayerVoteType::NoVote;
                 }
             }();
-            if (layer->isVisible() && (frameRate.rate > 0 || voteType == LayerVoteType::NoVote)) {
-                info->setLayerVote(voteType, frameRate.rate);
+
+            if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
+                const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
+                info->setLayerVote(type, frameRate.rate);
             } else {
                 info->resetLayerVote();
             }
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index bf1fb88..b7d0bdd 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -111,23 +111,46 @@
 
     // Calculate the refresh rate by finding the average delta between frames
     nsecs_t totalPresentTimeDeltas = 0;
+    nsecs_t totalQueueTimeDeltas = 0;
+    auto missingPresentTime = false;
     for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
-        // If there are no presentation timestamp provided we can't calculate the refresh rate
+        totalQueueTimeDeltas +=
+                std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
+
         if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
-            return std::nullopt;
+            missingPresentTime = true;
+            continue;
         }
 
         totalPresentTimeDeltas +=
                 std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
     }
+
+    // If there are no presentation timestamps provided we can't calculate the refresh rate
+    if (missingPresentTime && mLastReportedRefreshRate == 0) {
+        return std::nullopt;
+    }
+
+    // Calculate the average frame time based on presentation timestamps. If those
+    // doesn't exist, we look at the time the buffer was queued only. We can do that only if
+    // we calculated a refresh rate based on presentation timestamps in the past. The reason
+    // we look at the queue time is to handle cases where hwui attaches presentation timestamps
+    // when implementing render ahead for specific refresh rates. When hwui no longer provides
+    // presentation timestamps we look at the queue time to see if the current refresh rate still
+    // matches the content.
     const float averageFrameTime =
-            static_cast<float>(totalPresentTimeDeltas) / (mFrameTimes.size() - 1);
+            static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
+            (mFrameTimes.size() - 1);
 
     // Now once we calculated the refresh rate we need to make sure that all the frames we captured
     // 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);
+        const auto presentTimeDeltas = [&] {
+            const auto delta = missingPresentTime ? (it + 1)->queueTime - it->queueTime
+                                                  : (it + 1)->presetTime - it->presetTime;
+            return std::max(delta, mHighRefreshRatePeriod);
+        }();
+
         if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
             return std::nullopt;
         }
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
index ad91f18..e36b7f7 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.h
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -88,6 +88,7 @@
         // 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();
+        mLastReportedRefreshRate = 0.0f;
     }
 
 private:
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 9d6e1d8..6067e69 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -18,10 +18,6 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wconversion"
 
-#include <errno.h>
-#include <stdint.h>
-#include <sys/types.h>
-
 #include <binder/IPCThreadState.h>
 
 #include <utils/Log.h>
@@ -35,26 +31,7 @@
 #include "MessageQueue.h"
 #include "SurfaceFlinger.h"
 
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-MessageBase::MessageBase() : MessageHandler() {}
-
-MessageBase::~MessageBase() {}
-
-void MessageBase::handleMessage(const Message&) {
-    this->handler();
-    barrier.open();
-};
-
-// ---------------------------------------------------------------------------
-
-MessageQueue::~MessageQueue() = default;
-
-// ---------------------------------------------------------------------------
-
-namespace impl {
+namespace android::impl {
 
 void MessageQueue::Handler::dispatchRefresh() {
     if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
@@ -123,14 +100,8 @@
     } while (true);
 }
 
-status_t MessageQueue::postMessage(const sp<MessageBase>& messageHandler, nsecs_t relTime) {
-    const Message dummyMessage;
-    if (relTime > 0) {
-        mLooper->sendMessageDelayed(relTime, messageHandler, dummyMessage);
-    } else {
-        mLooper->sendMessage(messageHandler, dummyMessage);
-    }
-    return NO_ERROR;
+void MessageQueue::postMessage(sp<MessageHandler>&& handler) {
+    mLooper->sendMessage(handler, Message());
 }
 
 void MessageQueue::invalidate() {
@@ -160,10 +131,7 @@
     return 1;
 }
 
-// ---------------------------------------------------------------------------
-
-} // namespace impl
-} // namespace android
+} // namespace android::impl
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
 #pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index ebc4315..132b416 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_MESSAGE_QUEUE_H
-#define ANDROID_MESSAGE_QUEUE_H
+#pragma once
 
-#include <errno.h>
-#include <stdint.h>
-#include <sys/types.h>
+#include <cstdint>
+#include <future>
+#include <type_traits>
+#include <utility>
 
 #include <utils/Looper.h>
 #include <utils/Timers.h>
@@ -28,52 +28,30 @@
 #include <gui/IDisplayEventConnection.h>
 #include <private/gui/BitTube.h>
 
-#include "Barrier.h"
 #include "EventThread.h"
 
-#include <functional>
-
 namespace android {
 
 class SurfaceFlinger;
 
-// ---------------------------------------------------------------------------
+template <typename F>
+class Task : public MessageHandler {
+    template <typename G>
+    friend auto makeTask(G&&);
 
-class MessageBase : public MessageHandler {
-public:
-    MessageBase();
+    explicit Task(F&& f) : mTask(std::move(f)) {}
 
-    // return true if message has a handler
-    virtual bool handler() = 0;
+    void handleMessage(const Message&) override { mTask(); }
 
-    // waits for the handler to be processed
-    void wait() const { barrier.wait(); }
-
-protected:
-    virtual ~MessageBase();
-
-private:
-    virtual void handleMessage(const Message& message);
-
-    mutable Barrier barrier;
+    using T = std::invoke_result_t<F>;
+    std::packaged_task<T()> mTask;
 };
 
-class LambdaMessage : public MessageBase {
-public:
-    explicit LambdaMessage(std::function<void()> handler)
-          : MessageBase(), mHandler(std::move(handler)) {}
-
-    bool handler() override {
-        mHandler();
-        // This return value is no longer checked, so it's always safe to return true
-        return true;
-    }
-
-private:
-    const std::function<void()> mHandler;
-};
-
-// ---------------------------------------------------------------------------
+template <typename F>
+inline auto makeTask(F&& f) {
+    sp<Task<F>> task = new Task<F>(std::move(f));
+    return std::make_pair(task, task->mTask.get_future());
+}
 
 class MessageQueue {
 public:
@@ -82,12 +60,12 @@
         REFRESH = 1,
     };
 
-    virtual ~MessageQueue();
+    virtual ~MessageQueue() = default;
 
     virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
     virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0;
     virtual void waitMessage() = 0;
-    virtual status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) = 0;
+    virtual void postMessage(sp<MessageHandler>&&) = 0;
     virtual void invalidate() = 0;
     virtual void refresh() = 0;
 };
@@ -127,7 +105,7 @@
     void setEventConnection(const sp<EventThreadConnection>& connection) override;
 
     void waitMessage() override;
-    status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) override;
+    void postMessage(sp<MessageHandler>&&) override;
 
     // sends INVALIDATE message at next VSYNC
     void invalidate() override;
@@ -136,9 +114,5 @@
     void refresh() override;
 };
 
-// ---------------------------------------------------------------------------
-
 } // namespace impl
 } // namespace android
-
-#endif /* ANDROID_MESSAGE_QUEUE_H */
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index d9aaa05..fe2e406 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -60,6 +60,12 @@
 PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
       : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
                      refreshRateConfigs.getCurrentRefreshRate().getFps(),
+                     sysprop::vsync_event_phase_offset_ns(1000000),
+                     sysprop::vsync_sf_event_phase_offset_ns(1000000),
+                     getProperty("debug.sf.early_phase_offset_ns"),
+                     getProperty("debug.sf.early_gl_phase_offset_ns"),
+                     getProperty("debug.sf.early_app_phase_offset_ns"),
+                     getProperty("debug.sf.early_gl_app_phase_offset_ns"),
                      // Below defines the threshold when an offset is considered to be negative,
                      // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
                      // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
@@ -69,8 +75,18 @@
                              .value_or(std::numeric_limits<nsecs_t>::max())) {}
 
 PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
-                           nsecs_t thresholdForNextVsync)
-      : mThresholdForNextVsync(thresholdForNextVsync),
+                           nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+                           std::optional<nsecs_t> earlySfOffsetNs,
+                           std::optional<nsecs_t> earlyGlSfOffsetNs,
+                           std::optional<nsecs_t> earlyAppOffsetNs,
+                           std::optional<nsecs_t> earlyGlAppOffsetNs, nsecs_t thresholdForNextVsync)
+      : mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
+        mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
+        mEarlySfOffsetNs(earlySfOffsetNs),
+        mEarlyGlSfOffsetNs(earlyGlSfOffsetNs),
+        mEarlyAppOffsetNs(earlyAppOffsetNs),
+        mEarlyGlAppOffsetNs(earlyGlAppOffsetNs),
+        mThresholdForNextVsync(thresholdForNextVsync),
         mOffsets(initializeOffsets(refreshRates)),
         mRefreshRateFps(currentFps) {}
 
@@ -106,35 +122,27 @@
 }
 
 PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
-    const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000);
-    const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000);
-
-    const auto earlySfOffsetNs = getProperty("debug.sf.early_phase_offset_ns");
-    const auto earlyGlSfOffsetNs = getProperty("debug.sf.early_gl_phase_offset_ns");
-    const auto earlyAppOffsetNs = getProperty("debug.sf.early_app_phase_offset_ns");
-    const auto earlyGlAppOffsetNs = getProperty("debug.sf.early_gl_app_phase_offset_ns");
-
     return {
             {
-                    earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync
-                            ? earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs)
-                            : earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration,
+                    mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+                            ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+                            : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
 
-                    earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs),
+                    mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
             },
             {
-                    earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync
-                            ? earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs)
-                            : earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration,
+                    mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+                            ? mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+                            : mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
 
-                    earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs),
+                    mEarlyGlAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
             },
             {
-                    sfVsyncPhaseOffsetNs < mThresholdForNextVsync
-                            ? sfVsyncPhaseOffsetNs
-                            : sfVsyncPhaseOffsetNs - vsyncDuration,
+                    mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
+                            ? mSfVSyncPhaseOffsetNs
+                            : mSfVSyncPhaseOffsetNs - vsyncDuration,
 
-                    vsyncPhaseOffsetNs,
+                    mVSyncPhaseOffsetNs,
             },
     };
 }
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index fa8011d..9ec6d56 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -69,6 +69,9 @@
 protected:
     // Used for unit tests
     PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+                 nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs,
+                 std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGlSfOffsetNs,
+                 std::optional<nsecs_t> earlyAppOffsetNs, std::optional<nsecs_t> earlyGlAppOffsetNs,
                  nsecs_t thresholdForNextVsync);
     std::unordered_map<float, Offsets> initializeOffsets(
             const std::vector<float>& refreshRates) const;
@@ -76,6 +79,12 @@
     Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const;
     Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const;
 
+    const nsecs_t mVSyncPhaseOffsetNs;
+    const nsecs_t mSfVSyncPhaseOffsetNs;
+    const std::optional<nsecs_t> mEarlySfOffsetNs;
+    const std::optional<nsecs_t> mEarlyGlSfOffsetNs;
+    const std::optional<nsecs_t> mEarlyAppOffsetNs;
+    const std::optional<nsecs_t> mEarlyGlAppOffsetNs;
     const nsecs_t mThresholdForNextVsync;
     const std::unordered_map<float, Offsets> mOffsets;
 
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 5634adb..8d958df 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -58,7 +58,7 @@
     ATRACE_INT("ContentFPS", contentFramerate);
 
     // Find the appropriate refresh rate with minimal error
-    auto iter = min_element(mAvailableRefreshRates.cbegin(), mAvailableRefreshRates.cend(),
+    auto iter = min_element(mPrimaryRefreshRates.cbegin(), mPrimaryRefreshRates.cend(),
                             [contentFramerate](const auto& lhs, const auto& rhs) -> bool {
                                 return std::abs(lhs->fps - contentFramerate) <
                                         std::abs(rhs->fps - contentFramerate);
@@ -71,7 +71,7 @@
     constexpr float MARGIN = 0.05f;
     float ratio = (*iter)->fps / contentFramerate;
     if (std::abs(std::round(ratio) - ratio) > MARGIN) {
-        while (iter != mAvailableRefreshRates.cend()) {
+        while (iter != mPrimaryRefreshRates.cend()) {
             ratio = (*iter)->fps / contentFramerate;
 
             if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
@@ -97,8 +97,8 @@
     return {displayFramesQuot, displayFramesRem};
 }
 
-const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2(
-        const std::vector<LayerRequirement>& layers, bool touchActive,
+const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
+        const std::vector<LayerRequirement>& layers, bool touchActive, bool idle,
         bool* touchConsidered) const {
     ATRACE_CALL();
     ALOGV("getRefreshRateForContent %zu layers", layers.size());
@@ -106,13 +106,6 @@
     *touchConsidered = false;
     std::lock_guard lock(mLock);
 
-    // If there are not layers, there is not content detection, so return the current
-    // refresh rate.
-    if (layers.empty()) {
-        *touchConsidered = touchActive;
-        return touchActive ? *mAvailableRefreshRates.back() : getCurrentRefreshRateByPolicyLocked();
-    }
-
     int noVoteLayers = 0;
     int minVoteLayers = 0;
     int maxVoteLayers = 0;
@@ -135,25 +128,33 @@
         }
     }
 
-    // Consider the touch event if there are no ExplicitDefault layers.
-    // ExplicitDefault are mostly interactive (as opposed to ExplicitExactOrMultiple)
-    // and therefore if those posted an explicit vote we should not change it
-    // if get get a touch event.
-    if (touchActive && explicitDefaultVoteLayers == 0) {
+    // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
+    // selected a refresh rate to see if we should apply touch boost.
+    if (touchActive && explicitDefaultVoteLayers == 0 && explicitExactOrMultipleVoteLayers == 0) {
         *touchConsidered = true;
-        return *mAvailableRefreshRates.back();
+        return getMaxRefreshRateByPolicyLocked();
+    }
+
+    if (!touchActive && idle) {
+        return getMinRefreshRateByPolicyLocked();
+    }
+
+    if (layers.empty()) {
+        return getCurrentRefreshRateByPolicyLocked();
     }
 
     // Only if all layers want Min we should return Min
     if (noVoteLayers + minVoteLayers == layers.size()) {
-        return *mAvailableRefreshRates.front();
+        return getMinRefreshRateByPolicyLocked();
     }
 
+    const Policy* policy = getCurrentPolicyLocked();
+
     // Find the best refresh rate based on score
     std::vector<std::pair<const RefreshRate*, float>> scores;
-    scores.reserve(mAvailableRefreshRates.size());
+    scores.reserve(mAppRequestRefreshRates.size());
 
-    for (const auto refreshRate : mAvailableRefreshRates) {
+    for (const auto refreshRate : mAppRequestRefreshRates) {
         scores.emplace_back(refreshRate, 0.0f);
     }
 
@@ -166,6 +167,15 @@
         auto weight = layer.weight;
 
         for (auto i = 0u; i < scores.size(); i++) {
+            bool inPrimaryRange =
+                    scores[i].first->inPolicy(policy->primaryRange.min, policy->primaryRange.max);
+            if (!inPrimaryRange && layer.vote != LayerVoteType::ExplicitDefault &&
+                layer.vote != LayerVoteType::ExplicitExactOrMultiple) {
+                // Only layers with explicit frame rate settings are allowed to score refresh rates
+                // outside the primary range.
+                continue;
+            }
+
             // If the layer wants Max, give higher score to the higher refresh rate
             if (layer.vote == LayerVoteType::Max) {
                 const auto ratio = scores[i].first->fps / scores.back().first->fps;
@@ -249,6 +259,17 @@
             ? getBestRefreshRate(scores.rbegin(), scores.rend())
             : getBestRefreshRate(scores.begin(), scores.end());
 
+    // Consider the touch event if there are no ExplicitDefault layers. ExplicitDefault are mostly
+    // interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
+    // vote we should not change it if we get a touch event. Only apply touch boost if it will
+    // actually increase the refresh rate over the normal selection.
+    const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();
+    if (touchActive && explicitDefaultVoteLayers == 0 &&
+        bestRefreshRate->fps < touchRefreshRate.fps) {
+        *touchConsidered = true;
+        return touchRefreshRate;
+    }
+
     return *bestRefreshRate;
 }
 
@@ -278,12 +299,20 @@
 
 const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
     std::lock_guard lock(mLock);
-    return *mAvailableRefreshRates.front();
+    return getMinRefreshRateByPolicyLocked();
+}
+
+const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
+    return *mPrimaryRefreshRates.front();
 }
 
 const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
     std::lock_guard lock(mLock);
-        return *mAvailableRefreshRates.back();
+    return getMaxRefreshRateByPolicyLocked();
+}
+
+const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const {
+    return *mPrimaryRefreshRates.back();
 }
 
 const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const {
@@ -297,8 +326,8 @@
 }
 
 const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicyLocked() const {
-    if (std::find(mAvailableRefreshRates.begin(), mAvailableRefreshRates.end(),
-                  mCurrentRefreshRate) != mAvailableRefreshRates.end()) {
+    if (std::find(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(),
+                  mCurrentRefreshRate) != mAppRequestRefreshRates.end()) {
         return *mCurrentRefreshRate;
     }
     return *mRefreshRates.at(getCurrentPolicyLocked()->defaultConfig);
@@ -320,7 +349,7 @@
         const float fps = 1e9f / config->getVsyncPeriod();
         mRefreshRates.emplace(configId,
                               std::make_unique<RefreshRate>(configId, config,
-                                                            base::StringPrintf("%2.ffps", fps), fps,
+                                                            base::StringPrintf("%.0ffps", fps), fps,
                                                             RefreshRate::ConstructorTag(0)));
         if (configId == currentConfigId) {
             mCurrentRefreshRate = mRefreshRates.at(configId).get();
@@ -342,10 +371,11 @@
         return false;
     }
     const RefreshRate& refreshRate = *iter->second;
-    if (!refreshRate.inPolicy(policy.minRefreshRate, policy.maxRefreshRate)) {
+    if (!refreshRate.inPolicy(policy.primaryRange.min, policy.primaryRange.max)) {
         return false;
     }
-    return true;
+    return policy.appRequestRange.min <= policy.primaryRange.min &&
+            policy.appRequestRange.max >= policy.primaryRange.max;
 }
 
 status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
@@ -392,7 +422,7 @@
 
 bool RefreshRateConfigs::isConfigAllowed(HwcConfigIndexType config) const {
     std::lock_guard lock(mLock);
-    for (const RefreshRate* refreshRate : mAvailableRefreshRates) {
+    for (const RefreshRate* refreshRate : mAppRequestRefreshRates) {
         if (refreshRate->configId == config) {
             return true;
         }
@@ -430,33 +460,44 @@
     // Filter configs based on current policy and sort based on vsync period
     const Policy* policy = getCurrentPolicyLocked();
     const auto& defaultConfig = mRefreshRates.at(policy->defaultConfig)->hwcConfig;
-    ALOGV("constructAvailableRefreshRates: default %d group %d min %.2f max %.2f",
-          policy->defaultConfig.value(), defaultConfig->getConfigGroup(), policy->minRefreshRate,
-          policy->maxRefreshRate);
-    getSortedRefreshRateList(
-            [&](const RefreshRate& refreshRate) REQUIRES(mLock) {
-                const auto& hwcConfig = refreshRate.hwcConfig;
+    ALOGV("constructAvailableRefreshRates: default %d group %d primaryRange=[%.2f %.2f]"
+          " appRequestRange=[%.2f %.2f]",
+          policy->defaultConfig.value(), defaultConfig->getConfigGroup(), policy->primaryRange.min,
+          policy->primaryRange.max, policy->appRequestRange.min, policy->appRequestRange.max);
 
-                return hwcConfig->getHeight() == defaultConfig->getHeight() &&
-                        hwcConfig->getWidth() == defaultConfig->getWidth() &&
-                        hwcConfig->getDpiX() == defaultConfig->getDpiX() &&
-                        hwcConfig->getDpiY() == defaultConfig->getDpiY() &&
-                        (policy->allowGroupSwitching ||
-                         hwcConfig->getConfigGroup() == defaultConfig->getConfigGroup()) &&
-                        refreshRate.inPolicy(policy->minRefreshRate, policy->maxRefreshRate);
-            },
-            &mAvailableRefreshRates);
+    auto filterRefreshRates = [&](float min, float max, const char* listName,
+                                  std::vector<const RefreshRate*>* outRefreshRates) {
+        getSortedRefreshRateList(
+                [&](const RefreshRate& refreshRate) REQUIRES(mLock) {
+                    const auto& hwcConfig = refreshRate.hwcConfig;
 
-    std::string availableRefreshRates;
-    for (const auto& refreshRate : mAvailableRefreshRates) {
-        base::StringAppendF(&availableRefreshRates, "%s ", refreshRate->name.c_str());
-    }
+                    return hwcConfig->getHeight() == defaultConfig->getHeight() &&
+                            hwcConfig->getWidth() == defaultConfig->getWidth() &&
+                            hwcConfig->getDpiX() == defaultConfig->getDpiX() &&
+                            hwcConfig->getDpiY() == defaultConfig->getDpiY() &&
+                            (policy->allowGroupSwitching ||
+                             hwcConfig->getConfigGroup() == defaultConfig->getConfigGroup()) &&
+                            refreshRate.inPolicy(min, max);
+                },
+                outRefreshRates);
 
-    ALOGV("Available refresh rates: %s", availableRefreshRates.c_str());
-    LOG_ALWAYS_FATAL_IF(mAvailableRefreshRates.empty(),
-                        "No compatible display configs for default=%d min=%.0f max=%.0f",
-                        policy->defaultConfig.value(), policy->minRefreshRate,
-                        policy->maxRefreshRate);
+        LOG_ALWAYS_FATAL_IF(outRefreshRates->empty(),
+                            "No matching configs for %s range: min=%.0f max=%.0f", listName, min,
+                            max);
+        auto stringifyRefreshRates = [&]() -> std::string {
+            std::string str;
+            for (auto refreshRate : *outRefreshRates) {
+                base::StringAppendF(&str, "%s ", refreshRate->name.c_str());
+            }
+            return str;
+        };
+        ALOGV("%s refresh rates: %s", listName, stringifyRefreshRates().c_str());
+    };
+
+    filterRefreshRates(policy->primaryRange.min, policy->primaryRange.max, "primary",
+                       &mPrimaryRefreshRates);
+    filterRefreshRates(policy->appRequestRange.min, policy->appRequestRange.max, "app request",
+                       &mAppRequestRefreshRates);
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index dea7e90..2657dee 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -107,18 +107,46 @@
             std::unordered_map<HwcConfigIndexType, std::unique_ptr<const RefreshRate>>;
 
     struct Policy {
+        struct Range {
+            float min = 0;
+            float max = std::numeric_limits<float>::max();
+
+            bool operator==(const Range& other) const {
+                return min == other.min && max == other.max;
+            }
+
+            bool operator!=(const Range& other) const { return !(*this == other); }
+        };
+
         // The default config, used to ensure we only initiate display config switches within the
         // same config group as defaultConfigId's group.
         HwcConfigIndexType defaultConfig;
-        // The min and max FPS allowed by the policy.
-        float minRefreshRate = 0;
-        float maxRefreshRate = std::numeric_limits<float>::max();
+        // The primary refresh rate range represents display manager's general guidance on the
+        // display configs we'll consider when switching refresh rates. Unless we get an explicit
+        // signal from an app, we should stay within this range.
+        Range primaryRange;
+        // The app request refresh rate range allows us to consider more display configs when
+        // switching refresh rates. Although we should generally stay within the primary range,
+        // specific considerations, such as layer frame rate settings specified via the
+        // setFrameRate() api, may cause us to go outside the primary range. We never go outside the
+        // app request range. The app request range will be greater than or equal to the primary
+        // refresh rate range, never smaller.
+        Range appRequestRange;
         // Whether or not we switch config groups to get the best frame rate. Only used by tests.
         bool allowGroupSwitching = false;
 
+        Policy() = default;
+        Policy(HwcConfigIndexType defaultConfig, const Range& range)
+              : Policy(defaultConfig, range, range) {}
+        Policy(HwcConfigIndexType defaultConfig, const Range& primaryRange,
+               const Range& appRequestRange)
+              : defaultConfig(defaultConfig),
+                primaryRange(primaryRange),
+                appRequestRange(appRequestRange) {}
+
         bool operator==(const Policy& other) const {
-            return defaultConfig == other.defaultConfig && minRefreshRate == other.minRefreshRate &&
-                    maxRefreshRate == other.maxRefreshRate &&
+            return defaultConfig == other.defaultConfig && primaryRange == other.primaryRange &&
+                    appRequestRange == other.appRequestRange &&
                     allowGroupSwitching == other.allowGroupSwitching;
         }
 
@@ -184,12 +212,14 @@
     const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const
             EXCLUDES(mLock);
 
-    // Returns the refresh rate that fits best to the given layers. This function also gets a
-    // boolean flag that indicates whether user touched the screen recently to be factored in when
-    // choosing the refresh rate and returns whether the refresh rate was chosen as a result of
-    // a touch event.
-    const RefreshRate& getRefreshRateForContentV2(const std::vector<LayerRequirement>& layers,
-                                                  bool touchActive, bool* touchConsidered) const
+    // Returns the refresh rate that fits best to the given layers.
+    //   layers - The layer requirements to consider.
+    //   touchActive - Whether the user touched the screen recently. Used to apply touch boost.
+    //   idle - True if the system hasn't seen any buffers posted to layers recently.
+    //   touchConsidered - An output param that tells the caller whether the refresh rate was chosen
+    //                     based on touch boost.
+    const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers,
+                                          bool touchActive, bool idle, bool* touchConsidered) const
             EXCLUDES(mLock);
 
     // Returns all the refresh rates supported by the device. This won't change at runtime.
@@ -198,13 +228,15 @@
     // Returns the lowest refresh rate supported by the device. This won't change at runtime.
     const RefreshRate& getMinRefreshRate() const { return *mMinSupportedRefreshRate; }
 
-    // Returns the lowest refresh rate according to the current policy. May change in runtime.
+    // Returns the lowest refresh rate according to the current policy. May change at runtime. Only
+    // uses the primary range, not the app request range.
     const RefreshRate& getMinRefreshRateByPolicy() const EXCLUDES(mLock);
 
     // Returns the highest refresh rate supported by the device. This won't change at runtime.
     const RefreshRate& getMaxRefreshRate() const { return *mMaxSupportedRefreshRate; }
 
-    // Returns the highest refresh rate according to the current policy. May change in runtime.
+    // Returns the highest refresh rate according to the current policy. May change at runtime. Only
+    // uses the primary range, not the app request range.
     const RefreshRate& getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
 
     // Returns the current refresh rate
@@ -243,6 +275,14 @@
     // display refresh period.
     std::pair<nsecs_t, nsecs_t> getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const;
 
+    // Returns the lowest refresh rate according to the current policy. May change at runtime. Only
+    // uses the primary range, not the app request range.
+    const RefreshRate& getMinRefreshRateByPolicyLocked() const REQUIRES(mLock);
+
+    // Returns the highest refresh rate according to the current policy. May change at runtime. Only
+    // uses the primary range, not the app request range.
+    const RefreshRate& getMaxRefreshRateByPolicyLocked() const REQUIRES(mLock);
+
     // Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
     // the policy.
     const RefreshRate& getCurrentRefreshRateByPolicyLocked() const REQUIRES(mLock);
@@ -254,9 +294,13 @@
     // object is initialized.
     AllRefreshRatesMapType mRefreshRates;
 
-    // The list of refresh rates which are available in the current policy, ordered by vsyncPeriod
-    // (the first element is the lowest refresh rate)
-    std::vector<const RefreshRate*> mAvailableRefreshRates GUARDED_BY(mLock);
+    // The list of refresh rates in the primary range of the current policy, ordered by vsyncPeriod
+    // (the first element is the lowest refresh rate).
+    std::vector<const RefreshRate*> mPrimaryRefreshRates GUARDED_BY(mLock);
+
+    // The list of refresh rates in the app request range of the current policy, ordered by
+    // vsyncPeriod (the first element is the lowest refresh rate).
+    std::vector<const RefreshRate*> mAppRequestRefreshRates GUARDED_BY(mLock);
 
     // The current config. This will change at runtime. This is set by SurfaceFlinger on
     // the main thread, and read by the Scheduler (and other objects) on other threads.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 86bb6eb..d73fd8b 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -469,7 +469,7 @@
     // that is currently on top. b/142507166 will give us this capability.
     std::lock_guard<std::mutex> lock(mFeatureStateLock);
     if (mLayerHistory) {
-        // Layer History will be cleared based on RefreshRateConfigs::getRefreshRateForContentV2
+        // Layer History will be cleared based on RefreshRateConfigs::getBestRefreshRate
 
         mTouchTimer->reset();
 
@@ -574,29 +574,28 @@
 HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() {
     ATRACE_CALL();
 
-    // NOTE: If we remove the kernel idle timer, and use our internal idle timer, this
-    // code will have to be refactored. 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 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().getConfigId();
     }
 
+    const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active;
+    const bool idle = mIdleTimer && mFeatures.idleTimer == TimerState::Expired;
+
     if (!mUseContentDetectionV2) {
         // As long as touch is active we want to be in performance mode.
-        if (mTouchTimer && mFeatures.touch == TouchState::Active) {
+        if (touchActive) {
             return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
         }
-    }
 
-    // If timer has expired as it means there is no new content on the screen.
-    if (mIdleTimer && mFeatures.idleTimer == TimerState::Expired) {
-        return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId();
-    }
+        // If timer has expired as it means there is no new content on the screen.
+        if (idle) {
+            return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId();
+        }
 
-    if (!mUseContentDetectionV2) {
         // If content detection is off we choose performance as we don't know the content fps.
         if (mFeatures.contentDetectionV1 == ContentDetectionState::Off) {
             // NOTE: V1 always calls this, but this is not a default behavior for V2.
@@ -609,13 +608,10 @@
     }
 
     bool touchConsidered;
-    const auto& ret =
-            mRefreshRateConfigs
-                    .getRefreshRateForContentV2(mFeatures.contentRequirements,
-                                                mTouchTimer &&
-                                                        mFeatures.touch == TouchState::Active,
-                                                &touchConsidered)
-                    .getConfigId();
+    const auto& ret = mRefreshRateConfigs
+                              .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle,
+                                                  &touchConsidered)
+                              .getConfigId();
     if (touchConsidered) {
         // Clear layer history if refresh rate was selected based on touch to allow
         // the hueristic to pick up with the new rate.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 737f8f8..4549ab3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -27,7 +27,7 @@
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware/power/1.0/IPower.h>
+#include <android/hardware/power/Boost.h>
 #include <android/native_window.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -108,6 +108,7 @@
 #include "LayerVector.h"
 #include "MonitoredProducer.h"
 #include "NativeWindowSurface.h"
+#include "Promise.h"
 #include "RefreshRateOverlay.h"
 #include "RegionSamplingThread.h"
 #include "Scheduler/DispSync.h"
@@ -132,7 +133,7 @@
 using namespace android::hardware::configstore::V1_0;
 using namespace android::sysprop;
 
-using android::hardware::power::V1_0::PowerHint;
+using android::hardware::power::Boost;
 using base::StringAppendF;
 using ui::ColorMode;
 using ui::Dataspace;
@@ -188,7 +189,7 @@
 using ConditionalLock = ConditionalLockGuard<Mutex>;
 
 // TODO(b/141333600): Consolidate with HWC2::Display::Config::Builder::getDefaultDensity.
-constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV / 160.f;
+constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
 
 float getDensityFromProperty(const char* property, bool required) {
     char value[PROPERTY_VALUE_MAX];
@@ -197,7 +198,7 @@
         ALOGE("%s must be defined as a build property", property);
         return FALLBACK_DENSITY;
     }
-    return density / 160.f;
+    return density;
 }
 
 // Currently we only support V0_SRGB and DISPLAY_P3 as composition preference.
@@ -414,15 +415,13 @@
     useFrameRateApi = use_frame_rate_api(true);
 }
 
-void SurfaceFlinger::onFirstRef()
-{
+SurfaceFlinger::~SurfaceFlinger() = default;
+
+void SurfaceFlinger::onFirstRef() {
     mEventQueue->init(this);
 }
 
-SurfaceFlinger::~SurfaceFlinger() = default;
-
-void SurfaceFlinger::binderDied(const wp<IBinder>& /* who */)
-{
+void SurfaceFlinger::binderDied(const wp<IBinder>&) {
     // the window manager died on us. prepare its eulogy.
     mBootFinished = false;
 
@@ -433,21 +432,25 @@
     startBootAnim();
 }
 
-static sp<ISurfaceComposerClient> initClient(const sp<Client>& client) {
-    status_t err = client->initCheck();
-    if (err == NO_ERROR) {
-        return client;
+void SurfaceFlinger::run() {
+    while (true) {
+        mEventQueue->waitMessage();
     }
-    return nullptr;
+}
+
+template <typename F, typename T>
+inline std::future<T> SurfaceFlinger::schedule(F&& f) {
+    auto [task, future] = makeTask(std::move(f));
+    mEventQueue->postMessage(std::move(task));
+    return std::move(future);
 }
 
 sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
-    return initClient(new Client(this));
+    const sp<Client> client = new Client(this);
+    return client->initCheck() == NO_ERROR ? client : nullptr;
 }
 
-sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName,
-        bool secure)
-{
+sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure) {
     class DisplayToken : public BBinder {
         sp<SurfaceFlinger> flinger;
         virtual ~DisplayToken() {
@@ -583,14 +586,13 @@
     LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
                    ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
 
-    postMessageAsync(new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS {
+    static_cast<void>(schedule([this] {
         readPersistentProperties();
         mPowerAdvisor.onBootFinished();
         mBootStage = BootStage::FINISHED;
 
         if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
-            mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
-            mRefreshRateOverlay->changeRefreshRate(mRefreshRateConfigs->getCurrentRefreshRate());
+            enableRefreshRateOverlay(true);
         }
     }));
 }
@@ -611,9 +613,12 @@
 
     // The pool was empty, so we need to get a new texture name directly using a
     // blocking call to the main thread
-    uint32_t name = 0;
-    postMessageSync(new LambdaMessage([&]() { getRenderEngine().genTextures(1, &name); }));
-    return name;
+    return schedule([this] {
+               uint32_t name = 0;
+               getRenderEngine().genTextures(1, &name);
+               return name;
+           })
+            .get();
 }
 
 void SurfaceFlinger::deleteTextureAsync(uint32_t texture) {
@@ -667,7 +672,7 @@
             // mStateLock from the vr flinger dispatch thread might trigger a
             // deadlock in surface flinger (see b/66916578), so post a message
             // to be handled on the main thread instead.
-            postMessageAsync(new LambdaMessage([=] {
+            static_cast<void>(schedule([=] {
                 ALOGI("VR request display mode: requestDisplay=%d", requestDisplay);
                 mVrFlingerRequestsDisplay = requestDisplay;
                 signalTransaction();
@@ -828,9 +833,10 @@
                 ? mInternalDisplayDensity
                 : FALLBACK_DENSITY;
     }
+    info->density /= ACONFIGURATION_DENSITY_MEDIUM;
 
     info->secure = display->isSecure();
-    info->deviceProductInfo = getDeviceProductInfoLocked(*display);
+    info->deviceProductInfo = display->getDeviceProductInfo();
 
     return NO_ERROR;
 }
@@ -932,9 +938,8 @@
     }
 
     if (isPrimary) {
-        std::lock_guard<std::mutex> lock(mActiveConfigLock);
-        if (mDesiredActiveConfigChanged) {
-            return mDesiredActiveConfig.configId.value();
+        if (const auto config = getDesiredActiveConfig()) {
+            return config->configId.value();
         }
     }
 
@@ -989,29 +994,26 @@
         return BAD_VALUE;
     }
 
-    status_t result = NAME_NOT_FOUND;
-
-    postMessageSync(new LambdaMessage([&]() {
+    auto future = schedule([=]() -> status_t {
         const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
             ALOGE("Attempt to set allowed display configs for invalid display token %p",
                   displayToken.get());
+            return NAME_NOT_FOUND;
         } else if (display->isVirtual()) {
             ALOGW("Attempt to set allowed display configs for virtual display");
-            result = INVALID_OPERATION;
+            return INVALID_OPERATION;
         } else {
-            HwcConfigIndexType config(mode);
-            const auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(config);
-            result = setDesiredDisplayConfigSpecsInternal(display,
-                                                          scheduler::RefreshRateConfigs::
-                                                                  Policy{config,
-                                                                         refreshRate.getFps(),
-                                                                         refreshRate.getFps()},
-                                                          /*overridePolicy=*/false);
-        }
-    }));
+            const HwcConfigIndexType config(mode);
+            const float fps = mRefreshRateConfigs->getRefreshRateFromConfigId(config).getFps();
+            const scheduler::RefreshRateConfigs::Policy policy{config, {fps, fps}};
+            constexpr bool kOverridePolicy = false;
 
-    return result;
+            return setDesiredDisplayConfigSpecsInternal(display, policy, kOverridePolicy);
+        }
+    });
+
+    return future.get();
 }
 
 void SurfaceFlinger::setActiveConfigInternal() {
@@ -1064,14 +1066,7 @@
     ATRACE_CALL();
     ALOGV("performSetActiveConfig");
     // Store the local variable to release the lock.
-    const auto desiredActiveConfig = [&]() -> std::optional<ActiveConfigInfo> {
-        std::lock_guard<std::mutex> lock(mActiveConfigLock);
-        if (mDesiredActiveConfigChanged) {
-            return mDesiredActiveConfig;
-        }
-        return std::nullopt;
-    }();
-
+    const auto desiredActiveConfig = getDesiredActiveConfig();
     if (!desiredActiveConfig) {
         // No desired active config pending to be applied
         return;
@@ -1185,7 +1180,7 @@
 }
 
 status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
-    postMessageSync(new LambdaMessage([&] {
+    schedule([=] {
         Vector<ColorMode> modes;
         getDisplayColorModes(displayToken, &modes);
         bool exists = std::find(std::begin(modes), std::end(modes), mode) != std::end(modes);
@@ -1207,7 +1202,7 @@
                                                             RenderIntent::COLORIMETRIC,
                                                             Dataspace::UNKNOWN});
         }
-    }));
+    }).wait();
 
     return NO_ERROR;
 }
@@ -1231,7 +1226,7 @@
 }
 
 void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
-    postMessageAsync(new LambdaMessage([=] {
+    static_cast<void>(schedule([=] {
         if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
             getHwComposer().setAutoLowLatencyMode(*displayId, on);
         } else {
@@ -1262,7 +1257,7 @@
 }
 
 void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
-    postMessageAsync(new LambdaMessage([=] {
+    static_cast<void>(schedule([=] {
         if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
             const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE;
             getHwComposer().setContentType(*displayId, type);
@@ -1306,30 +1301,6 @@
     return NO_ERROR;
 }
 
-std::optional<DeviceProductInfo> SurfaceFlinger::getDeviceProductInfoLocked(
-        const DisplayDevice& display) const {
-    // TODO(b/149075047): Populate DeviceProductInfo on hotplug and store it in DisplayDevice to
-    // avoid repetitive HAL IPC and EDID parsing.
-    const auto displayId = display.getId();
-    LOG_FATAL_IF(!displayId);
-
-    const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
-    LOG_FATAL_IF(!hwcDisplayId);
-
-    uint8_t port;
-    DisplayIdentificationData data;
-    if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) {
-        ALOGV("%s: No identification data.", __FUNCTION__);
-        return {};
-    }
-
-    const auto info = parseDisplayIdentificationData(port, data);
-    if (!info) {
-        return {};
-    }
-    return info->deviceProductInfo;
-}
-
 status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
                                                                ui::PixelFormat* outFormat,
                                                                ui::Dataspace* outDataspace,
@@ -1352,18 +1323,17 @@
 status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken,
                                                           bool enable, uint8_t componentMask,
                                                           uint64_t maxFrames) {
-    status_t result = NAME_NOT_FOUND;
-
-    postMessageSync(new LambdaMessage([&] {
-        if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
-            result = getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
-                                                                      componentMask, maxFrames);
-        } else {
-            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
-        }
-    }));
-
-    return result;
+    return schedule([=]() -> status_t {
+               if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
+                   return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
+                                                                           componentMask,
+                                                                           maxFrames);
+               } else {
+                   ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+                   return NAME_NOT_FOUND;
+               }
+           })
+            .get();
 }
 
 status_t SurfaceFlinger::getDisplayedContentSample(const sp<IBinder>& displayToken,
@@ -1405,14 +1375,14 @@
 }
 
 status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
-    postMessageSync(new LambdaMessage([&] {
+    schedule([=] {
         Mutex::Autolock lock(mStateLock);
 
         if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
             mEventQueue->setEventConnection(
                     mScheduler->getEventConnection(enable ? handle : mSfConnectionHandle));
         }
-    }));
+    }).wait();
 
     return NO_ERROR;
 }
@@ -1432,10 +1402,10 @@
         return TIMED_OUT;
     }
 
+    const auto display = getDefaultDisplayDeviceLocked();
     outLayers->clear();
-    mCurrentState.traverseInZOrder([&](Layer* layer) {
-        outLayers->push_back(layer->getLayerDebugInfo());
-    });
+    mCurrentState.traverseInZOrder(
+            [&](Layer* layer) { outLayers->push_back(layer->getLayerDebugInfo(display.get())); });
 
     mStateLock.unlock();
     return NO_ERROR;
@@ -1494,23 +1464,22 @@
         return BAD_VALUE;
     }
 
-    status_t result = NAME_NOT_FOUND;
-
-    postMessageSync(new LambdaMessage([&] {
-        if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
-            result = getHwComposer().setDisplayBrightness(*displayId, brightness);
-        } else {
-            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
-        }
-    }));
-
-    return result;
+    return promise::chain(schedule([=] {
+               if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
+                   return getHwComposer().setDisplayBrightness(*displayId, brightness);
+               } else {
+                   ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+                   return promise::yield<status_t>(NAME_NOT_FOUND);
+               }
+           }))
+            .then([](std::future<status_t> task) { return task; })
+            .get();
 }
 
-status_t SurfaceFlinger::notifyPowerHint(int32_t hintId) {
-    PowerHint powerHint = static_cast<PowerHint>(hintId);
+status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) {
+    Boost powerBoost = static_cast<Boost>(boostId);
 
-    if (powerHint == PowerHint::INTERACTION) {
+    if (powerBoost == Boost::INTERACTION) {
         mScheduler->notifyTouchEvent();
     }
 
@@ -1527,12 +1496,6 @@
     return mScheduler->createDisplayEventConnection(handle, configChanged);
 }
 
-// ----------------------------------------------------------------------------
-
-void SurfaceFlinger::waitForEvent() {
-    mEventQueue->waitMessage();
-}
-
 void SurfaceFlinger::signalTransaction() {
     mScheduler->resetIdleTimer();
     mPowerAdvisor.notifyDisplayUpdateImminent();
@@ -1550,26 +1513,6 @@
     mEventQueue->refresh();
 }
 
-status_t SurfaceFlinger::postMessageAsync(const sp<MessageBase>& msg,
-        nsecs_t reltime, uint32_t /* flags */) {
-    return mEventQueue->postMessage(msg, reltime);
-}
-
-status_t SurfaceFlinger::postMessageSync(const sp<MessageBase>& msg,
-        nsecs_t reltime, uint32_t /* flags */) {
-    status_t res = mEventQueue->postMessage(msg, reltime);
-    if (res == NO_ERROR) {
-        msg->wait();
-    }
-    return res;
-}
-
-void SurfaceFlinger::run() {
-    do {
-        waitForEvent();
-    } while (true);
-}
-
 nsecs_t SurfaceFlinger::getVsyncPeriod() const {
     const auto displayId = getInternalDisplayIdLocked();
     if (!displayId || !getHwComposer().isConnected(*displayId)) {
@@ -1634,7 +1577,7 @@
 }
 
 void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId,
-                                       hal::Connection connection) {
+                                       hal::Connection connection) NO_THREAD_SAFETY_ANALYSIS {
     ALOGV("%s(%d, %" PRIu64 ", %s)", __FUNCTION__, sequenceId, hwcDisplayId,
           connection == hal::Connection::CONNECTED ? "connected" : "disconnected");
 
@@ -1687,8 +1630,8 @@
 
     // Enable / Disable HWVsync from the main thread to avoid race conditions with
     // display power state.
-    postMessageAsync(new LambdaMessage(
-            [=]() NO_THREAD_SAFETY_ANALYSIS { setPrimaryVsyncEnabledInternal(enabled); }));
+    static_cast<void>(
+            schedule([=]() NO_THREAD_SAFETY_ANALYSIS { setPrimaryVsyncEnabledInternal(enabled); }));
 }
 
 void SurfaceFlinger::setPrimaryVsyncEnabledInternal(bool enabled) {
@@ -1813,11 +1756,10 @@
         return false;
     }
 
-    if (graceTimeMs > 0 && fence->getStatus() == Fence::Status::Unsignaled) {
-        fence->wait(graceTimeMs);
-    }
-
-    return (fence->getStatus() == Fence::Status::Unsignaled);
+    const status_t status = fence->wait(graceTimeMs);
+    // This is the same as Fence::Status::Unsignaled, but it saves a getStatus() call,
+    // which calls wait(0) again internally
+    return status == -ETIME;
 }
 
 nsecs_t SurfaceFlinger::previousFramePresentTime() NO_THREAD_SAFETY_ANALYSIS {
@@ -1843,184 +1785,191 @@
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
-            const nsecs_t frameStart = systemTime();
-            // calculate the expected present time once and use the cached
-            // value throughout this frame to make sure all layers are
-            // seeing this same value.
-            const nsecs_t lastExpectedPresentTime = mExpectedPresentTime.load();
-            mExpectedPresentTime = expectedVSyncTime;
-
-            // When Backpressure propagation is enabled we want to give a small grace period
-            // for the present fence to fire instead of just giving up on this frame to handle cases
-            // where present fence is just about to get signaled.
-            const int graceTimeForPresentFenceMs =
-                    (mPropagateBackpressure &&
-                     (mPropagateBackpressureClientComposition || !mHadClientComposition))
-                    ? 1
-                    : 0;
-
-            // Pending frames may trigger backpressure propagation.
-            const TracedOrdinal<bool> framePending = {"PrevFramePending",
-                                                      previousFramePending(
-                                                              graceTimeForPresentFenceMs)};
-
-            // Frame missed counts for metrics tracking.
-            // A frame is missed if the prior frame is still pending. If no longer pending,
-            // then we still count the frame as missed if the predicted present time
-            // was further in the past than when the fence actually fired.
-
-            // Add some slop to correct for drift. This should generally be
-            // smaller than a typical frame duration, but should not be so small
-            // that it reports reasonable drift as a missed frame.
-            DisplayStatInfo stats;
-            mScheduler->getDisplayStatInfo(&stats);
-            const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
-            const nsecs_t previousPresentTime = previousFramePresentTime();
-            const TracedOrdinal<bool> frameMissed =
-                    {"PrevFrameMissed",
-                     framePending ||
-                             (previousPresentTime >= 0 &&
-                              (lastExpectedPresentTime < previousPresentTime - frameMissedSlop))};
-            const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
-                                                        mHadDeviceComposition && frameMissed};
-            const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
-                                                        mHadClientComposition && frameMissed};
-
-            if (frameMissed) {
-                mFrameMissedCount++;
-                mTimeStats->incrementMissedFrames();
-                if (mMissedFrameJankCount == 0) {
-                    mMissedFrameJankStart = systemTime();
-                }
-                mMissedFrameJankCount++;
-            }
-
-            if (hwcFrameMissed) {
-                mHwcFrameMissedCount++;
-            }
-
-            if (gpuFrameMissed) {
-                mGpuFrameMissedCount++;
-            }
-
-            // If we are in the middle of a config change and the fence hasn't
-            // fired yet just wait for the next invalidate
-            if (mSetActiveConfigPending) {
-                if (framePending) {
-                    mEventQueue->invalidate();
-                    break;
-                }
-
-                // We received the present fence from the HWC, so we assume it successfully updated
-                // the config, hence we update SF.
-                mSetActiveConfigPending = false;
-                setActiveConfigInternal();
-            }
-
-            if (framePending && mPropagateBackpressure) {
-                if ((hwcFrameMissed && !gpuFrameMissed) ||
-                    mPropagateBackpressureClientComposition) {
-                    signalLayerUpdate();
-                    break;
-                }
-            }
-
-            // Our jank window is always at least 100ms since we missed a
-            // frame...
-            static constexpr nsecs_t kMinJankyDuration =
-                    std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
-            // ...but if it's larger than 1s then we missed the trace cutoff.
-            static constexpr nsecs_t kMaxJankyDuration =
-                    std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
-            // If we're in a user build then don't push any atoms
-            if (!mIsUserBuild && mMissedFrameJankCount > 0) {
-                const auto displayDevice = getDefaultDisplayDeviceLocked();
-                // Only report jank when the display is on, as displays in DOZE
-                // power mode may operate at a different frame rate than is
-                // reported in their config, which causes noticeable (but less
-                // severe) jank.
-                if (displayDevice && displayDevice->getPowerMode() == hal::PowerMode::ON) {
-                    const nsecs_t currentTime = systemTime();
-                    const nsecs_t jankDuration = currentTime - mMissedFrameJankStart;
-                    if (jankDuration > kMinJankyDuration && jankDuration < kMaxJankyDuration) {
-                        ATRACE_NAME("Jank detected");
-                        ALOGD("Detected janky event. Missed frames: %d", mMissedFrameJankCount);
-                        const int32_t jankyDurationMillis = jankDuration / (1000 * 1000);
-                        android::util::stats_write(android::util::DISPLAY_JANK_REPORTED,
-                                                   jankyDurationMillis, mMissedFrameJankCount);
-                    }
-
-                    // We either reported a jank event or we missed the trace
-                    // window, so clear counters here.
-                    if (jankDuration > kMinJankyDuration) {
-                        mMissedFrameJankCount = 0;
-                        mMissedFrameJankStart = 0;
-                    }
-                }
-            }
-
-            // Now that we're going to make it to the handleMessageTransaction()
-            // call below it's safe to call updateVrFlinger(), which will
-            // potentially trigger a display handoff.
-            updateVrFlinger();
-
-            if (mTracingEnabledChanged) {
-                mTracingEnabled = mTracing.isEnabled();
-                mTracingEnabledChanged = false;
-            }
-
-            bool refreshNeeded;
-            {
-                ConditionalLockGuard<std::mutex> lock(mTracingLock, mTracingEnabled);
-
-                refreshNeeded = handleMessageTransaction();
-                refreshNeeded |= handleMessageInvalidate();
-                if (mTracingEnabled) {
-                    mAddCompositionStateToTrace =
-                            mTracing.flagIsSetLocked(SurfaceTracing::TRACE_COMPOSITION);
-                    if (mVisibleRegionsDirty && !mAddCompositionStateToTrace) {
-                        mTracing.notifyLocked("visibleRegionsDirty");
-                    }
-                }
-            }
-
-            // Layers need to get updated (in the previous line) before we can use them for
-            // choosing the refresh rate.
-            // Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
-            // and may eventually call to ~Layer() if it holds the last reference
-            {
-                Mutex::Autolock _l(mStateLock);
-                mScheduler->chooseRefreshRateForContent();
-            }
-
-            performSetActiveConfig();
-
-            updateCursorAsync();
-            updateInputFlinger();
-
-            refreshNeeded |= mRepaintEverything;
-            if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {
-                // Signal a refresh if a transaction modified the window state,
-                // a new buffer was latched, or if HWC has requested a full
-                // repaint
-                if (mFrameStartTime <= 0) {
-                    // We should only use the time of the first invalidate
-                    // message that signals a refresh as the beginning of the
-                    // frame. Otherwise the real frame time will be
-                    // underestimated.
-                    mFrameStartTime = frameStart;
-                }
-                signalRefresh();
-            }
+            onMessageInvalidate(expectedVSyncTime);
             break;
         }
         case MessageQueue::REFRESH: {
-            handleMessageRefresh();
+            onMessageRefresh();
             break;
         }
     }
 }
 
+void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) NO_THREAD_SAFETY_ANALYSIS {
+    ATRACE_CALL();
+
+    const nsecs_t frameStart = systemTime();
+    // calculate the expected present time once and use the cached
+    // value throughout this frame to make sure all layers are
+    // seeing this same value.
+    const nsecs_t lastExpectedPresentTime = mExpectedPresentTime.load();
+    mExpectedPresentTime = expectedVSyncTime;
+
+    // When Backpressure propagation is enabled we want to give a small grace period
+    // for the present fence to fire instead of just giving up on this frame to handle cases
+    // where present fence is just about to get signaled.
+    const int graceTimeForPresentFenceMs =
+            (mPropagateBackpressure &&
+             (mPropagateBackpressureClientComposition || !mHadClientComposition))
+            ? 1
+            : 0;
+
+    // Pending frames may trigger backpressure propagation.
+    const TracedOrdinal<bool> framePending = {"PrevFramePending",
+                                              previousFramePending(graceTimeForPresentFenceMs)};
+
+    // Frame missed counts for metrics tracking.
+    // A frame is missed if the prior frame is still pending. If no longer pending,
+    // then we still count the frame as missed if the predicted present time
+    // was further in the past than when the fence actually fired.
+
+    // Add some slop to correct for drift. This should generally be
+    // smaller than a typical frame duration, but should not be so small
+    // that it reports reasonable drift as a missed frame.
+    DisplayStatInfo stats;
+    mScheduler->getDisplayStatInfo(&stats);
+    const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
+    const nsecs_t previousPresentTime = previousFramePresentTime();
+    const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
+                                             framePending ||
+                                                     (previousPresentTime >= 0 &&
+                                                      (lastExpectedPresentTime <
+                                                       previousPresentTime - frameMissedSlop))};
+    const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
+                                                mHadDeviceComposition && frameMissed};
+    const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
+                                                mHadClientComposition && frameMissed};
+
+    if (frameMissed) {
+        mFrameMissedCount++;
+        mTimeStats->incrementMissedFrames();
+        if (mMissedFrameJankCount == 0) {
+            mMissedFrameJankStart = systemTime();
+        }
+        mMissedFrameJankCount++;
+    }
+
+    if (hwcFrameMissed) {
+        mHwcFrameMissedCount++;
+    }
+
+    if (gpuFrameMissed) {
+        mGpuFrameMissedCount++;
+    }
+
+    // If we are in the middle of a config change and the fence hasn't
+    // fired yet just wait for the next invalidate
+    if (mSetActiveConfigPending) {
+        if (framePending) {
+            mEventQueue->invalidate();
+            return;
+        }
+
+        // We received the present fence from the HWC, so we assume it successfully updated
+        // the config, hence we update SF.
+        mSetActiveConfigPending = false;
+        setActiveConfigInternal();
+    }
+
+    if (framePending && mPropagateBackpressure) {
+        if ((hwcFrameMissed && !gpuFrameMissed) || mPropagateBackpressureClientComposition) {
+            signalLayerUpdate();
+            return;
+        }
+    }
+
+    // Our jank window is always at least 100ms since we missed a
+    // frame...
+    static constexpr nsecs_t kMinJankyDuration =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+    // ...but if it's larger than 1s then we missed the trace cutoff.
+    static constexpr nsecs_t kMaxJankyDuration =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+    // If we're in a user build then don't push any atoms
+    if (!mIsUserBuild && mMissedFrameJankCount > 0) {
+        const auto displayDevice = getDefaultDisplayDeviceLocked();
+        // Only report jank when the display is on, as displays in DOZE
+        // power mode may operate at a different frame rate than is
+        // reported in their config, which causes noticeable (but less
+        // severe) jank.
+        if (displayDevice && displayDevice->getPowerMode() == hal::PowerMode::ON) {
+            const nsecs_t currentTime = systemTime();
+            const nsecs_t jankDuration = currentTime - mMissedFrameJankStart;
+            if (jankDuration > kMinJankyDuration && jankDuration < kMaxJankyDuration) {
+                ATRACE_NAME("Jank detected");
+                ALOGD("Detected janky event. Missed frames: %d", mMissedFrameJankCount);
+                const int32_t jankyDurationMillis = jankDuration / (1000 * 1000);
+                {
+                    ATRACE_NAME("Pushing to statsd");
+                    android::util::stats_write(android::util::DISPLAY_JANK_REPORTED,
+                                               jankyDurationMillis, mMissedFrameJankCount);
+                }
+            }
+
+            // We either reported a jank event or we missed the trace
+            // window, so clear counters here.
+            if (jankDuration > kMinJankyDuration) {
+                mMissedFrameJankCount = 0;
+                mMissedFrameJankStart = 0;
+            }
+        }
+    }
+
+    // Now that we're going to make it to the handleMessageTransaction()
+    // call below it's safe to call updateVrFlinger(), which will
+    // potentially trigger a display handoff.
+    updateVrFlinger();
+
+    if (mTracingEnabledChanged) {
+        mTracingEnabled = mTracing.isEnabled();
+        mTracingEnabledChanged = false;
+    }
+
+    bool refreshNeeded;
+    {
+        ConditionalLockGuard<std::mutex> lock(mTracingLock, mTracingEnabled);
+
+        refreshNeeded = handleMessageTransaction();
+        refreshNeeded |= handleMessageInvalidate();
+        if (mTracingEnabled) {
+            mAddCompositionStateToTrace =
+                    mTracing.flagIsSetLocked(SurfaceTracing::TRACE_COMPOSITION);
+            if (mVisibleRegionsDirty && !mAddCompositionStateToTrace) {
+                mTracing.notifyLocked("visibleRegionsDirty");
+            }
+        }
+    }
+
+    // Layers need to get updated (in the previous line) before we can use them for
+    // choosing the refresh rate.
+    // Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
+    // and may eventually call to ~Layer() if it holds the last reference
+    {
+        Mutex::Autolock _l(mStateLock);
+        mScheduler->chooseRefreshRateForContent();
+    }
+
+    performSetActiveConfig();
+
+    updateCursorAsync();
+    updateInputFlinger();
+
+    refreshNeeded |= mRepaintEverything;
+    if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {
+        // Signal a refresh if a transaction modified the window state,
+        // a new buffer was latched, or if HWC has requested a full
+        // repaint
+        if (mFrameStartTime <= 0) {
+            // We should only use the time of the first invalidate
+            // message that signals a refresh as the beginning of the
+            // frame. Otherwise the real frame time will be
+            // underestimated.
+            mFrameStartTime = frameStart;
+        }
+        signalRefresh();
+    }
+}
+
 bool SurfaceFlinger::handleMessageTransaction() {
     ATRACE_CALL();
     uint32_t transactionFlags = peekTransactionFlags();
@@ -2043,7 +1992,7 @@
     return runHandleTransaction;
 }
 
-void SurfaceFlinger::handleMessageRefresh() {
+void SurfaceFlinger::onMessageRefresh() {
     ATRACE_CALL();
 
     mRefreshPending = false;
@@ -2073,6 +2022,7 @@
     refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
     refreshArgs.updatingGeometryThisFrame = mGeometryInvalid || mVisibleRegionsDirty;
     refreshArgs.blursAreExpensive = mBlursAreExpensive;
+    refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
 
     if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
         refreshArgs.colorTransformMatrix = mDrawingState.colorMatrix;
@@ -2141,7 +2091,6 @@
     }
 }
 
-
 bool SurfaceFlinger::handleMessageInvalidate() {
     ATRACE_CALL();
     bool refreshNeeded = handlePageFlip();
@@ -2263,8 +2212,9 @@
     }
 
     mDrawingState.traverse([&](Layer* layer) {
-        bool frameLatched = layer->onPostComposition(displayDevice, glCompositionDoneFenceTime,
-                                                     presentFenceTime, compositorTiming);
+        const bool frameLatched =
+                layer->onPostComposition(displayDevice.get(), glCompositionDoneFenceTime,
+                                         presentFenceTime, compositorTiming);
         if (frameLatched) {
             recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
         }
@@ -2336,6 +2286,9 @@
     }
     getBE().mLastSwapTime = currentTime;
 
+    // Cleanup any outstanding resources due to rendering a prior frame.
+    getRenderEngine().cleanupPostRender();
+
     {
         std::lock_guard lock(mTexturePoolMutex);
         if (mTexturePool.size() < mTexturePoolSize) {
@@ -2446,8 +2399,10 @@
                 }
 
                 DisplayDeviceState state;
-                state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId),
-                                  event.hwcDisplayId};
+                state.physical = {.id = displayId,
+                                  .type = getHwComposer().getDisplayConnectionType(displayId),
+                                  .hwcDisplayId = event.hwcDisplayId,
+                                  .deviceProductInfo = info->deviceProductInfo};
                 state.isSecure = true; // All physical displays are currently considered secure.
                 state.displayName = info->name;
 
@@ -2563,6 +2518,7 @@
         LOG_ALWAYS_FATAL_IF(!displayId);
         auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId));
         display->setActiveConfig(activeConfigId);
+        display->setDeviceProductInfo(state.physical->deviceProductInfo);
     }
 
     display->setLayerStack(state.layerStack);
@@ -2702,9 +2658,14 @@
         if (currentState.width != drawingState.width ||
             currentState.height != drawingState.height) {
             display->setDisplaySize(currentState.width, currentState.height);
+
             if (display->isPrimary()) {
                 mScheduler->onPrimaryDisplayAreaChanged(currentState.width * currentState.height);
             }
+
+            if (mRefreshRateOverlay) {
+                mRefreshRateOverlay->setViewport(display->getSize());
+            }
         }
     }
 }
@@ -2788,7 +2749,7 @@
         processDisplayHotplugEventsLocked();
     }
 
-    if (transactionFlags & (eDisplayLayerStackChanged|eDisplayTransactionNeeded)) {
+    if (transactionFlags & (eTransformHintUpdateNeeded | eDisplayTransactionNeeded)) {
         // The transform hint might have changed for some layers
         // (either because a display has changed, or because a layer
         // as changed).
@@ -2849,14 +2810,13 @@
             // could be null if there is no display available at all to get
             // the transform hint from.
             if (hintDisplay) {
-                layer->updateTransformHint(hintDisplay);
+                layer->updateTransformHint(hintDisplay->getTransformHint());
             }
 
             first = false;
         });
     }
 
-
     /*
      * Perform our own transaction if needed
      */
@@ -3161,7 +3121,8 @@
 status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
                                         const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
                                         const sp<IBinder>& parentHandle,
-                                        const sp<Layer>& parentLayer, bool addToCurrentState) {
+                                        const sp<Layer>& parentLayer, bool addToCurrentState,
+                                        uint32_t* outTransformHint) {
     // add this layer to the current state list
     {
         Mutex::Autolock _l(mStateLock);
@@ -3207,6 +3168,14 @@
                       mNumLayers.load());
             }
         }
+
+        if (const auto display = getDefaultDisplayDeviceLocked()) {
+            lbc->updateTransformHint(display->getTransformHint());
+        }
+        if (outTransformHint) {
+            *outTransformHint = lbc->getTransformHint();
+        }
+
         mLayersAdded = true;
     }
 
@@ -3216,6 +3185,13 @@
     return NO_ERROR;
 }
 
+void SurfaceFlinger::removeGraphicBufferProducerAsync(const wp<IBinder>& binder) {
+    static_cast<void>(schedule([=] {
+        Mutex::Autolock lock(mStateLock);
+        mGraphicBufferProducerList.erase(binder);
+    }));
+}
+
 uint32_t SurfaceFlinger::peekTransactionFlags() {
     return mTransactionFlags;
 }
@@ -3442,18 +3418,26 @@
                                                   : Scheduler::TransactionStart::NORMAL;
         setTransactionFlags(transactionFlags, start);
 
-        // if this is a synchronous transaction, wait for it to take effect
-        // before returning.
-        if (flags & eSynchronous) {
-            mTransactionPending = true;
-        }
         if (flags & eAnimation) {
             mAnimTransactionPending = true;
         }
-        if (mPendingInputWindowCommands.syncInputWindows) {
+
+        // if this is a synchronous transaction, wait for it to take effect
+        // before returning.
+        const bool synchronous = flags & eSynchronous;
+        const bool syncInput = inputWindowCommands.syncInputWindows;
+        if (!synchronous && !syncInput) {
+            return;
+        }
+
+        if (synchronous) {
+            mTransactionPending = true;
+        }
+        if (syncInput) {
             mPendingSyncInputWindows = true;
         }
 
+
         // applyTransactionState can be called by either the main SF thread or by
         // another process through setTransactionState.  While a given process may wish
         // to wait on synchronous transactions, the main SF thread should never
@@ -3693,7 +3677,7 @@
             mCurrentState.layersSortedByZ.add(layer);
             // we need traversal (state changed)
             // AND transaction (list changed)
-            flags |= eTransactionNeeded|eTraversalNeeded|eDisplayLayerStackChanged;
+            flags |= eTransactionNeeded | eTraversalNeeded | eTransformHintUpdateNeeded;
         }
     }
     if (what & layer_state_t::eDeferTransaction_legacy) {
@@ -3790,6 +3774,11 @@
             flags |= eTraversalNeeded;
         }
     }
+    if (what & layer_state_t::eFixedTransformHintChanged) {
+        if (layer->setFixedTransformHint(s.fixedTransformHint)) {
+            flags |= eTraversalNeeded | eTransformHintUpdateNeeded;
+        }
+    }
     // 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
     // the same transaction, then we have to make sure we reparent the children first so we do not
@@ -3877,7 +3866,8 @@
         mirrorLayer->mClonedChild = mirrorFrom->createClone();
     }
 
-    return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false);
+    return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false,
+                          nullptr /* outTransformHint */);
 }
 
 status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
@@ -3922,7 +3912,7 @@
             break;
         case ISurfaceComposerClient::eFXSurfaceBufferState:
             result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags,
-                                            std::move(metadata), handle, outTransformHint, &layer);
+                                            std::move(metadata), handle, &layer);
             break;
         case ISurfaceComposerClient::eFXSurfaceEffect:
             // check if buffer size is set for color layer.
@@ -3960,7 +3950,7 @@
 
     bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess();
     result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer,
-                            addToCurrentState);
+                            addToCurrentState, outTransformHint);
     if (result != NO_ERROR) {
         return result;
     }
@@ -4037,14 +4027,10 @@
 status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, std::string name,
                                                 uint32_t w, uint32_t h, uint32_t flags,
                                                 LayerMetadata metadata, sp<IBinder>* handle,
-                                                uint32_t* outTransformHint, sp<Layer>* outLayer) {
+                                                sp<Layer>* outLayer) {
     LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
-    args.displayDevice = getDefaultDisplayDevice();
     args.textureName = getNewTexture();
     sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
-    if (outTransformHint) {
-        *outTransformHint = layer->getTransformHint();
-    }
     *handle = layer->getHandle();
     *outLayer = layer;
 
@@ -4139,8 +4125,7 @@
 
 void SurfaceFlinger::initializeDisplays() {
     // Async since we may be called from the main thread.
-    postMessageAsync(
-            new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS { onInitializeDisplays(); }));
+    static_cast<void>(schedule([this]() NO_THREAD_SAFETY_ANALYSIS { onInitializeDisplays(); }));
 }
 
 void SurfaceFlinger::setVsyncEnabledInHWC(DisplayId displayId, hal::Vsync enabled) {
@@ -4231,7 +4216,7 @@
 }
 
 void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
-    postMessageSync(new LambdaMessage([&]() NO_THREAD_SAFETY_ANALYSIS {
+    schedule([=]() NO_THREAD_SAFETY_ANALYSIS {
         const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
             ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
@@ -4241,11 +4226,9 @@
         } else {
             setPowerModeInternal(display, static_cast<hal::PowerMode>(mode));
         }
-    }));
+    }).wait();
 }
 
-// ---------------------------------------------------------------------------
-
 status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args,
                                 bool asProto) NO_THREAD_SAFETY_ANALYSIS {
     std::string result;
@@ -4400,17 +4383,19 @@
     scheduler::RefreshRateConfigs::Policy policy = mRefreshRateConfigs->getDisplayManagerPolicy();
     StringAppendF(&result,
                   "DesiredDisplayConfigSpecs (DisplayManager): default config ID: %d"
-                  ", min: %.2f Hz, max: %.2f Hz",
-                  policy.defaultConfig.value(), policy.minRefreshRate, policy.maxRefreshRate);
+                  ", primary range: [%.2f %.2f], app request range: [%.2f %.2f]\n\n",
+                  policy.defaultConfig.value(), policy.primaryRange.min, policy.primaryRange.max,
+                  policy.appRequestRange.min, policy.appRequestRange.max);
     StringAppendF(&result, "(config override by backdoor: %s)\n\n",
                   mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
     scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy();
     if (currentPolicy != policy) {
         StringAppendF(&result,
                       "DesiredDisplayConfigSpecs (Override): default config ID: %d"
-                      ", min: %.2f Hz, max: %.2f Hz\n\n",
-                      currentPolicy.defaultConfig.value(), currentPolicy.minRefreshRate,
-                      currentPolicy.maxRefreshRate);
+                      ", primary range: [%.2f %.2f], app request range: [%.2f %.2f]\n\n",
+                      currentPolicy.defaultConfig.value(), currentPolicy.primaryRange.min,
+                      currentPolicy.primaryRange.max, currentPolicy.appRequestRange.min,
+                      currentPolicy.appRequestRange.max);
     }
 
     mScheduler->dump(mAppConnectionHandle, result);
@@ -4575,7 +4560,7 @@
 
     LayersProto layersProto;
     for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
-        layer->writeToProto(layersProto, traceFlags, display);
+        layer->writeToProto(layersProto, traceFlags, display.get());
     }
 
     return layersProto;
@@ -4606,20 +4591,21 @@
 }
 
 LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
-    LayersProto layersProto;
-    postMessageSync(new LambdaMessage([&] { layersProto = dumpDrawingStateProto(traceFlags); }));
-    return layersProto;
+    return schedule([=] { return dumpDrawingStateProto(traceFlags); }).get();
 }
 
 void SurfaceFlinger::dumpOffscreenLayers(std::string& result) {
     result.append("Offscreen Layers:\n");
-    postMessageSync(new LambdaMessage([&]() {
-        for (Layer* offscreenLayer : mOffscreenLayers) {
-            offscreenLayer->traverse(LayerVector::StateSet::Drawing, [&](Layer* layer) {
-                layer->dumpCallingUidPid(result);
-            });
-        }
-    }));
+    result.append(schedule([this] {
+                      std::string result;
+                      for (Layer* offscreenLayer : mOffscreenLayers) {
+                          offscreenLayer->traverse(LayerVector::StateSet::Drawing,
+                                                   [&](Layer* layer) {
+                                                       layer->dumpCallingUidPid(result);
+                                                   });
+                      }
+                      return result;
+                  }).get());
 }
 
 void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
@@ -4763,9 +4749,9 @@
 
         StringAppendF(&result, "Display %s HWC layers:\n", to_string(*displayId).c_str());
         Layer::miniDumpHeader(result);
-        const sp<DisplayDevice> displayDevice = display;
-        mCurrentState.traverseInZOrder(
-                [&](Layer* layer) { layer->miniDump(result, displayDevice); });
+
+        const DisplayDevice& ref = *display;
+        mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, ref); });
         result.append("\n");
     }
 
@@ -4847,7 +4833,7 @@
         case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
         case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
         case GET_DISPLAYED_CONTENT_SAMPLE:
-        case NOTIFY_POWER_HINT:
+        case NOTIFY_POWER_BOOST:
         case SET_GLOBAL_SHADOW_SETTINGS:
         case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
             // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
@@ -5242,15 +5228,15 @@
                 return NO_ERROR;
             }
             case 1034: {
-                n = data.readInt32();
-                if (n == 1 && !mRefreshRateOverlay) {
-                    mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
-                    auto& current = mRefreshRateConfigs->getCurrentRefreshRate();
-                    mRefreshRateOverlay->changeRefreshRate(current);
-                } else if (n == 0) {
-                    mRefreshRateOverlay.reset();
-                } else {
-                    reply->writeBool(mRefreshRateOverlay != nullptr);
+                switch (n = data.readInt32()) {
+                    case 0:
+                    case 1:
+                        enableRefreshRateOverlay(static_cast<bool>(n));
+                        break;
+                    default: {
+                        Mutex::Autolock lock(mStateLock);
+                        reply->writeBool(mRefreshRateOverlay != nullptr);
+                    }
                 }
                 return NO_ERROR;
             }
@@ -5298,29 +5284,26 @@
 void SurfaceFlinger::kernelTimerChanged(bool expired) {
     static bool updateOverlay =
             property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true);
-    if (!updateOverlay || !mRefreshRateOverlay) return;
+    if (!updateOverlay) return;
+    if (Mutex::Autolock lock(mStateLock); !mRefreshRateOverlay) return;
 
     // Update the overlay on the main thread to avoid race conditions with
     // mRefreshRateConfigs->getCurrentRefreshRate()
-    postMessageAsync(new LambdaMessage([this, expired]() NO_THREAD_SAFETY_ANALYSIS {
-        if (mRefreshRateOverlay) {
+    static_cast<void>(schedule([=] {
+        const auto desiredActiveConfig = getDesiredActiveConfig();
+        const auto& current = desiredActiveConfig
+                ? mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig->configId)
+                : mRefreshRateConfigs->getCurrentRefreshRate();
+        const auto& min = mRefreshRateConfigs->getMinRefreshRate();
+
+        if (current != min) {
             const auto kernelTimerEnabled = property_get_bool(KERNEL_IDLE_TIMER_PROP, false);
             const bool timerExpired = kernelTimerEnabled && expired;
-            const auto& current = [this]() -> const RefreshRate& {
-                std::lock_guard<std::mutex> lock(mActiveConfigLock);
-                if (mDesiredActiveConfigChanged) {
-                    return mRefreshRateConfigs->getRefreshRateFromConfigId(
-                            mDesiredActiveConfig.configId);
-                }
 
-                return mRefreshRateConfigs->getCurrentRefreshRate();
-            }();
-            const auto& min = mRefreshRateConfigs->getMinRefreshRate();
-
-            if (current != min) {
+            if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
                 mRefreshRateOverlay->changeRefreshRate(timerExpired ? min : current);
-                mEventQueue->invalidate();
             }
+            mEventQueue->invalidate();
         }
     }));
 }
@@ -5686,60 +5669,33 @@
                                              const sp<GraphicBuffer>& buffer,
                                              bool useIdentityTransform, bool regionSampling,
                                              bool& outCapturedSecureLayers) {
-    // This mutex protects syncFd and captureResult for communication of the return values from the
-    // main thread back to this Binder thread
-    std::mutex captureMutex;
-    std::condition_variable captureCondition;
-    std::unique_lock<std::mutex> captureLock(captureMutex);
-    int syncFd = -1;
-    std::optional<status_t> captureResult;
-
     const int uid = IPCThreadState::self()->getCallingUid();
     const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
 
-    sp<LambdaMessage> message = new LambdaMessage([&] {
-        // If there is a refresh pending, bug out early and tell the binder thread to try again
-        // after the refresh.
-        if (mRefreshPending) {
-            ATRACE_NAME("Skipping screenshot for now");
-            std::unique_lock<std::mutex> captureLock(captureMutex);
-            captureResult = std::make_optional<status_t>(EAGAIN);
-            captureCondition.notify_one();
-            return;
-        }
+    status_t result;
+    int syncFd;
 
-        status_t result = NO_ERROR;
-        int fd = -1;
-        {
-            Mutex::Autolock _l(mStateLock);
-            renderArea.render([&] {
-                result = captureScreenImplLocked(renderArea, traverseLayers, buffer.get(),
-                                                 useIdentityTransform, forSystem, &fd,
-                                                 regionSampling, outCapturedSecureLayers);
-            });
-        }
+    do {
+        std::tie(result, syncFd) =
+                schedule([&] {
+                    if (mRefreshPending) {
+                        ATRACE_NAME("Skipping screenshot for now");
+                        return std::make_pair(EAGAIN, -1);
+                    }
 
-        {
-            std::unique_lock<std::mutex> captureLock(captureMutex);
-            syncFd = fd;
-            captureResult = std::make_optional<status_t>(result);
-            captureCondition.notify_one();
-        }
-    });
+                    status_t result = NO_ERROR;
+                    int fd = -1;
 
-    status_t result = postMessageAsync(message);
-    if (result == NO_ERROR) {
-        captureCondition.wait(captureLock, [&] { return captureResult; });
-        while (*captureResult == EAGAIN) {
-            captureResult.reset();
-            result = postMessageAsync(message);
-            if (result != NO_ERROR) {
-                return result;
-            }
-            captureCondition.wait(captureLock, [&] { return captureResult; });
-        }
-        result = *captureResult;
-    }
+                    Mutex::Autolock lock(mStateLock);
+                    renderArea.render([&] {
+                        result = captureScreenImplLocked(renderArea, traverseLayers, buffer.get(),
+                                                         useIdentityTransform, forSystem, &fd,
+                                                         regionSampling, outCapturedSecureLayers);
+                    });
+
+                    return std::make_pair(result, fd);
+                }).get();
+    } while (result == EAGAIN);
 
     if (result == NO_ERROR) {
         sync_wait(syncFd, -1);
@@ -5785,6 +5741,7 @@
     fillLayer.alpha = half(alpha);
     clientCompositionLayers.push_back(fillLayer);
 
+    const auto display = renderArea.getDisplayDevice();
     std::vector<Layer*> renderedLayers;
     Region clearRegion = Region::INVALID_REGION;
     traverseLayers([&](Layer* layer) {
@@ -5793,7 +5750,7 @@
         compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
                 clip,
                 useIdentityTransform,
-                layer->needsFilteringForScreenshots(renderArea.getDisplayDevice(), transform) ||
+                layer->needsFilteringForScreenshots(display.get(), transform) ||
                         renderArea.needsFiltering(),
                 renderArea.isSecure(),
                 supportProtectedContent,
@@ -5972,9 +5929,11 @@
     }
     scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy();
 
-    ALOGV("Setting desired display config specs: defaultConfig: %d min: %.f max: %.f",
-          currentPolicy.defaultConfig.value(), currentPolicy.minRefreshRate,
-          currentPolicy.maxRefreshRate);
+    ALOGV("Setting desired display config specs: defaultConfig: %d primaryRange: [%.0f %.0f]"
+          " expandedRange: [%.0f %.0f]",
+          currentPolicy.defaultConfig.value(), currentPolicy.primaryRange.min,
+          currentPolicy.primaryRange.max, currentPolicy.appRequestRange.min,
+          currentPolicy.appRequestRange.max);
 
     // TODO(b/140204874): This hack triggers a notification that something has changed, so
     // that listeners that care about a change in allowed configs can get the notification.
@@ -6007,45 +5966,50 @@
 }
 
 status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                      int32_t defaultConfig, float minRefreshRate,
-                                                      float maxRefreshRate) {
+                                                      int32_t defaultConfig,
+                                                      float primaryRefreshRateMin,
+                                                      float primaryRefreshRateMax,
+                                                      float appRequestRefreshRateMin,
+                                                      float appRequestRefreshRateMax) {
     ATRACE_CALL();
 
     if (!displayToken) {
         return BAD_VALUE;
     }
 
-    status_t result = NAME_NOT_FOUND;
-
-    postMessageSync(new LambdaMessage([&]() {
+    auto future = schedule([=]() -> status_t {
         const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
             ALOGE("Attempt to set desired display configs for invalid display token %p",
                   displayToken.get());
+            return NAME_NOT_FOUND;
         } else if (display->isVirtual()) {
             ALOGW("Attempt to set desired display configs for virtual display");
-            result = INVALID_OPERATION;
+            return INVALID_OPERATION;
         } else {
-            result = setDesiredDisplayConfigSpecsInternal(display,
-                                                          scheduler::RefreshRateConfigs::
-                                                                  Policy{HwcConfigIndexType(
-                                                                                 defaultConfig),
-                                                                         minRefreshRate,
-                                                                         maxRefreshRate},
-                                                          /*overridePolicy=*/false);
-        }
-    }));
+            using Policy = scheduler::RefreshRateConfigs::Policy;
+            const Policy policy{HwcConfigIndexType(defaultConfig),
+                                {primaryRefreshRateMin, primaryRefreshRateMax},
+                                {appRequestRefreshRateMin, appRequestRefreshRateMax}};
+            constexpr bool kOverridePolicy = false;
 
-    return result;
+            return setDesiredDisplayConfigSpecsInternal(display, policy, kOverridePolicy);
+        }
+    });
+
+    return future.get();
 }
 
 status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
                                                       int32_t* outDefaultConfig,
-                                                      float* outMinRefreshRate,
-                                                      float* outMaxRefreshRate) {
+                                                      float* outPrimaryRefreshRateMin,
+                                                      float* outPrimaryRefreshRateMax,
+                                                      float* outAppRequestRefreshRateMin,
+                                                      float* outAppRequestRefreshRateMax) {
     ATRACE_CALL();
 
-    if (!displayToken || !outDefaultConfig || !outMinRefreshRate || !outMaxRefreshRate) {
+    if (!displayToken || !outDefaultConfig || !outPrimaryRefreshRateMin ||
+        !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) {
         return BAD_VALUE;
     }
 
@@ -6059,8 +6023,10 @@
         scheduler::RefreshRateConfigs::Policy policy =
                 mRefreshRateConfigs->getDisplayManagerPolicy();
         *outDefaultConfig = policy.defaultConfig.value();
-        *outMinRefreshRate = policy.minRefreshRate;
-        *outMaxRefreshRate = policy.maxRefreshRate;
+        *outPrimaryRefreshRateMin = policy.primaryRange.min;
+        *outPrimaryRefreshRateMax = policy.primaryRange.max;
+        *outAppRequestRefreshRateMin = policy.appRequestRange.min;
+        *outAppRequestRefreshRateMax = policy.appRequestRange.max;
         return NO_ERROR;
     } else if (display->isVirtual()) {
         return INVALID_OPERATION;
@@ -6070,8 +6036,10 @@
 
         *outDefaultConfig = getHwComposer().getActiveConfigIndex(*displayId);
         auto vsyncPeriod = getHwComposer().getActiveConfig(*displayId)->getVsyncPeriod();
-        *outMinRefreshRate = 1e9f / vsyncPeriod;
-        *outMaxRefreshRate = 1e9f / vsyncPeriod;
+        *outPrimaryRefreshRateMin = 1e9f / vsyncPeriod;
+        *outPrimaryRefreshRateMax = 1e9f / vsyncPeriod;
+        *outAppRequestRefreshRateMin = 1e9f / vsyncPeriod;
+        *outAppRequestRefreshRateMax = 1e9f / vsyncPeriod;
         return NO_ERROR;
     }
 }
@@ -6165,7 +6133,7 @@
         return BAD_VALUE;
     }
 
-    postMessageAsync(new LambdaMessage([=]() NO_THREAD_SAFETY_ANALYSIS {
+    static_cast<void>(schedule([=]() NO_THREAD_SAFETY_ANALYSIS {
         Mutex::Autolock lock(mStateLock);
         if (authenticateSurfaceTextureLocked(surface)) {
             sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
@@ -6188,8 +6156,11 @@
     if (!outToken) {
         return BAD_VALUE;
     }
-    status_t result = NO_ERROR;
-    postMessageSync(new LambdaMessage([&]() {
+
+    auto future = schedule([this] {
+        status_t result = NO_ERROR;
+        sp<IBinder> token;
+
         if (mFrameRateFlexibilityTokenCount == 0) {
             // |mStateLock| not needed as we are on the main thread
             const auto display = getDefaultDisplayDeviceLocked();
@@ -6203,8 +6174,8 @@
             overridePolicy.defaultConfig =
                     mRefreshRateConfigs->getDisplayManagerPolicy().defaultConfig;
             overridePolicy.allowGroupSwitching = true;
-            result = setDesiredDisplayConfigSpecsInternal(display, overridePolicy,
-                                                          /*overridePolicy=*/true);
+            constexpr bool kOverridePolicy = true;
+            result = setDesiredDisplayConfigSpecsInternal(display, overridePolicy, kOverridePolicy);
         }
 
         if (result == NO_ERROR) {
@@ -6221,17 +6192,22 @@
             //   2. The frame rate flexibility token is acquired/released only by CTS tests, so even
             //   if condition 1 were changed, the problem would only show up when running CTS tests,
             //   not on end user devices, so we could spot it and fix it without serious impact.
-            *outToken = new FrameRateFlexibilityToken(
+            token = new FrameRateFlexibilityToken(
                     [this]() { onFrameRateFlexibilityTokenReleased(); });
             ALOGD("Frame rate flexibility token acquired. count=%d",
                   mFrameRateFlexibilityTokenCount);
         }
-    }));
+
+        return std::make_pair(result, token);
+    });
+
+    status_t result;
+    std::tie(result, *outToken) = future.get();
     return result;
 }
 
 void SurfaceFlinger::onFrameRateFlexibilityTokenReleased() {
-    postMessageAsync(new LambdaMessage([&]() {
+    static_cast<void>(schedule([this] {
         LOG_ALWAYS_FATAL_IF(mFrameRateFlexibilityTokenCount == 0,
                             "Failed tracking frame rate flexibility tokens");
         mFrameRateFlexibilityTokenCount--;
@@ -6239,13 +6215,36 @@
         if (mFrameRateFlexibilityTokenCount == 0) {
             // |mStateLock| not needed as we are on the main thread
             const auto display = getDefaultDisplayDeviceLocked();
-            status_t result =
-                    setDesiredDisplayConfigSpecsInternal(display, {}, /*overridePolicy=*/true);
+            constexpr bool kOverridePolicy = true;
+            status_t result = setDesiredDisplayConfigSpecsInternal(display, {}, kOverridePolicy);
             LOG_ALWAYS_FATAL_IF(result < 0, "Failed releasing frame rate flexibility token");
         }
     }));
 }
 
+void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
+    static_cast<void>(schedule([=] {
+        std::unique_ptr<RefreshRateOverlay> overlay;
+        if (enable) {
+            overlay = std::make_unique<RefreshRateOverlay>(*this);
+        }
+
+        {
+            Mutex::Autolock lock(mStateLock);
+
+            // Destroy the layer of the current overlay, if any, outside the lock.
+            mRefreshRateOverlay.swap(overlay);
+            if (!mRefreshRateOverlay) return;
+
+            if (const auto display = getDefaultDisplayDeviceLocked()) {
+                mRefreshRateOverlay->setViewport(display->getSize());
+            }
+
+            mRefreshRateOverlay->changeRefreshRate(mRefreshRateConfigs->getCurrentRefreshRate());
+        }
+    }));
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index f26a62b..481f350 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -68,6 +68,7 @@
 #include <atomic>
 #include <cstdint>
 #include <functional>
+#include <future>
 #include <map>
 #include <memory>
 #include <mutex>
@@ -116,7 +117,7 @@
     eTransactionNeeded = 0x01,
     eTraversalNeeded = 0x02,
     eDisplayTransactionNeeded = 0x04,
-    eDisplayLayerStackChanged = 0x08,
+    eTransformHintUpdateNeeded = 0x08,
     eTransactionFlushNeeded = 0x10,
     eTransactionMask = 0x1f,
 };
@@ -276,12 +277,9 @@
     // starts SurfaceFlinger main loop in the current thread
     void run() ANDROID_API;
 
-    // post an asynchronous message to the main thread
-    status_t postMessageAsync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0);
-
-    // post a synchronous message to the main thread
-    status_t postMessageSync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0)
-            EXCLUDES(mStateLock);
+    // Schedule an asynchronous or synchronous task on the main thread.
+    template <typename F, typename T = std::invoke_result_t<F>>
+    [[nodiscard]] std::future<T> schedule(F&&);
 
     // force full composition on all displays
     void repaintEverything();
@@ -495,14 +493,19 @@
                                        const sp<IRegionSamplingListener>& listener) override;
     status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
     status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t displayModeId,
-                                          float minRefreshRate, float maxRefreshRate) override;
+                                          float primaryRefreshRateMin, float primaryRefreshRateMax,
+                                          float appRequestRefreshRateMin,
+                                          float appRequestRefreshRateMax) override;
     status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                          int32_t* outDefaultConfig, float* outMinRefreshRate,
-                                          float* outMaxRefreshRate) override;
+                                          int32_t* outDefaultConfig,
+                                          float* outPrimaryRefreshRateMin,
+                                          float* outPrimaryRefreshRateMax,
+                                          float* outAppRequestRefreshRateMin,
+                                          float* outAppRequestRefreshRateMax) override;
     status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
                                          bool* outSupport) const override;
     status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override;
-    status_t notifyPowerHint(int32_t hintId) override;
+    status_t notifyPowerBoost(int32_t boostId) override;
     status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                      float lightPosY, float lightPosZ, float lightRadius) override;
     status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
@@ -542,7 +545,6 @@
     /* ------------------------------------------------------------------------
      * Message handling
      */
-    void waitForEvent();
     // Can only be called from the main thread or with mStateLock held
     void signalTransaction();
     // Can only be called from the main thread or with mStateLock held
@@ -583,14 +585,20 @@
             const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy)
             EXCLUDES(mStateLock);
 
+    // Handle the INVALIDATE message queue event, latching new buffers and applying
+    // incoming transactions
+    void onMessageInvalidate(nsecs_t expectedVSyncTime);
+
     // Returns whether the transaction actually modified any state
     bool handleMessageTransaction();
 
+    // Handle the REFRESH message queue event, sending the current frame down to RenderEngine and
+    // the Composer HAL for presentation
+    void onMessageRefresh();
+
     // Returns whether a new buffer has been latched (see handlePageFlip())
     bool handleMessageInvalidate();
 
-    void handleMessageRefresh();
-
     void handleTransaction(uint32_t transactionFlags);
     void handleTransactionLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
 
@@ -667,8 +675,7 @@
 
     status_t createBufferStateLayer(const sp<Client>& client, std::string name, uint32_t w,
                                     uint32_t h, uint32_t flags, LayerMetadata metadata,
-                                    sp<IBinder>* outHandle, uint32_t* outTransformHint,
-                                    sp<Layer>* outLayer);
+                                    sp<IBinder>* outHandle, sp<Layer>* outLayer);
 
     status_t createEffectLayer(const sp<Client>& client, std::string name, uint32_t w, uint32_t h,
                                uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle,
@@ -693,7 +700,7 @@
     status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
                             const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
                             const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
-                            bool addToCurrentState);
+                            bool addToCurrentState, uint32_t* outTransformHint);
 
     // Traverse through all the layers and compute and cache its bounds.
     void computeLayerBounds();
@@ -766,7 +773,7 @@
         return nullptr;
     }
 
-    std::optional<DeviceProductInfo> getDeviceProductInfoLocked(const DisplayDevice&) const;
+    std::optional<DeviceProductInfo> getDeviceProductInfoLocked(DisplayId) const;
 
     // mark a region of a layer stack dirty. this updates the dirty
     // region of all screens presenting this layer stack.
@@ -821,13 +828,13 @@
             const DisplayDeviceState& state,
             const sp<compositionengine::DisplaySurface>& displaySurface,
             const sp<IGraphicBufferProducer>& producer);
-    void processDisplayChangesLocked();
+    void processDisplayChangesLocked() REQUIRES(mStateLock);
     void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState& state);
     void processDisplayRemoved(const wp<IBinder>& displayToken);
     void processDisplayChanged(const wp<IBinder>& displayToken,
                                const DisplayDeviceState& currentState,
-                               const DisplayDeviceState& drawingState);
-    void processDisplayHotplugEventsLocked();
+                               const DisplayDeviceState& drawingState) REQUIRES(mStateLock);
+    void processDisplayHotplugEventsLocked() REQUIRES(mStateLock);
 
     void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
 
@@ -1003,6 +1010,8 @@
     size_t mGraphicBufferProducerListSizeLogThreshold =
             static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS));
 
+    void removeGraphicBufferProducerAsync(const wp<IBinder>&);
+
     // protected by mStateLock (but we could use another lock)
     bool mLayersRemoved = false;
     bool mLayersAdded = false;
@@ -1210,6 +1219,12 @@
      * Misc
      */
 
+    std::optional<ActiveConfigInfo> getDesiredActiveConfig() EXCLUDES(mActiveConfigLock) {
+        std::lock_guard<std::mutex> lock(mActiveConfigLock);
+        if (mDesiredActiveConfigChanged) return mDesiredActiveConfig;
+        return std::nullopt;
+    }
+
     std::mutex mActiveConfigLock;
     // This bit is set once we start setting the config. We read from this bit during the
     // process. If at the end, this bit is different than mDesiredActiveConfig, we restart
@@ -1252,7 +1267,8 @@
     // This should only be accessed on the main thread.
     nsecs_t mFrameStartTime = 0;
 
-    std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
+    void enableRefreshRateOverlay(bool enable);
+    std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay GUARDED_BY(mStateLock);
 
     // Flag used to set override allowed display configs from backdoor
     bool mDebugDisplayConfigSetByBackdoor = false;
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
index c145a39..4e7f67d 100644
--- a/services/surfaceflinger/TracedOrdinal.h
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -16,10 +16,13 @@
 
 #pragma once
 #include <android-base/stringprintf.h>
+#include <cutils/compiler.h>
 #include <utils/Trace.h>
 #include <cmath>
 #include <string>
 
+namespace android {
+
 template <typename T>
 class TracedOrdinal {
 public:
@@ -27,9 +30,8 @@
                   "Type is not supported. Please test it with systrace before adding "
                   "it to the list.");
 
-    TracedOrdinal(const std::string& name, T initialValue)
-          : mName(name),
-            mNameNegative(android::base::StringPrintf("%sNegative", name.c_str())),
+    TracedOrdinal(std::string name, T initialValue)
+          : mName(std::move(name)),
             mHasGoneNegative(std::signbit(initialValue)),
             mData(initialValue) {
         trace();
@@ -46,6 +48,14 @@
 
 private:
     void trace() {
+        if (CC_LIKELY(!ATRACE_ENABLED())) {
+            return;
+        }
+
+        if (mNameNegative.empty()) {
+            mNameNegative = base::StringPrintf("%sNegative", mName.c_str());
+        }
+
         if (!std::signbit(mData)) {
             ATRACE_INT64(mName.c_str(), int64_t(mData));
             if (mHasGoneNegative) {
@@ -58,7 +68,9 @@
     }
 
     const std::string mName;
-    const std::string mNameNegative;
+    std::string mNameNegative;
     bool mHasGoneNegative;
     T mData;
 };
+
+} // namespace android
diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc
index d3942e8..575e70d 100644
--- a/services/surfaceflinger/surfaceflinger.rc
+++ b/services/surfaceflinger/surfaceflinger.rc
@@ -4,7 +4,7 @@
     group graphics drmrpc readproc
     capabilities SYS_NICE
     onrestart restart zygote
-    writepid /dev/stune/foreground/tasks
+    task_profiles HighPerformance
     socket pdx/system/vr/display/client     stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
     socket pdx/system/vr/display/manager    stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0
     socket pdx/system/vr/display/vsync      stream 0666 system graphics u:object_r:pdx_display_vsync_endpoint_socket:s0
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 507d28b..c136708 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -215,14 +215,21 @@
 TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) {
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     int32_t defaultConfig;
-    float minFps;
-    float maxFps;
-    status_t res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(display, &defaultConfig,
-                                                                       &minFps, &maxFps);
+    float primaryFpsMin;
+    float primaryFpsMax;
+    float appRequestFpsMin;
+    float appRequestFpsMax;
+    status_t res =
+            SurfaceComposerClient::getDesiredDisplayConfigSpecs(display, &defaultConfig,
+                                                                &primaryFpsMin, &primaryFpsMax,
+                                                                &appRequestFpsMin,
+                                                                &appRequestFpsMax);
     ASSERT_EQ(res, NO_ERROR);
     std::function<status_t()> condition = [=]() {
-        return SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, defaultConfig, minFps,
-                                                                   maxFps);
+        return SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, defaultConfig,
+                                                                   primaryFpsMin, primaryFpsMax,
+                                                                   appRequestFpsMin,
+                                                                   appRequestFpsMax);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 0ed2ffb..debfe83 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -39,11 +39,16 @@
 
 TEST_F(RefreshRateRangeTest, setAllConfigs) {
     int32_t initialDefaultConfig;
-    float initialMin;
-    float initialMax;
+    float initialPrimaryMin;
+    float initialPrimaryMax;
+    float initialAppRequestMin;
+    float initialAppRequestMax;
     status_t res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken,
                                                                        &initialDefaultConfig,
-                                                                       &initialMin, &initialMax);
+                                                                       &initialPrimaryMin,
+                                                                       &initialPrimaryMax,
+                                                                       &initialAppRequestMin,
+                                                                       &initialAppRequestMax);
     ASSERT_EQ(res, NO_ERROR);
 
     Vector<DisplayConfig> configs;
@@ -53,22 +58,33 @@
     for (size_t i = 0; i < configs.size(); i++) {
         res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, i,
                                                                   configs[i].refreshRate,
+                                                                  configs[i].refreshRate,
+                                                                  configs[i].refreshRate,
                                                                   configs[i].refreshRate);
         ASSERT_EQ(res, NO_ERROR);
 
         int defaultConfig;
-        float minRefreshRate;
-        float maxRefreshRate;
+        float primaryRefreshRateMin;
+        float primaryRefreshRateMax;
+        float appRequestRefreshRateMin;
+        float appRequestRefreshRateMax;
         res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
-                                                                  &minRefreshRate, &maxRefreshRate);
+                                                                  &primaryRefreshRateMin,
+                                                                  &primaryRefreshRateMax,
+                                                                  &appRequestRefreshRateMin,
+                                                                  &appRequestRefreshRateMax);
         ASSERT_EQ(res, NO_ERROR);
         ASSERT_EQ(defaultConfig, i);
-        ASSERT_EQ(minRefreshRate, configs[i].refreshRate);
-        ASSERT_EQ(maxRefreshRate, configs[i].refreshRate);
+        ASSERT_EQ(primaryRefreshRateMin, configs[i].refreshRate);
+        ASSERT_EQ(primaryRefreshRateMax, configs[i].refreshRate);
+        ASSERT_EQ(appRequestRefreshRateMin, configs[i].refreshRate);
+        ASSERT_EQ(appRequestRefreshRateMax, configs[i].refreshRate);
     }
 
     res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, initialDefaultConfig,
-                                                              initialMin, initialMax);
+                                                              initialPrimaryMin, initialPrimaryMax,
+                                                              initialAppRequestMin,
+                                                              initialAppRequestMax);
     ASSERT_EQ(res, NO_ERROR);
 }
 
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 32c58ad..a03fd89 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -443,6 +443,8 @@
                 EXPECT_EQ(NO_ERROR,
                           SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
                                                                               config.refreshRate,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate,
                                                                               config.refreshRate));
                 waitForDisplayTransaction();
                 EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
@@ -547,6 +549,8 @@
                 EXPECT_EQ(NO_ERROR,
                           SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
                                                                               config.refreshRate,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate,
                                                                               config.refreshRate));
                 waitForDisplayTransaction();
                 EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
@@ -659,9 +663,11 @@
             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].refreshRate,
-                                                                              configs[i].refreshRate));
+                          SurfaceComposerClient::
+                                  setDesiredDisplayConfigSpecs(display, i, configs[i].refreshRate,
+                                                               configs[i].refreshRate,
+                                                               configs[i].refreshRate,
+                                                               configs[i].refreshRate));
                 waitForDisplayTransaction();
                 EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
                 break;
@@ -706,6 +712,8 @@
                 EXPECT_EQ(NO_ERROR,
                           SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
                                                                               config.refreshRate,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate,
                                                                               config.refreshRate));
                 waitForDisplayTransaction();
                 EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
@@ -751,6 +759,8 @@
                 EXPECT_EQ(NO_ERROR,
                           SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
                                                                               config.refreshRate,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate,
                                                                               config.refreshRate));
                 waitForDisplayTransaction();
                 EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 2f2fb20..7574ff1 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -48,6 +48,7 @@
         "LayerHistoryTestV2.cpp",
         "LayerMetadataTest.cpp",
         "PhaseOffsetsTest.cpp",
+        "PromiseTest.cpp",
         "SchedulerTest.cpp",
         "SchedulerUtilsTest.cpp",
         "SetFrameRateTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 5066a83..95e2ef5 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -542,7 +542,7 @@
 
         EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
         enqueueBuffer(test, layer);
-        Mock::VerifyAndClear(test->mMessageQueue);
+        Mock::VerifyAndClearExpectations(test->mMessageQueue);
 
         EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
         bool ignoredRecomputeVisibleRegions;
@@ -834,7 +834,7 @@
 struct BaseLayerVariant {
     template <typename L, typename F>
     static sp<L> createLayerWithFactory(CompositionTest* test, F factory) {
-        EXPECT_CALL(*test->mMessageQueue, postMessage(_, 0)).Times(0);
+        EXPECT_CALL(*test->mMessageQueue, postMessage(_)).Times(0);
 
         sp<L> layer = factory();
 
@@ -843,7 +843,7 @@
 
         Mock::VerifyAndClear(test->mComposer);
         Mock::VerifyAndClear(test->mRenderEngine);
-        Mock::VerifyAndClear(test->mMessageQueue);
+        Mock::VerifyAndClearExpectations(test->mMessageQueue);
 
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
         layerDrawingState.layerStack = DEFAULT_LAYER_STACK;
@@ -944,7 +944,7 @@
     }
 
     static void cleanupInjectedLayers(CompositionTest* test) {
-        EXPECT_CALL(*test->mMessageQueue, postMessage(_, 0)).Times(1);
+        EXPECT_CALL(*test->mMessageQueue, postMessage(_)).Times(1);
         Base::cleanupInjectedLayers(test);
     }
 
@@ -1073,7 +1073,8 @@
 
 struct ForcedClientCompositionResultVariant : public CompositionResultBaseVariant {
     static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
-        const auto outputLayer = layer->findOutputLayerForDisplay(test->mDisplay);
+        const auto outputLayer =
+                TestableSurfaceFlinger::findOutputLayerForDisplay(layer, test->mDisplay);
         LOG_FATAL_IF(!outputLayer);
         outputLayer->editState().forceClientComposition = true;
     }
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index ce5f35c..9130b04 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -23,6 +23,7 @@
 
 #include <type_traits>
 
+#include <android/hardware/power/Boost.h>
 #include <compositionengine/Display.h>
 #include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/impl/Display.h>
@@ -57,6 +58,8 @@
 
 namespace hal = android::hardware::graphics::composer::hal;
 
+using android::hardware::power::Boost;
+
 using testing::_;
 using testing::AnyNumber;
 using testing::DoAll;
@@ -1347,6 +1350,30 @@
 }
 
 /* ------------------------------------------------------------------------
+ * SurfaceFlinger::notifyPowerBoost
+ */
+
+TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
+    mFlinger.scheduler()->replaceTouchTimer(100);
+    std::this_thread::sleep_for(10ms);                  // wait for callback to be triggered
+    EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
+
+    std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+    EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+    EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT)));
+    std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered
+    EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+    std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+    EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+
+    EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION)));
+    std::this_thread::sleep_for(10ms); // wait for callback to be triggered.
+    EXPECT_TRUE(mFlinger.scheduler()->isTouchActive());
+}
+
+/* ------------------------------------------------------------------------
  * DisplayDevice::GetBestColorMode
  */
 class GetBestColorModeTest : public DisplayTransactionTest {
@@ -1794,7 +1821,7 @@
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
-        state.physical = {*displayId, *connectionType, *hwcDisplayId};
+        state.physical = {.id = *displayId, .type = *connectionType, .hwcDisplayId = *hwcDisplayId};
     }
 
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
@@ -1970,7 +1997,9 @@
         ASSERT_TRUE(displayId);
         const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
         ASSERT_TRUE(hwcDisplayId);
-        expectedPhysical = {*displayId, *connectionType, *hwcDisplayId};
+        expectedPhysical = {.id = *displayId,
+                            .type = *connectionType,
+                            .hwcDisplayId = *hwcDisplayId};
     }
 
     // The display should have been set up in the current display state
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index 6fca673..431cf0f 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -512,5 +512,34 @@
     EXPECT_EQ(1, frequentLayerCount(time));
 }
 
+TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) {
+    auto explicitVisiblelayer = createLayer();
+    auto explicitInvisiblelayer = createLayer();
+
+    EXPECT_CALL(*explicitVisiblelayer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*explicitVisiblelayer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(
+                    Layer::FrameRate(60.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+    EXPECT_CALL(*explicitInvisiblelayer, isVisible()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*explicitInvisiblelayer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(
+                    Layer::FrameRate(90.0f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+    nsecs_t time = systemTime();
+
+    // Post a buffer to the layers to make them active
+    history().record(explicitVisiblelayer.get(), time, time);
+    history().record(explicitInvisiblelayer.get(), time, time);
+
+    EXPECT_EQ(2, layerCount());
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(60.0f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+}
+
 } // namespace
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
index ce5993a..0b74682 100644
--- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -145,7 +145,9 @@
 
 class TestablePhaseOffsets : public impl::PhaseOffsets {
 public:
-    TestablePhaseOffsets() : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 10'000'000) {}
+    TestablePhaseOffsets()
+          : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 1'000'000, 1'000'000, {}, {}, {}, {},
+                               10'000'000) {}
 };
 
 class PhaseOffsetsTest : public testing::Test {
diff --git a/services/surfaceflinger/tests/unittests/PromiseTest.cpp b/services/surfaceflinger/tests/unittests/PromiseTest.cpp
new file mode 100644
index 0000000..e4dc1fe
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/PromiseTest.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 <algorithm>
+#include <future>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "Promise.h"
+
+namespace android {
+namespace {
+
+using Bytes = std::vector<uint8_t>;
+
+Bytes decrement(Bytes bytes) {
+    std::transform(bytes.begin(), bytes.end(), bytes.begin(), [](auto b) { return b - 1; });
+    return bytes;
+}
+
+} // namespace
+
+TEST(PromiseTest, yield) {
+    EXPECT_EQ(42, promise::yield(42).get());
+
+    auto ptr = std::make_unique<char>('!');
+    auto future = promise::yield(std::move(ptr));
+    EXPECT_EQ('!', *future.get());
+}
+
+TEST(PromiseTest, chain) {
+    std::packaged_task<const char*()> fetchString([] { return "ifmmp-"; });
+
+    std::packaged_task<Bytes(std::string)> appendString([](std::string str) {
+        str += "!xpsme";
+        return Bytes{str.begin(), str.end()};
+    });
+
+    std::packaged_task<std::future<Bytes>(Bytes)> decrementBytes(
+            [](Bytes bytes) { return promise::defer(decrement, std::move(bytes)); });
+
+    auto fetch = fetchString.get_future();
+    std::thread fetchThread(std::move(fetchString));
+
+    std::thread appendThread, decrementThread;
+
+    EXPECT_EQ("hello, world",
+              promise::chain(std::move(fetch))
+                      .then([](const char* str) { return std::string(str); })
+                      .then([&](std::string str) {
+                          auto append = appendString.get_future();
+                          appendThread = std::thread(std::move(appendString), std::move(str));
+                          return append;
+                      })
+                      .then([&](Bytes bytes) {
+                          auto decrement = decrementBytes.get_future();
+                          decrementThread = std::thread(std::move(decrementBytes),
+                                                        std::move(bytes));
+                          return decrement;
+                      })
+                      .then([](std::future<Bytes> bytes) { return bytes; })
+                      .then([](const Bytes& bytes) {
+                          return std::string(bytes.begin(), bytes.end());
+                      })
+                      .get());
+
+    fetchThread.join();
+    appendThread.join();
+    decrementThread.join();
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 2ceb89c..2b168b2 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -166,8 +166,8 @@
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60OnlyConfigDevice,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
-    ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HwcConfigIndexType(10), 60, 60}), 0);
-    ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, 20, 40}), 0);
+    ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HwcConfigIndexType(10), {60, 60}}), 0);
+    ASSERT_LT(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {20, 40}}), 0);
 }
 
 TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
@@ -201,7 +201,7 @@
     ASSERT_EQ(mExpected60Config, minRate60);
     ASSERT_EQ(mExpected60Config, performanceRate60);
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, 60, 90}), 0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60, 90}}), 0);
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
 
     const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
@@ -226,7 +226,7 @@
     ASSERT_EQ(mExpected60Config, minRate60);
     ASSERT_EQ(mExpected60Config, performanceRate60);
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, 60, 90}), 0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60, 90}}), 0);
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
 
     const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
@@ -248,7 +248,7 @@
     ASSERT_EQ(mExpected60Config, minRate);
     ASSERT_EQ(mExpected90Config, performanceRate);
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, 60, 60}), 0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
 
     auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
     auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
@@ -271,7 +271,7 @@
         EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90);
     }
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, 90, 90}), 0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
     {
         auto& current = refreshRateConfigs->getCurrentRefreshRate();
         EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90);
@@ -298,7 +298,7 @@
     EXPECT_EQ(mExpected60Config,
               refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, 60, 60}), 0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
     EXPECT_EQ(mExpected60Config,
               refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
     EXPECT_EQ(mExpected60Config,
@@ -310,7 +310,7 @@
     EXPECT_EQ(mExpected60Config,
               refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, 90, 90}), 0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
     EXPECT_EQ(mExpected90Config,
               refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
     EXPECT_EQ(mExpected90Config,
@@ -321,7 +321,7 @@
               refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
     EXPECT_EQ(mExpected90Config,
               refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, 0, 120}), 0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0, 120}}), 0);
     EXPECT_EQ(mExpected90Config,
               refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
     EXPECT_EQ(mExpected60Config,
@@ -334,7 +334,7 @@
               refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
 }
 
-TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_noLayers) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) {
     bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_72_90Device, /*currentConfigId=*/
@@ -344,17 +344,17 @@
     // refresh rate.
     auto layers = std::vector<LayerRequirement>{};
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/
-                                                             false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/
+                                                     false, /*idle*/ false, &ignored));
 
     // Current refresh rate can always be changed.
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/
-                                                             false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/
+                                                     false, /*idle*/ false, &ignored));
 }
 
-TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_60_90) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
     bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
@@ -366,162 +366,162 @@
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     lr.name = "60Hz Heuristic";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     lr.name = "45Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     lr.name = "30Hz Heuristic";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     lr.name = "24Hz Heuristic";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.name = "";
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, 60, 60}), 0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, 90, 90}), 0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
-    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, 0, 120}), 0);
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0, 120}}), 0);
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 }
 
-TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_60_72_90) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
     bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_72_90Device,
@@ -532,42 +532,42 @@
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 }
 
-TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_30_60_72_90_120) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) {
     bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
@@ -583,27 +583,27 @@
     lr2.desiredRefreshRate = 60.0f;
     lr2.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48.0f;
     lr2.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 48.0f;
     lr2.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 }
 
-TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_30_60_90_120_DifferentTypes) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
     bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
@@ -621,8 +621,8 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
     EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -631,8 +631,8 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "60Hz Heuristic";
     EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -641,8 +641,8 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "60Hz ExplicitDefault";
     EXPECT_EQ(mExpected120Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -651,8 +651,8 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -661,8 +661,8 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -671,8 +671,8 @@
     lr2.vote = LayerVoteType::Heuristic;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::Heuristic;
@@ -681,8 +681,8 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -691,8 +691,8 @@
     lr2.vote = LayerVoteType::ExplicitDefault;
     lr2.name = "90Hz ExplicitDefault";
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.desiredRefreshRate = 24.0f;
     lr1.vote = LayerVoteType::ExplicitDefault;
@@ -701,11 +701,11 @@
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.name = "90Hz ExplicitExactOrMultiple";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 }
 
-TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_30_60) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
     bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60Device,
@@ -716,42 +716,42 @@
 
     lr.vote = LayerVoteType::Min;
     EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.vote = LayerVoteType::Max;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 }
 
-TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_30_60_72_90) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) {
     bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_72_90Device,
@@ -763,70 +763,70 @@
     lr.vote = LayerVoteType::Min;
     lr.name = "Min";
     EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.vote = LayerVoteType::Max;
     lr.name = "Max";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 90.0f;
     lr.vote = LayerVoteType::Heuristic;
     lr.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 60.0f;
     lr.name = "60Hz Heuristic";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 45.0f;
     lr.name = "45Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 30.0f;
     lr.name = "30Hz Heuristic";
     EXPECT_EQ(mExpected30Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     lr.name = "24Hz Heuristic";
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
+                                                     /*idle*/ false, &ignored));
 
     lr.desiredRefreshRate = 24.0f;
     lr.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr.name = "24Hz ExplicitExactOrMultiple";
     EXPECT_EQ(mExpected72Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ true,
+                                                     /*idle*/ false, &ignored));
 }
 
-TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_PriorityTest) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) {
     bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m30_60_90Device,
@@ -840,55 +840,55 @@
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Max;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::Min;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 24.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::Max;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 15.0f;
     lr2.vote = LayerVoteType::Heuristic;
     lr2.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 30.0f;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 45.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 }
 
-TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_24FpsVideo) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) {
     bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
@@ -901,8 +901,8 @@
     for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
         lr.desiredRefreshRate = fps;
         const auto& refreshRate =
-                refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                               &ignored);
+                refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                       /*idle*/ false, &ignored);
         printf("%.2fHz chooses %s\n", fps, refreshRate.getName().c_str());
         EXPECT_EQ(mExpected60Config, refreshRate);
     }
@@ -931,7 +931,7 @@
     EXPECT_EQ(mExpected60Config, refreshRateConfigs->getRefreshRateForContent(layers));
 }
 
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_Explicit) {
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) {
     bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
@@ -947,24 +947,24 @@
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 90.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 90.0f;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::Heuristic;
     lr1.desiredRefreshRate = 90.0f;
     lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr2.desiredRefreshRate = 60.0f;
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 }
 
 TEST_F(RefreshRateConfigsTest, testInPolicy) {
@@ -975,7 +975,7 @@
     ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(50.0f, 59.998f));
 }
 
-TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_75HzContent) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
     bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
@@ -988,14 +988,14 @@
     for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
         lr.desiredRefreshRate = fps;
         const auto& refreshRate =
-                refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                               &ignored);
+                refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                       /*idle*/ false, &ignored);
         printf("%.2fHz chooses %s\n", fps, refreshRate.getName().c_str());
         EXPECT_EQ(mExpected90Config, refreshRate);
     }
 }
 
-TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_Multiples) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) {
     bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90Device,
@@ -1013,8 +1013,8 @@
     lr2.desiredRefreshRate = 90.0f;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
@@ -1023,8 +1023,8 @@
     lr2.desiredRefreshRate = 90.0f;
     lr2.name = "90Hz ExplicitDefault";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
@@ -1032,8 +1032,8 @@
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30.0f;
@@ -1042,8 +1042,8 @@
     lr2.desiredRefreshRate = 90.0f;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 30.0f;
@@ -1051,8 +1051,8 @@
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
-                                                             &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, /*touchActive*/ false,
+                                                     /*idle*/ false, &ignored));
 }
 
 TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
@@ -1072,7 +1072,7 @@
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
     EXPECT_EQ(mExpected60Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
@@ -1080,7 +1080,7 @@
     lr2.vote = LayerVoteType::NoVote;
     lr2.name = "NoVote";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, true, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
@@ -1088,7 +1088,7 @@
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, true, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &ignored));
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
@@ -1096,7 +1096,7 @@
     lr2.vote = LayerVoteType::Max;
     lr2.name = "Max";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored));
 
     // The other layer starts to provide buffers
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -1106,7 +1106,7 @@
     lr2.desiredRefreshRate = 90.0f;
     lr2.name = "90Hz Heuristic";
     EXPECT_EQ(mExpected90Config,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, false, &ignored));
+              refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored));
 }
 
 TEST_F(RefreshRateConfigsTest, touchConsidered) {
@@ -1115,10 +1115,10 @@
             std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                  /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    refreshRateConfigs->getRefreshRateForContentV2({}, false, &touchConsidered);
+    refreshRateConfigs->getBestRefreshRate({}, false, /*idle*/ false, &touchConsidered);
     EXPECT_EQ(false, touchConsidered);
 
-    refreshRateConfigs->getRefreshRateForContentV2({}, true, &touchConsidered);
+    refreshRateConfigs->getBestRefreshRate({}, true, /*idle*/ false, &touchConsidered);
     EXPECT_EQ(true, touchConsidered);
 
     auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
@@ -1130,36 +1130,40 @@
     lr1.desiredRefreshRate = 60.0f;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.name = "NoVote";
-    refreshRateConfigs->getRefreshRateForContentV2(layers, true, &touchConsidered);
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.name = "60Hz Heuristic";
+    refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
     EXPECT_EQ(true, touchConsidered);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 60.0f;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.name = "NoVote";
-    refreshRateConfigs->getRefreshRateForContentV2(layers, true, &touchConsidered);
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.name = "60Hz Heuristic";
+    refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
     EXPECT_EQ(false, touchConsidered);
 
     lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
     lr1.desiredRefreshRate = 60.0f;
     lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.name = "NoVote";
-    refreshRateConfigs->getRefreshRateForContentV2(layers, true, &touchConsidered);
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.name = "60Hz Heuristic";
+    refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
     EXPECT_EQ(true, touchConsidered);
 
     lr1.vote = LayerVoteType::ExplicitDefault;
     lr1.desiredRefreshRate = 60.0f;
-    lr1.name = "60Hz ExplicitExactrMultiple";
+    lr1.name = "60Hz ExplicitExactOrMultiple";
     lr2.vote = LayerVoteType::Heuristic;
-    lr2.name = "NoVote";
-    refreshRateConfigs->getRefreshRateForContentV2(layers, true, &touchConsidered);
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.name = "60Hz Heuristic";
+    refreshRateConfigs->getBestRefreshRate(layers, true, /*idle*/ false, &touchConsidered);
     EXPECT_EQ(false, touchConsidered);
 }
 
-TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_ExplicitDefault) {
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) {
     bool ignored;
     auto refreshRateConfigs =
             std::make_unique<RefreshRateConfigs>(m60_90_72_120Device, /*currentConfigId=*/
@@ -1196,7 +1200,7 @@
         lr.name = ss.str();
 
         const auto& refreshRate =
-                refreshRateConfigs->getRefreshRateForContentV2(layers, false, &ignored);
+                refreshRateConfigs->getBestRefreshRate(layers, false, /*idle*/ false, &ignored);
         EXPECT_FLOAT_EQ(refreshRate.getFps(), test.second)
                 << "Expecting " << test.first << "fps => " << test.second << "Hz";
     }
@@ -1215,7 +1219,8 @@
 
     bool touchConsidered;
     ASSERT_EQ(HWC_CONFIG_ID_60,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, false, &touchConsidered)
+              refreshRateConfigs
+                      ->getBestRefreshRate(layers, false, /*idle*/ false, &touchConsidered)
                       .getConfigId());
 
     RefreshRateConfigs::Policy policy;
@@ -1223,7 +1228,117 @@
     policy.allowGroupSwitching = true;
     ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
     ASSERT_EQ(HWC_CONFIG_ID_90,
-              refreshRateConfigs->getRefreshRateForContentV2(layers, false, &touchConsidered)
+              refreshRateConfigs
+                      ->getBestRefreshRate(layers, false, /*idle*/ false, &touchConsidered)
+                      .getConfigId());
+}
+
+TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    layers[0].name = "Test layer";
+
+    // Return the config ID from calling getBestRefreshRate() for a single layer with the
+    // given voteType and fps.
+    auto getFrameRate = [&](LayerVoteType voteType, float fps,
+                            bool touchActive = false) -> HwcConfigIndexType {
+        layers[0].vote = voteType;
+        layers[0].desiredRefreshRate = fps;
+        bool touchConsidered;
+        return refreshRateConfigs
+                ->getBestRefreshRate(layers, touchActive, /*idle*/ false, &touchConsidered)
+                .getConfigId();
+    };
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+                      {HWC_CONFIG_ID_60, {60.f, 60.f}, {60.f, 90.f}}),
+              0);
+    bool touchConsidered;
+    EXPECT_EQ(HWC_CONFIG_ID_60,
+              refreshRateConfigs
+                      ->getBestRefreshRate({}, /*touchActive=*/false, /*idle*/ false,
+                                           &touchConsidered)
+                      .getConfigId());
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90.f));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Min, 90.f));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90.f));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90.f));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, 90.f));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f));
+
+    // Touch boost should be restricted to the primary range.
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90.f, /*touch=*/true));
+    // When we're higher than the primary range max due to a layer frame rate setting, touch boost
+    // shouldn't drag us back down to the primary range max.
+    EXPECT_EQ(HWC_CONFIG_ID_90, getFrameRate(LayerVoteType::ExplicitDefault, 90.f, /*touch=*/true));
+    EXPECT_EQ(HWC_CONFIG_ID_90,
+              getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f, /*touch=*/true));
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+                      {HWC_CONFIG_ID_60, {60.f, 60.f}, {60.f, 60.f}}),
+              0);
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90.f));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Min, 90.f));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Max, 90.f));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::Heuristic, 90.f));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitDefault, 90.f));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f));
+}
+
+TEST_F(RefreshRateConfigsTest, idle) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    layers[0].name = "Test layer";
+
+    auto getIdleFrameRate = [&](LayerVoteType voteType, bool touchActive) -> HwcConfigIndexType {
+        layers[0].vote = voteType;
+        layers[0].desiredRefreshRate = 90.f;
+        bool touchConsidered;
+        return refreshRateConfigs
+                ->getBestRefreshRate(layers, touchActive, /*idle=*/true, &touchConsidered)
+                .getConfigId();
+    };
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+                      {HWC_CONFIG_ID_60, {60.f, 90.f}, {60.f, 90.f}}),
+              0);
+
+    // Idle should be lower priority than touch boost.
+    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::NoVote, true));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Min, true));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Max, true));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Heuristic, true));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::ExplicitDefault, true));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, true));
+
+    // With no layers, idle should still be lower priority than touch boost.
+    bool touchConsidered;
+    EXPECT_EQ(HWC_CONFIG_ID_90,
+              refreshRateConfigs
+                      ->getBestRefreshRate({}, /*touchActive=*/true, /*idle=*/true,
+                                           &touchConsidered)
+                      .getConfigId());
+
+    // Idle should be higher precedence than other layer frame rate considerations.
+    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::NoVote, false));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Min, false));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Max, false));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Heuristic, false));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::ExplicitDefault, false));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, false));
+
+    // Idle should be applied rather than the current config when there are no layers.
+    EXPECT_EQ(HWC_CONFIG_ID_60,
+              refreshRateConfigs
+                      ->getBestRefreshRate({}, /*touchActive=*/false, /*idle=*/true,
+                                           &touchConsidered)
                       .getConfigId());
 }
 
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 41b5d49..806f95c 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -72,13 +72,31 @@
     auto& mutableEventControlThread() { return mEventControlThread; }
     auto& mutablePrimaryDispSync() { return mPrimaryDispSync; }
     auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
+
     auto mutableLayerHistory() {
         return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
     }
+
     auto mutableLayerHistoryV2() {
         return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get());
     }
 
+    void replaceTouchTimer(int64_t millis) {
+        if (mTouchTimer) {
+            mTouchTimer.reset();
+        }
+        mTouchTimer.emplace(
+                std::chrono::milliseconds(millis),
+                [this] { touchTimerCallback(TimerState::Reset); },
+                [this] { touchTimerCallback(TimerState::Expired); });
+        mTouchTimer->start();
+    }
+
+    bool isTouchActive() {
+        std::lock_guard<std::mutex> lock(mFeatureStateLock);
+        return mFeatures.touch == Scheduler::TouchState::Active;
+    }
+
     ~TestableScheduler() {
         // All these pointer and container clears help ensure that GMock does
         // not report a leaked object, since the Scheduler instance may
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index bf05f70..82e68dd 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -177,6 +177,8 @@
 
 class TestableSurfaceFlinger {
 public:
+    using HotplugEvent = SurfaceFlinger::HotplugEvent;
+
     SurfaceFlinger* flinger() { return mFlinger.get(); }
     TestableScheduler* scheduler() { return mScheduler; }
 
@@ -246,28 +248,32 @@
         memcpy(&mFlinger->mInternalDisplayPrimaries, &primaries, sizeof(ui::DisplayPrimaries));
     }
 
-    using HotplugEvent = SurfaceFlinger::HotplugEvent;
-
-    auto& mutableLayerCurrentState(sp<Layer> layer) { return layer->mCurrentState; }
-    auto& mutableLayerDrawingState(sp<Layer> layer) { return layer->mDrawingState; }
+    static auto& mutableLayerCurrentState(const sp<Layer>& layer) { return layer->mCurrentState; }
+    static auto& mutableLayerDrawingState(const sp<Layer>& layer) { return layer->mDrawingState; }
 
     auto& mutableStateLock() { return mFlinger->mStateLock; }
 
-    void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) {
+    static auto findOutputLayerForDisplay(const sp<Layer>& layer,
+                                          const sp<const DisplayDevice>& display) {
+        return layer->findOutputLayerForDisplay(display.get());
+    }
+
+    static void setLayerSidebandStream(const sp<Layer>& layer,
+                                       const sp<NativeHandle>& sidebandStream) {
         layer->mDrawingState.sidebandStream = sidebandStream;
         layer->mSidebandStream = sidebandStream;
         layer->editCompositionState()->sidebandStream = sidebandStream;
     }
 
-    void setLayerCompositionType(sp<Layer> layer, hal::Composition type) {
-        auto outputLayer = layer->findOutputLayerForDisplay(mFlinger->getDefaultDisplayDevice());
+    void setLayerCompositionType(const sp<Layer>& layer, hal::Composition type) {
+        auto outputLayer = findOutputLayerForDisplay(layer, mFlinger->getDefaultDisplayDevice());
         LOG_ALWAYS_FATAL_IF(!outputLayer);
         auto& state = outputLayer->editState();
         LOG_ALWAYS_FATAL_IF(!outputLayer->getState().hwc);
         (*state.hwc).hwcCompositionType = type;
-    };
+    }
 
-    void setLayerPotentialCursor(sp<Layer> layer, bool potentialCursor) {
+    static void setLayerPotentialCursor(const sp<Layer>& layer, bool potentialCursor) {
         layer->mPotentialCursor = potentialCursor;
     }
 
@@ -315,6 +321,8 @@
         return mFlinger->onInitializeDisplays();
     }
 
+    auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); }
+
     // Allow reading display state without locking, as if called on the SF main thread.
     auto setPowerModeInternal(const sp<DisplayDevice>& display,
                               hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
@@ -627,7 +635,7 @@
             if (const auto type = mCreationArgs.connectionType) {
                 LOG_ALWAYS_FATAL_IF(!displayId);
                 LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
-                state.physical = {*displayId, *type, *mHwcDisplayId};
+                state.physical = {.id = *displayId, .type = *type, .hwcDisplayId = *mHwcDisplayId};
             }
 
             state.isSecure = mCreationArgs.isSecure;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
index 67d3e1c..dade9fc 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
@@ -84,15 +84,15 @@
     MOCK_METHOD2(validate, hal::Error(uint32_t*, uint32_t*));
     MOCK_METHOD4(presentOrValidate,
                  hal::Error(uint32_t*, uint32_t*, android::sp<android::Fence>*, uint32_t*));
-    MOCK_CONST_METHOD1(setDisplayBrightness, hal::Error(float));
+    MOCK_METHOD1(setDisplayBrightness, std::future<hal::Error>(float));
     MOCK_CONST_METHOD1(getDisplayVsyncPeriod, hal::Error(nsecs_t*));
     MOCK_METHOD3(setActiveConfigWithConstraints,
                  hal::Error(const std::shared_ptr<const HWC2::Display::Config>&,
                             const hal::VsyncPeriodChangeConstraints&,
                             hal::VsyncPeriodChangeTimeline*));
-    MOCK_CONST_METHOD1(setAutoLowLatencyMode, hal::Error(bool on));
+    MOCK_METHOD1(setAutoLowLatencyMode, hal::Error(bool on));
     MOCK_CONST_METHOD1(getSupportedContentTypes, hal::Error(std::vector<hal::ContentType>*));
-    MOCK_CONST_METHOD1(setContentType, hal::Error(hal::ContentType));
+    MOCK_METHOD1(setContentType, hal::Error(hal::ContentType));
     MOCK_CONST_METHOD1(getConnectionType, hal::Error(android::DisplayConnectionType*));
     MOCK_CONST_METHOD0(isVsyncPeriodSwitchSupported, bool());
 };
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 50eb390..054aaf8 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -40,7 +40,7 @@
                  status_t(const sp<android::EventThreadConnection> &));
     MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &));
     MOCK_METHOD1(requestNextVsync, void(const sp<android::EventThreadConnection> &));
-    MOCK_METHOD0(requestLatestConfig, void());
+    MOCK_METHOD1(requestLatestConfig, void(const sp<android::EventThreadConnection> &));
     MOCK_METHOD1(pauseVsyncCallback, void(bool));
     MOCK_METHOD0(getEventThreadConnectionCount, size_t());
 };
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp
index 97a13e4..5fb06fd 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp
@@ -16,12 +16,15 @@
 
 #include "mock/MockMessageQueue.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
-// Explicit default instantiation is recommended.
-MessageQueue::MessageQueue() = default;
+MessageQueue::MessageQueue() {
+    ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
+        // Execute task to prevent broken promise exception on destruction.
+        handler->handleMessage(Message());
+    });
+}
+
 MessageQueue::~MessageQueue() = default;
 
-} // namespace mock
-} // namespace android
\ No newline at end of file
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index e781c0a..a82b583 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -21,8 +21,7 @@
 #include "Scheduler/EventThread.h"
 #include "Scheduler/MessageQueue.h"
 
-namespace android {
-namespace mock {
+namespace android::mock {
 
 class MessageQueue : public android::MessageQueue {
 public:
@@ -32,10 +31,9 @@
     MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
     MOCK_METHOD1(setEventConnection, void(const sp<EventThreadConnection>& connection));
     MOCK_METHOD0(waitMessage, void());
-    MOCK_METHOD2(postMessage, status_t(const sp<MessageBase>&, nsecs_t));
+    MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&));
     MOCK_METHOD0(invalidate, void());
     MOCK_METHOD0(refresh, void());
 };
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 018f200..f69de1f 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -79,6 +79,7 @@
         "hwvulkan_headers",
         "libnativeloader-headers",
         "vulkan_headers",
+        "libsurfaceflinger_headers",
     ],
     export_header_lib_headers: ["vulkan_headers"],
     shared_libs: [
@@ -100,6 +101,7 @@
         "libnativeloader_lazy",
         "libnativewindow",
         "android.hardware.graphics.common@1.0",
+        "libSurfaceFlingerProp",
     ],
     static_libs: ["libgrallocusage"],
 }
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 9f754a0..74ef0e7 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -23,9 +23,10 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <SurfaceFlingerProperties.h>
+#include <android-base/properties.h>
 #include <android/dlext.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <android-base/properties.h>
 #include <configstore/Utils.h>
 #include <cutils/properties.h>
 #include <graphicsenv/GraphicsEnv.h>
@@ -251,18 +252,6 @@
     result = LoadUpdatedDriver(&module);
     if (result == -ENOENT) {
         result = LoadBuiltinDriver(&module);
-        if (result != 0) {
-            // -ENOENT means the sphal namespace doesn't exist, not that there
-            // is a problem with the driver.
-            ALOGW_IF(
-                result != -ENOENT,
-                "Failed to load Vulkan driver into sphal namespace. This "
-                "usually means the driver has forbidden library dependencies."
-                "Please fix, this will soon stop working.");
-            result =
-                hw_get_module(HWVULKAN_HARDWARE_MODULE_ID,
-                              reinterpret_cast<const hw_module_t**>(&module));
-        }
     }
     if (result != 0) {
         android::GraphicsEnv::getInstance().setDriverLoaded(
@@ -959,9 +948,7 @@
         VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME,
         VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION});
 
-    bool hdrBoardConfig =
-        getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasHDRDisplay>(
-            false);
+    bool hdrBoardConfig = android::sysprop::has_HDR_display(false);
     if (hdrBoardConfig) {
         loader_extensions.push_back({VK_EXT_HDR_METADATA_EXTENSION_NAME,
                                      VK_EXT_HDR_METADATA_SPEC_VERSION});
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 21b80b7..c7ff640 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1477,7 +1477,7 @@
     ANativeWindowBuffer* buffer;
     int fence_fd;
     err = window->dequeueBuffer(window, &buffer, &fence_fd);
-    if (err == android::TIMED_OUT) {
+    if (err == android::TIMED_OUT || err == android::INVALID_OPERATION) {
         ALOGW("dequeueBuffer timed out: %s (%d)", strerror(-err), err);
         return timeout ? VK_TIMEOUT : VK_NOT_READY;
     } else if (err != android::OK) {