Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)

Bug: 166295507
Merged-In: I70ea776b8589ac3a7982c710c5c8b2941d86e55b
Change-Id: Ic1d535e9d2d6f80d95215240dbdb024995b045f8
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
new file mode 100644
index 0000000..c3da216
--- /dev/null
+++ b/services/automotive/display/Android.bp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+    name: "android.frameworks.automotive.display@1.0-service",
+    defaults: ["hidl_defaults"],
+    srcs: [
+        "main_automotivedisplayproxy.cpp",
+        "AutomotiveDisplayProxyService.cpp",
+    ],
+    init_rc: ["android.frameworks.automotive.display@1.0-service.rc"],
+
+    shared_libs: [
+        "android.frameworks.automotive.display@1.0",
+        "android.hardware.graphics.bufferqueue@2.0",
+        "libgui",
+        "libhidlbase",
+        "liblog",
+        "libui",
+        "libutils",
+    ],
+
+    local_include_dirs: [
+        "include",
+    ],
+
+    cflags: [
+        "-DLOG_TAG=\"AutomotiveDisplayService\""
+    ],
+
+    vintf_fragments: [
+        "manifest_android.frameworks.automotive.display@1.0.xml",
+    ],
+}
diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp
new file mode 100644
index 0000000..4767406
--- /dev/null
+++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp
@@ -0,0 +1,193 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <utility>
+
+#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
+
+#include "AutomotiveDisplayProxyService.h"
+
+namespace android {
+namespace frameworks {
+namespace automotive {
+namespace display {
+namespace V1_0 {
+namespace implementation {
+
+
+Return<sp<IGraphicBufferProducer>>
+AutomotiveDisplayProxyService::getIGraphicBufferProducer(uint64_t id) {
+    auto it = mDisplays.find(id);
+    sp<IBinder> displayToken = nullptr;
+    sp<SurfaceControl> surfaceControl = nullptr;
+    if (it == mDisplays.end()) {
+        displayToken = SurfaceComposerClient::getPhysicalDisplayToken(id);
+        if (displayToken == nullptr) {
+            ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
+            return nullptr;
+        }
+
+        // Get the resolution from stored display state.
+        DisplayConfig displayConfig = {};
+        auto err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+        if (err != NO_ERROR) {
+            ALOGE("Failed to get display configuration of %lX.  "
+                  "This display will be ignored.", (unsigned long)id);
+            return nullptr;
+        }
+
+        ui::DisplayState displayState = {};
+        err = SurfaceComposerClient::getDisplayState(displayToken, &displayState);
+        if (err != NO_ERROR) {
+            ALOGE("Failed to get current display status of %lX.  "
+                  "This display will be ignored.", (unsigned long)id);
+            return nullptr;
+        }
+
+        auto displayWidth  = displayConfig.resolution.getWidth();
+        auto displayHeight = displayConfig.resolution.getHeight();
+        if ((displayState.orientation != ui::ROTATION_0) &&
+            (displayState.orientation != ui::ROTATION_180)) {
+            std::swap(displayWidth, displayHeight);
+        }
+
+        sp<android::SurfaceComposerClient> surfaceClient = new SurfaceComposerClient();
+        err = surfaceClient->initCheck();
+        if (err != NO_ERROR) {
+            ALOGE("SurfaceComposerClient::initCheck error: %#x", err);
+            return nullptr;
+        }
+
+        // Create a SurfaceControl instance
+        surfaceControl = surfaceClient->createSurface(
+                String8::format("AutomotiveDisplay::%lX", (unsigned long)id),
+                displayWidth, displayHeight,
+                PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque);
+        if (surfaceControl == nullptr || !surfaceControl->isValid()) {
+            ALOGE("Failed to create SurfaceControl.");
+            return nullptr;
+        }
+
+        // Store
+        DisplayDesc descriptor = {displayToken, surfaceControl};
+        mDisplays.insert_or_assign(id, std::move(descriptor));
+    } else {
+        displayToken = it->second.token;
+        surfaceControl = it->second.surfaceControl;
+    }
+
+    // SurfaceControl::getSurface is guaranteed to be not null.
+    auto targetSurface = surfaceControl->getSurface();
+    return new ::android::hardware::graphics::bufferqueue::V2_0::utils::
+               B2HGraphicBufferProducer(targetSurface->getIGraphicBufferProducer());
+}
+
+
+Return<bool> AutomotiveDisplayProxyService::showWindow(uint64_t id) {
+    auto it = mDisplays.find(id);
+    if (it == mDisplays.end()) {
+        ALOGE("Given display token is invalid or unknown.");
+        return false;
+    }
+
+    ui::DisplayState displayState;
+    auto err = SurfaceComposerClient::getDisplayState(it->second.token, &displayState);
+    if (err != NO_ERROR) {
+        ALOGE("Failed to get current state of the display 0x%lX", (unsigned long)id);
+        return false;
+    }
+
+    SurfaceComposerClient::Transaction t;
+    t.setDisplayLayerStack(it->second.token, displayState.layerStack);
+    t.setLayerStack(it->second.surfaceControl, displayState.layerStack);
+
+    status_t status = t.setLayer(it->second.surfaceControl, 0x7FFFFFFF)
+                      .show(it->second.surfaceControl)
+                      .apply();
+
+    return status == NO_ERROR;
+}
+
+
+Return<bool> AutomotiveDisplayProxyService::hideWindow(uint64_t id) {
+    auto it = mDisplays.find(id);
+    if (it == mDisplays.end()) {
+        ALOGE("Given display token is invalid or unknown.");
+        return false;
+    }
+
+    status_t status = SurfaceComposerClient::Transaction{}
+                      .hide(it->second.surfaceControl)
+                      .apply();
+
+    return status == NO_ERROR;
+}
+
+
+Return<void> AutomotiveDisplayProxyService::getDisplayIdList(getDisplayIdList_cb _cb) {
+    hardware::hidl_vec<uint64_t> ids;
+
+    // Get stable IDs of all available displays and get their tokens and
+    // descriptors.
+    auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
+    ids.resize(displayIds.size());
+    for (auto i = 0; i < displayIds.size(); ++i) {
+        ids[i] = displayIds[i];
+    }
+
+    _cb(ids);
+    return hardware::Void();
+}
+
+
+Return<void> AutomotiveDisplayProxyService::getDisplayInfo(uint64_t id, getDisplayInfo_cb _cb) {
+    HwDisplayConfig activeConfig;
+    HwDisplayState  activeState;
+
+    auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(id);
+    if (displayToken == nullptr) {
+        ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
+    } else {
+        DisplayConfig displayConfig = {};
+        auto err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+        if (err != NO_ERROR) {
+            ALOGW("Failed to get display configuration of %lX.  "
+                  "This display will be ignored.", (unsigned long)id);
+        }
+
+        ui::DisplayState displayState = {};
+        err = SurfaceComposerClient::getDisplayState(displayToken, &displayState);
+        if (err != NO_ERROR) {
+            ALOGW("Failed to get current display status of %lX.  "
+                  "This display will be ignored.", (unsigned long)id);
+        }
+
+        activeConfig.setToExternal((uint8_t*)&displayConfig, sizeof(DisplayConfig));
+        activeState.setToExternal((uint8_t*)&displayState, sizeof(DisplayState));
+    }
+
+    _cb(activeConfig, activeState);
+    return hardware::Void();
+}
+
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace display
+}  // namespace automotive
+}  // namespace frameworks
+}  // namespace android
+
diff --git a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
new file mode 100644
index 0000000..5c7f344
--- /dev/null
+++ b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
@@ -0,0 +1,4 @@
+service automotive_display /system/bin/android.frameworks.automotive.display@1.0-service
+    class hal
+    user graphics
+    group automotive_evs
diff --git a/services/automotive/display/include/AutomotiveDisplayProxyService.h b/services/automotive/display/include/AutomotiveDisplayProxyService.h
new file mode 100644
index 0000000..e2fc0d2
--- /dev/null
+++ b/services/automotive/display/include/AutomotiveDisplayProxyService.h
@@ -0,0 +1,66 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#pragma once
+
+#include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+#include <ui/DisplayState.h>
+#include <tuple>
+#include <vector>
+
+namespace android {
+namespace frameworks {
+namespace automotive {
+namespace display {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::Return;
+using ::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer;
+using ::android::sp;
+
+
+typedef struct DisplayDesc {
+    sp<IBinder>        token;
+    sp<SurfaceControl> surfaceControl;
+} DisplayDesc;
+
+
+class AutomotiveDisplayProxyService : public IAutomotiveDisplayProxyService {
+public:
+    Return<sp<IGraphicBufferProducer>> getIGraphicBufferProducer(uint64_t id) override;
+    Return<bool> showWindow(uint64_t id) override;
+    Return<bool> hideWindow(uint64_t id) override;
+    Return<void> getDisplayIdList(getDisplayIdList_cb _cb) override;
+    Return<void> getDisplayInfo(uint64_t, getDisplayInfo_cb _cb) override;
+
+private:
+    uint8_t getDisplayPort(const uint64_t id) { return (id & 0xF); }
+
+    std::unordered_map<uint64_t, DisplayDesc> mDisplays;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace display
+}  // namespace automotive
+}  // namespace frameworks
+}  // namespace android
+
diff --git a/services/automotive/display/main_automotivedisplayproxy.cpp b/services/automotive/display/main_automotivedisplayproxy.cpp
new file mode 100644
index 0000000..59b584c
--- /dev/null
+++ b/services/automotive/display/main_automotivedisplayproxy.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+
+#include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include "AutomotiveDisplayProxyService.h"
+
+// libhidl:
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+// Generated HIDL files
+using android::frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService;
+
+// The namespace in which all our implementation code lives
+using namespace android::frameworks::automotive::display::V1_0::implementation;
+using namespace android;
+
+const static char kServiceName[] = "default";
+
+int main() {
+    ALOGI("Automotive Display Proxy Service is starting");
+
+    android::sp<IAutomotiveDisplayProxyService> service =
+        new AutomotiveDisplayProxyService();
+
+    configureRpcThreadpool(1, true /* callerWillJoin */);
+
+    // Register our service -- if somebody is already registered by our name,
+    // they will be killed (their thread pool will throw an exception).
+    status_t status = service->registerAsService(kServiceName);
+    if (status == OK) {
+        ALOGD("%s is ready.", kServiceName);
+        joinRpcThreadpool();
+    } else {
+        ALOGE("Could not register service %s (%d).", kServiceName, status);
+    }
+
+    // In normal operation, we don't expect the thread pool to exit
+    ALOGE("Automotive Window Service is shutting down");
+
+    return 1;
+}
+
diff --git a/services/bufferhub/android.frameworks.bufferhub@1.0-service.xml b/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml
similarity index 65%
rename from services/bufferhub/android.frameworks.bufferhub@1.0-service.xml
rename to services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml
index bd958d3..464dcac 100644
--- a/services/bufferhub/android.frameworks.bufferhub@1.0-service.xml
+++ b/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml
@@ -1,10 +1,10 @@
 <manifest version="1.0" type="framework">
     <hal>
-        <name>android.frameworks.bufferhub</name>
+        <name>android.frameworks.automotive.display</name>
         <transport>hwbinder</transport>
         <version>1.0</version>
         <interface>
-            <name>IBufferHub</name>
+            <name>IAutomotiveDisplayProxyService</name>
             <instance>default</instance>
         </interface>
     </hal>
diff --git a/services/bufferhub/Android.bp b/services/bufferhub/Android.bp
deleted file mode 100644
index 2bb6aef..0000000
--- a/services/bufferhub/Android.bp
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-cc_library_shared {
-    name: "libbufferhubservice",
-    cflags: [
-        "-DLOG_TAG=\"libbufferhubservice\"",
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-    ],
-    srcs: [
-        "BufferClient.cpp",
-        "BufferHubIdGenerator.cpp",
-        "BufferHubService.cpp",
-        "BufferNode.cpp",
-    ],
-    header_libs: [
-        "libdvr_headers",
-        "libnativewindow_headers",
-    ],
-    shared_libs: [
-        "android.frameworks.bufferhub@1.0",
-        "libcrypto",
-        "libcutils",
-        "libhidlbase",
-        "liblog",
-        "libui",
-        "libutils",
-    ],
-    export_include_dirs: [
-        "include"
-    ],
-}
-
-cc_binary {
-    name: "android.frameworks.bufferhub@1.0-service",
-    relative_install_path: "hw",
-    srcs: [
-        "main_bufferhub.cpp"
-    ],
-    header_libs: [
-        "libdvr_headers",
-        "libnativewindow_headers",
-    ],
-    shared_libs: [
-        "android.frameworks.bufferhub@1.0",
-        "libbufferhubservice",
-        "libcrypto",
-        "libcutils",
-        "libhidlbase",
-        "liblog",
-        "libui",
-        "libutils",
-    ],
-    cflags: [
-        "-DLOG_TAG=\"bufferhub\"",
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-    ],
-    init_rc: ["android.frameworks.bufferhub@1.0-service.rc"],
-    vintf_fragments: ["android.frameworks.bufferhub@1.0-service.xml"],
-}
diff --git a/services/bufferhub/BufferClient.cpp b/services/bufferhub/BufferClient.cpp
deleted file mode 100644
index ec7e535..0000000
--- a/services/bufferhub/BufferClient.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <bufferhub/BufferClient.h>
-#include <bufferhub/BufferHubService.h>
-#include <hidl/HidlSupport.h>
-#include <log/log.h>
-
-namespace android {
-namespace frameworks {
-namespace bufferhub {
-namespace V1_0 {
-namespace implementation {
-
-using hardware::hidl_handle;
-using hardware::Void;
-
-BufferClient* BufferClient::create(BufferHubService* service,
-                                   const std::shared_ptr<BufferNode>& node) {
-    if (!service) {
-        ALOGE("%s: service cannot be nullptr.", __FUNCTION__);
-        return nullptr;
-    } else if (!node) {
-        ALOGE("%s: node cannot be nullptr.", __FUNCTION__);
-        return nullptr;
-    }
-    return new BufferClient(service, node);
-}
-
-BufferClient::~BufferClient() {
-    {
-        std::lock_guard<std::mutex> lock(mClosedMutex);
-        if (!mClosed) {
-            ALOGW("%s: client of buffer #%d destroyed without close. Closing it now.", __FUNCTION__,
-                  mBufferNode->id());
-        }
-    }
-
-    close();
-}
-
-Return<BufferHubStatus> BufferClient::close() {
-    std::lock_guard<std::mutex> lock(mClosedMutex);
-    if (mClosed) {
-        return BufferHubStatus::CLIENT_CLOSED;
-    }
-
-    getService()->onClientClosed(this);
-    mBufferNode.reset();
-    mClosed = true;
-    return BufferHubStatus::NO_ERROR;
-}
-
-Return<void> BufferClient::duplicate(duplicate_cb _hidl_cb) {
-    std::lock_guard<std::mutex> lock(mClosedMutex);
-    if (mClosed) {
-        _hidl_cb(/*token=*/hidl_handle(), /*status=*/BufferHubStatus::CLIENT_CLOSED);
-        return Void();
-    }
-
-    if (!mBufferNode) {
-        // Should never happen
-        ALOGE("%s: node is missing.", __FUNCTION__);
-        _hidl_cb(/*token=*/hidl_handle(), /*status=*/BufferHubStatus::BUFFER_FREED);
-        return Void();
-    }
-
-    const hidl_handle token = getService()->registerToken(this);
-    _hidl_cb(/*token=*/token, /*status=*/BufferHubStatus::NO_ERROR);
-    return Void();
-}
-
-sp<BufferHubService> BufferClient::getService() {
-    sp<BufferHubService> service = mService.promote();
-    if (service == nullptr) {
-        // Should never happen. Kill the process.
-        LOG_FATAL("%s: service died.", __FUNCTION__);
-    }
-
-    return service;
-}
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferhub
-} // namespace frameworks
-} // namespace android
\ No newline at end of file
diff --git a/services/bufferhub/BufferHubIdGenerator.cpp b/services/bufferhub/BufferHubIdGenerator.cpp
deleted file mode 100644
index 2c12f0e..0000000
--- a/services/bufferhub/BufferHubIdGenerator.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <bufferhub/BufferHubIdGenerator.h>
-#include <log/log.h>
-
-namespace android {
-namespace frameworks {
-namespace bufferhub {
-namespace V1_0 {
-namespace implementation {
-
-BufferHubIdGenerator& BufferHubIdGenerator::getInstance() {
-    static BufferHubIdGenerator generator;
-
-    return generator;
-}
-
-int BufferHubIdGenerator::getId() {
-    std::lock_guard<std::mutex> lock(mIdsInUseMutex);
-
-    do {
-        if (++mLastId >= std::numeric_limits<int>::max()) {
-            mLastId = 0;
-        }
-    } while (mIdsInUse.find(mLastId) != mIdsInUse.end());
-
-    mIdsInUse.insert(mLastId);
-    return mLastId;
-}
-
-void BufferHubIdGenerator::freeId(int id) {
-    std::lock_guard<std::mutex> lock(mIdsInUseMutex);
-    auto iter = mIdsInUse.find(id);
-    if (iter != mIdsInUse.end()) {
-        mIdsInUse.erase(iter);
-    } else {
-        ALOGW("%s: Cannot free nonexistent id #%d", __FUNCTION__, id);
-    }
-}
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferhub
-} // namespace frameworks
-} // namespace android
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
deleted file mode 100644
index 7a3472f..0000000
--- a/services/bufferhub/BufferHubService.cpp
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <array>
-#include <iomanip>
-#include <random>
-#include <sstream>
-
-#include <android/hardware_buffer.h>
-#include <bufferhub/BufferHubService.h>
-#include <cutils/native_handle.h>
-#include <log/log.h>
-#include <openssl/hmac.h>
-#include <system/graphics-base.h>
-#include <ui/BufferHubDefs.h>
-
-using ::android::BufferHubDefs::MetadataHeader;
-using ::android::hardware::Void;
-
-namespace android {
-namespace frameworks {
-namespace bufferhub {
-namespace V1_0 {
-namespace implementation {
-
-BufferHubService::BufferHubService() {
-    std::mt19937_64 randomEngine;
-    randomEngine.seed(time(nullptr));
-
-    mKey = randomEngine();
-}
-
-Return<void> BufferHubService::allocateBuffer(const HardwareBufferDescription& description,
-                                              const uint32_t userMetadataSize,
-                                              allocateBuffer_cb _hidl_cb) {
-    AHardwareBuffer_Desc desc;
-    memcpy(&desc, &description, sizeof(AHardwareBuffer_Desc));
-
-    std::shared_ptr<BufferNode> node =
-            std::make_shared<BufferNode>(desc.width, desc.height, desc.layers, desc.format,
-                                         desc.usage, userMetadataSize,
-                                         BufferHubIdGenerator::getInstance().getId());
-    if (node == nullptr || !node->isValid()) {
-        ALOGE("%s: creating BufferNode failed.", __FUNCTION__);
-        _hidl_cb(/*status=*/BufferHubStatus::ALLOCATION_FAILED, /*bufferClient=*/nullptr,
-                 /*bufferTraits=*/{});
-        return Void();
-    }
-
-    sp<BufferClient> client = BufferClient::create(this, node);
-    // Add it to list for bookkeeping and dumpsys.
-    std::lock_guard<std::mutex> lock(mClientSetMutex);
-    mClientSet.emplace(client);
-
-    // Allocate memory for bufferInfo of type hidl_handle on the stack. See
-    // http://aosp/286282 for the usage of NATIVE_HANDLE_DECLARE_STORAGE.
-    NATIVE_HANDLE_DECLARE_STORAGE(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
-                                  BufferHubDefs::kBufferInfoNumInts);
-    hidl_handle bufferInfo =
-            buildBufferInfo(bufferInfoStorage, node->id(), node->addNewActiveClientsBitToMask(),
-                            node->userMetadataSize(), node->metadata().ashmemFd(),
-                            node->eventFd().get());
-    // During the gralloc allocation carried out by BufferNode, gralloc allocator will populate the
-    // fields of its HardwareBufferDescription (i.e. strides) according to the actual
-    // gralloc implementation. We need to read those fields back and send them to the client via
-    // BufferTraits.
-    HardwareBufferDescription allocatedBufferDesc;
-    memcpy(&allocatedBufferDesc, &node->bufferDesc(), sizeof(AHardwareBuffer_Desc));
-    BufferTraits bufferTraits = {/*bufferDesc=*/allocatedBufferDesc,
-                                 /*bufferHandle=*/hidl_handle(node->bufferHandle()),
-                                 /*bufferInfo=*/std::move(bufferInfo)};
-
-    _hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client,
-             /*bufferTraits=*/std::move(bufferTraits));
-    return Void();
-}
-
-Return<void> BufferHubService::importBuffer(const hidl_handle& tokenHandle,
-                                            importBuffer_cb _hidl_cb) {
-    if (!tokenHandle.getNativeHandle() || tokenHandle->numFds != 0 || tokenHandle->numInts <= 1) {
-        // nullptr handle or wrong format
-        _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
-                 /*bufferTraits=*/{});
-        return Void();
-    }
-
-    int tokenId = tokenHandle->data[0];
-
-    wp<BufferClient> originClientWp;
-    {
-        std::lock_guard<std::mutex> lock(mTokenMutex);
-        auto iter = mTokenMap.find(tokenId);
-        if (iter == mTokenMap.end()) {
-            // Token Id not exist
-            ALOGD("%s: token #%d not found.", __FUNCTION__, tokenId);
-            _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
-                     /*bufferTraits=*/{});
-            return Void();
-        }
-
-        const std::vector<uint8_t>& tokenHMAC = iter->second.first;
-
-        int numIntsForHMAC = (int)ceil(tokenHMAC.size() * sizeof(uint8_t) / (double)sizeof(int));
-        if (tokenHandle->numInts - 1 != numIntsForHMAC) {
-            // HMAC size not match
-            ALOGD("%s: token #%d HMAC size not match. Expected: %d Actual: %d", __FUNCTION__,
-                  tokenId, numIntsForHMAC, tokenHandle->numInts - 1);
-            _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
-                     /*bufferTraits=*/{});
-            return Void();
-        }
-
-        size_t hmacSize = tokenHMAC.size() * sizeof(uint8_t);
-        if (memcmp(tokenHMAC.data(), &tokenHandle->data[1], hmacSize) != 0) {
-            // HMAC not match
-            ALOGD("%s: token #%d HMAC not match.", __FUNCTION__, tokenId);
-            _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr,
-                     /*bufferTraits=*/{});
-            return Void();
-        }
-
-        originClientWp = iter->second.second;
-        mTokenMap.erase(iter);
-    }
-
-    // Check if original client is dead
-    sp<BufferClient> originClient = originClientWp.promote();
-    if (!originClient) {
-        // Should not happen since token should be removed if already gone
-        ALOGE("%s: original client %p gone!", __FUNCTION__, originClientWp.unsafe_get());
-        _hidl_cb(/*status=*/BufferHubStatus::BUFFER_FREED, /*bufferClient=*/nullptr,
-                 /*bufferTraits=*/{});
-        return Void();
-    }
-
-    sp<BufferClient> client = new BufferClient(*originClient);
-    uint32_t clientStateMask = client->getBufferNode()->addNewActiveClientsBitToMask();
-    if (clientStateMask == 0U) {
-        // Reach max client count
-        ALOGE("%s: import failed, BufferNode#%u reached maximum clients.", __FUNCTION__,
-              client->getBufferNode()->id());
-        _hidl_cb(/*status=*/BufferHubStatus::MAX_CLIENT, /*bufferClient=*/nullptr,
-                 /*bufferTraits=*/{});
-        return Void();
-    }
-
-    std::lock_guard<std::mutex> lock(mClientSetMutex);
-    mClientSet.emplace(client);
-
-    std::shared_ptr<BufferNode> node = client->getBufferNode();
-
-    HardwareBufferDescription bufferDesc;
-    memcpy(&bufferDesc, &node->bufferDesc(), sizeof(HardwareBufferDescription));
-
-    // Allocate memory for bufferInfo of type hidl_handle on the stack. See
-    // http://aosp/286282 for the usage of NATIVE_HANDLE_DECLARE_STORAGE.
-    NATIVE_HANDLE_DECLARE_STORAGE(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
-                                  BufferHubDefs::kBufferInfoNumInts);
-    hidl_handle bufferInfo = buildBufferInfo(bufferInfoStorage, node->id(), clientStateMask,
-                                             node->userMetadataSize(), node->metadata().ashmemFd(),
-                                             node->eventFd().get());
-    BufferTraits bufferTraits = {/*bufferDesc=*/bufferDesc,
-                                 /*bufferHandle=*/hidl_handle(node->bufferHandle()),
-                                 /*bufferInfo=*/std::move(bufferInfo)};
-
-    _hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client,
-             /*bufferTraits=*/std::move(bufferTraits));
-    return Void();
-}
-
-Return<void> BufferHubService::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) {
-    if (fd.getNativeHandle() == nullptr || fd->numFds < 1) {
-        ALOGE("%s: missing fd for writing.", __FUNCTION__);
-        return Void();
-    }
-
-    FILE* out = fdopen(dup(fd->data[0]), "w");
-
-    if (args.size() != 0) {
-        fprintf(out,
-                "Note: lshal bufferhub currently does not support args. Input arguments are "
-                "ignored.\n");
-    }
-
-    std::ostringstream stream;
-
-    // Get the number of clients of each buffer.
-    // Map from bufferId to bufferNode_clientCount pair.
-    std::map<int, std::pair<const std::shared_ptr<BufferNode>, uint32_t>> clientCount;
-    {
-        std::lock_guard<std::mutex> lock(mClientSetMutex);
-        for (auto iter = mClientSet.begin(); iter != mClientSet.end(); ++iter) {
-            sp<BufferClient> client = iter->promote();
-            if (client != nullptr) {
-                const std::shared_ptr<BufferNode> node = client->getBufferNode();
-                auto mapIter = clientCount.find(node->id());
-                if (mapIter != clientCount.end()) {
-                    ++mapIter->second.second;
-                } else {
-                    clientCount.emplace(node->id(),
-                                        std::pair<std::shared_ptr<BufferNode>, uint32_t>(node, 1U));
-                }
-            }
-        }
-    }
-
-    stream << "Active Buffers:\n";
-    stream << std::right;
-    stream << std::setw(6) << "Id";
-    stream << " ";
-    stream << std::setw(9) << "#Clients";
-    stream << " ";
-    stream << std::setw(14) << "Geometry";
-    stream << " ";
-    stream << std::setw(6) << "Format";
-    stream << " ";
-    stream << std::setw(10) << "Usage";
-    stream << " ";
-    stream << std::setw(10) << "State";
-    stream << " ";
-    stream << std::setw(8) << "Index";
-    stream << std::endl;
-
-    for (auto iter = clientCount.begin(); iter != clientCount.end(); ++iter) {
-        const std::shared_ptr<BufferNode> node = std::move(iter->second.first);
-        const uint32_t clientCount = iter->second.second;
-        AHardwareBuffer_Desc desc = node->bufferDesc();
-
-        MetadataHeader* metadataHeader =
-                const_cast<BufferHubMetadata*>(&node->metadata())->metadataHeader();
-        const uint32_t state = metadataHeader->bufferState.load(std::memory_order_acquire);
-        const uint64_t index = metadataHeader->queueIndex;
-
-        stream << std::right;
-        stream << std::setw(6) << /*Id=*/node->id();
-        stream << " ";
-        stream << std::setw(9) << /*#Clients=*/clientCount;
-        stream << " ";
-        if (desc.format == HAL_PIXEL_FORMAT_BLOB) {
-            std::string size = std::to_string(desc.width) + " B";
-            stream << std::setw(14) << /*Geometry=*/size;
-        } else {
-            std::string dimensions = std::to_string(desc.width) + "x" +
-                    std::to_string(desc.height) + "x" + std::to_string(desc.layers);
-            stream << std::setw(14) << /*Geometry=*/dimensions;
-        }
-        stream << " ";
-        stream << std::setw(6) << /*Format=*/desc.format;
-        stream << " ";
-        stream << "0x" << std::hex << std::setfill('0');
-        stream << std::setw(8) << /*Usage=*/desc.usage;
-        stream << std::dec << std::setfill(' ');
-        stream << " ";
-        stream << "0x" << std::hex << std::setfill('0');
-        stream << std::setw(8) << /*State=*/state;
-        stream << std::dec << std::setfill(' ');
-        stream << " ";
-        stream << std::setw(8) << /*Index=*/index;
-        stream << std::endl;
-    }
-
-    stream << std::endl;
-
-    // Get the number of tokens of each buffer.
-    // Map from bufferId to tokenCount
-    std::map<int, uint32_t> tokenCount;
-    {
-        std::lock_guard<std::mutex> lock(mTokenMutex);
-        for (auto iter = mTokenMap.begin(); iter != mTokenMap.end(); ++iter) {
-            sp<BufferClient> client = iter->second.second.promote();
-            if (client != nullptr) {
-                const std::shared_ptr<BufferNode> node = client->getBufferNode();
-                auto mapIter = tokenCount.find(node->id());
-                if (mapIter != tokenCount.end()) {
-                    ++mapIter->second;
-                } else {
-                    tokenCount.emplace(node->id(), 1U);
-                }
-            }
-        }
-    }
-
-    stream << "Unused Tokens:\n";
-    stream << std::right;
-    stream << std::setw(8) << "Buffer Id";
-    stream << " ";
-    stream << std::setw(7) << "#Tokens";
-    stream << std::endl;
-
-    for (auto iter = tokenCount.begin(); iter != tokenCount.end(); ++iter) {
-        stream << std::right;
-        stream << std::setw(8) << /*Buffer Id=*/iter->first;
-        stream << " ";
-        stream << std::setw(7) << /*#Tokens=*/iter->second;
-        stream << std::endl;
-    }
-
-    fprintf(out, "%s", stream.str().c_str());
-
-    fclose(out);
-    return Void();
-}
-
-hidl_handle BufferHubService::registerToken(const wp<BufferClient>& client) {
-    // Find next available token id
-    std::lock_guard<std::mutex> lock(mTokenMutex);
-    do {
-        ++mLastTokenId;
-    } while (mTokenMap.find(mLastTokenId) != mTokenMap.end());
-
-    std::array<uint8_t, EVP_MAX_MD_SIZE> hmac;
-    uint32_t hmacSize = 0U;
-
-    HMAC(/*evp_md=*/EVP_sha256(), /*key=*/&mKey, /*key_len=*/kKeyLen,
-         /*data=*/(uint8_t*)&mLastTokenId, /*data_len=*/mTokenIdSize,
-         /*out=*/hmac.data(), /*out_len=*/&hmacSize);
-
-    int numIntsForHMAC = (int)ceil(hmacSize / (double)sizeof(int));
-    native_handle_t* handle = native_handle_create(/*numFds=*/0, /*numInts=*/1 + numIntsForHMAC);
-    handle->data[0] = mLastTokenId;
-    // Set all the the bits of last int to 0 since it might not be fully overwritten
-    handle->data[numIntsForHMAC] = 0;
-    memcpy(&handle->data[1], hmac.data(), hmacSize);
-
-    // returnToken owns the native_handle_t* thus doing lifecycle management
-    hidl_handle returnToken;
-    returnToken.setTo(handle, /*shoudOwn=*/true);
-
-    std::vector<uint8_t> hmacVec;
-    hmacVec.resize(hmacSize);
-    memcpy(hmacVec.data(), hmac.data(), hmacSize);
-    mTokenMap.emplace(mLastTokenId, std::pair(hmacVec, client));
-
-    return returnToken;
-}
-
-void BufferHubService::onClientClosed(const BufferClient* client) {
-    removeTokenByClient(client);
-
-    std::lock_guard<std::mutex> lock(mClientSetMutex);
-    auto iter = std::find(mClientSet.begin(), mClientSet.end(), client);
-    if (iter != mClientSet.end()) {
-        mClientSet.erase(iter);
-    }
-}
-
-// Implementation of this function should be consistent with the definition of bufferInfo handle in
-// ui/BufferHubDefs.h.
-hidl_handle BufferHubService::buildBufferInfo(char* bufferInfoStorage, int bufferId,
-                                              uint32_t clientBitMask, uint32_t userMetadataSize,
-                                              int metadataFd, int eventFd) {
-    native_handle_t* infoHandle =
-            native_handle_init(bufferInfoStorage, BufferHubDefs::kBufferInfoNumFds,
-                               BufferHubDefs::kBufferInfoNumInts);
-
-    infoHandle->data[0] = metadataFd;
-    infoHandle->data[1] = eventFd;
-    infoHandle->data[2] = bufferId;
-    // Use memcpy to convert to int without missing digit.
-    // TOOD(b/121345852): use bit_cast to unpack bufferInfo when C++20 becomes available.
-    memcpy(&infoHandle->data[3], &clientBitMask, sizeof(clientBitMask));
-    memcpy(&infoHandle->data[4], &userMetadataSize, sizeof(userMetadataSize));
-
-    hidl_handle bufferInfo;
-    bufferInfo.setTo(infoHandle, /*shouldOwn=*/false);
-
-    return bufferInfo;
-}
-
-void BufferHubService::removeTokenByClient(const BufferClient* client) {
-    std::lock_guard<std::mutex> lock(mTokenMutex);
-    auto iter = mTokenMap.begin();
-    while (iter != mTokenMap.end()) {
-        if (iter->second.second == client) {
-            auto oldIter = iter;
-            ++iter;
-            mTokenMap.erase(oldIter);
-        } else {
-            ++iter;
-        }
-    }
-}
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferhub
-} // namespace frameworks
-} // namespace android
diff --git a/services/bufferhub/BufferNode.cpp b/services/bufferhub/BufferNode.cpp
deleted file mode 100644
index 04ca649..0000000
--- a/services/bufferhub/BufferNode.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-#include <errno.h>
-
-#include <bufferhub/BufferHubService.h>
-#include <bufferhub/BufferNode.h>
-#include <log/log.h>
-#include <ui/GraphicBufferAllocator.h>
-
-namespace android {
-namespace frameworks {
-namespace bufferhub {
-namespace V1_0 {
-namespace implementation {
-
-void BufferNode::initializeMetadata() {
-    // Using placement new here to reuse shared memory instead of new allocation
-    // Initialize the atomic variables to zero.
-    BufferHubDefs::MetadataHeader* metadataHeader = mMetadata.metadataHeader();
-    mBufferState = new (&metadataHeader->bufferState) std::atomic<uint32_t>(0);
-    mFenceState = new (&metadataHeader->fenceState) std::atomic<uint32_t>(0);
-    mActiveClientsBitMask = new (&metadataHeader->activeClientsBitMask) std::atomic<uint32_t>(0);
-    // The C++ standard recommends (but does not require) that lock-free atomic operations are
-    // also address-free, that is, suitable for communication between processes using shared
-    // memory.
-    LOG_ALWAYS_FATAL_IF(!std::atomic_is_lock_free(mBufferState) ||
-                                !std::atomic_is_lock_free(mFenceState) ||
-                                !std::atomic_is_lock_free(mActiveClientsBitMask),
-                        "Atomic variables in ashmen are not lock free.");
-}
-
-// Allocates a new BufferNode.
-BufferNode::BufferNode(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
-                       uint64_t usage, size_t userMetadataSize, int id)
-      : mId(id) {
-    uint32_t outStride = 0;
-    // graphicBufferId is not used in GraphicBufferAllocator::allocate
-    // TODO(b/112338294) After move to the service folder, stop using the
-    // hardcoded service name "bufferhub".
-    int ret = GraphicBufferAllocator::get().allocate(width, height, format, layerCount, usage,
-                                                     const_cast<const native_handle_t**>(
-                                                             &mBufferHandle),
-                                                     &outStride,
-                                                     /*graphicBufferId=*/0,
-                                                     /*requestor=*/"bufferhub");
-
-    if (ret != OK || mBufferHandle == nullptr) {
-        ALOGE("%s: Failed to allocate buffer: %s", __FUNCTION__, strerror(-ret));
-        return;
-    }
-
-    mBufferDesc.width = width;
-    mBufferDesc.height = height;
-    mBufferDesc.layers = layerCount;
-    mBufferDesc.format = format;
-    mBufferDesc.usage = usage;
-    mBufferDesc.stride = outStride;
-
-    mMetadata = BufferHubMetadata::create(userMetadataSize);
-    if (!mMetadata.isValid()) {
-        ALOGE("%s: Failed to allocate metadata.", __FUNCTION__);
-        return;
-    }
-    initializeMetadata();
-}
-
-BufferNode::~BufferNode() {
-    // Free the handle
-    if (mBufferHandle != nullptr) {
-        status_t ret = GraphicBufferAllocator::get().free(mBufferHandle);
-        if (ret != OK) {
-            ALOGE("%s: Failed to free handle; Got error: %d", __FUNCTION__, ret);
-        }
-    }
-
-    // Free the id, if valid
-    if (mId >= 0) {
-        BufferHubIdGenerator::getInstance().freeId(mId);
-    }
-}
-
-uint32_t BufferNode::getActiveClientsBitMask() const {
-    return mActiveClientsBitMask->load(std::memory_order_acquire);
-}
-
-uint32_t BufferNode::addNewActiveClientsBitToMask() {
-    uint32_t currentActiveClientsBitMask = getActiveClientsBitMask();
-    uint32_t clientStateMask = 0U;
-    uint32_t updatedActiveClientsBitMask = 0U;
-    do {
-        clientStateMask =
-                BufferHubDefs::findNextAvailableClientStateMask(currentActiveClientsBitMask);
-        if (clientStateMask == 0U) {
-            ALOGE("%s: reached the maximum number of channels per buffer node: %d.", __FUNCTION__,
-                  BufferHubDefs::kMaxNumberOfClients);
-            errno = E2BIG;
-            return 0U;
-        }
-        updatedActiveClientsBitMask = currentActiveClientsBitMask | clientStateMask;
-    } while (!(mActiveClientsBitMask->compare_exchange_weak(currentActiveClientsBitMask,
-                                                            updatedActiveClientsBitMask,
-                                                            std::memory_order_acq_rel,
-                                                            std::memory_order_acquire)));
-    return clientStateMask;
-}
-
-void BufferNode::removeClientsBitFromMask(const uint32_t& value) {
-    mActiveClientsBitMask->fetch_and(~value);
-}
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferhub
-} // namespace frameworks
-} // namespace android
diff --git a/services/bufferhub/android.frameworks.bufferhub@1.0-service.rc b/services/bufferhub/android.frameworks.bufferhub@1.0-service.rc
deleted file mode 100644
index 36fbede..0000000
--- a/services/bufferhub/android.frameworks.bufferhub@1.0-service.rc
+++ /dev/null
@@ -1,6 +0,0 @@
-service system_bufferhub /system/bin/hw/android.frameworks.bufferhub@1.0-service
-  class hal animation
-  user system
-  group system graphics
-  onrestart restart surfaceflinger
-  writepid /dev/cpuset/system-background/tasks
diff --git a/services/bufferhub/include/bufferhub/BufferClient.h b/services/bufferhub/include/bufferhub/BufferClient.h
deleted file mode 100644
index 644b403..0000000
--- a/services/bufferhub/include/bufferhub/BufferClient.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_CLIENT_H
-#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_CLIENT_H
-
-#include <mutex>
-
-#include <android/frameworks/bufferhub/1.0/IBufferClient.h>
-#include <bufferhub/BufferNode.h>
-
-namespace android {
-namespace frameworks {
-namespace bufferhub {
-namespace V1_0 {
-namespace implementation {
-
-using hardware::hidl_handle;
-using hardware::Return;
-
-// Forward declaration to avoid circular dependency
-class BufferHubService;
-
-class BufferClient : public IBufferClient {
-public:
-    // Creates a server-side buffer client from an existing BufferNode. Note that
-    // this function takes ownership of the shared_ptr.
-    // Returns a raw pointer to the BufferClient on success, nullptr on failure.
-    static BufferClient* create(BufferHubService* service, const std::shared_ptr<BufferNode>& node);
-
-    // Creates a BufferClient from an existing BufferClient. Will share the same BufferNode.
-    explicit BufferClient(const BufferClient& other)
-          : mService(other.mService), mBufferNode(other.mBufferNode) {}
-    ~BufferClient();
-
-    Return<BufferHubStatus> close() override;
-    Return<void> duplicate(duplicate_cb _hidl_cb) override;
-
-    // Non-binder functions
-    const std::shared_ptr<BufferNode>& getBufferNode() const { return mBufferNode; }
-
-private:
-    BufferClient(wp<BufferHubService> service, const std::shared_ptr<BufferNode>& node)
-          : mService(service), mBufferNode(node) {}
-
-    sp<BufferHubService> getService();
-
-    wp<BufferHubService> mService;
-
-    std::mutex mClosedMutex;
-    bool mClosed GUARDED_BY(mClosedMutex) = false;
-
-    std::shared_ptr<BufferNode> mBufferNode;
-};
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferhub
-} // namespace frameworks
-} // namespace android
-
-#endif
diff --git a/services/bufferhub/include/bufferhub/BufferHubIdGenerator.h b/services/bufferhub/include/bufferhub/BufferHubIdGenerator.h
deleted file mode 100644
index ef7c077..0000000
--- a/services/bufferhub/include/bufferhub/BufferHubIdGenerator.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_ID_GENERATOR_H
-#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_ID_GENERATOR_H
-
-#include <mutex>
-#include <set>
-
-#include <utils/Mutex.h>
-
-namespace android {
-namespace frameworks {
-namespace bufferhub {
-namespace V1_0 {
-namespace implementation {
-
-// A thread-safe, non-negative, incremental, int id generator.
-class BufferHubIdGenerator {
-public:
-    // Get the singleton instance of this class
-    static BufferHubIdGenerator& getInstance();
-
-    // Gets next available id. If next id is greater than std::numeric_limits<int32_t>::max(), it
-    // will try to get an id start from 0 again.
-    int getId();
-
-    // Free a specific id.
-    void freeId(int id);
-
-private:
-    BufferHubIdGenerator() = default;
-    ~BufferHubIdGenerator() = default;
-
-    // Start from -1 so all valid ids will be >= 0
-    int mLastId = -1;
-
-    std::mutex mIdsInUseMutex;
-    std::set<int> mIdsInUse GUARDED_BY(mIdsInUseMutex);
-};
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferhub
-} // namespace frameworks
-} // namespace android
-
-#endif // ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_ID_GENERATOR_H
diff --git a/services/bufferhub/include/bufferhub/BufferHubService.h b/services/bufferhub/include/bufferhub/BufferHubService.h
deleted file mode 100644
index edad20b..0000000
--- a/services/bufferhub/include/bufferhub/BufferHubService.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
-#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
-
-#include <map>
-#include <mutex>
-#include <set>
-#include <vector>
-
-#include <android/frameworks/bufferhub/1.0/IBufferHub.h>
-#include <bufferhub/BufferClient.h>
-#include <bufferhub/BufferHubIdGenerator.h>
-#include <utils/Mutex.h>
-
-namespace android {
-namespace frameworks {
-namespace bufferhub {
-namespace V1_0 {
-namespace implementation {
-
-using hardware::hidl_handle;
-using hardware::hidl_string;
-using hardware::hidl_vec;
-using hardware::Return;
-using hardware::graphics::common::V1_2::HardwareBufferDescription;
-
-class BufferHubService : public IBufferHub {
-public:
-    BufferHubService();
-
-    Return<void> allocateBuffer(const HardwareBufferDescription& description,
-                                const uint32_t userMetadataSize,
-                                allocateBuffer_cb _hidl_cb) override;
-    Return<void> importBuffer(const hidl_handle& tokenHandle, importBuffer_cb _hidl_cb) override;
-
-    Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
-
-    // Non-binder functions
-    // Internal help function for IBufferClient::duplicate.
-    hidl_handle registerToken(const wp<BufferClient>& client);
-
-    void onClientClosed(const BufferClient* client);
-
-private:
-    // Helper function to build BufferTraits.bufferInfo handle
-    hidl_handle buildBufferInfo(char* bufferInfoStorage, int bufferId, uint32_t clientBitMask,
-                                uint32_t userMetadataSize, int metadataFd, int eventFd);
-
-    // Helper function to remove all the token belongs to a specific client.
-    void removeTokenByClient(const BufferClient* client);
-
-    // List of active BufferClient for bookkeeping.
-    std::mutex mClientSetMutex;
-    std::set<wp<BufferClient>> mClientSet GUARDED_BY(mClientSetMutex);
-
-    // Token generation related
-    // A random number used as private key for HMAC
-    uint64_t mKey;
-    static constexpr size_t kKeyLen = sizeof(uint64_t);
-
-    std::mutex mTokenMutex;
-    // The first TokenId will be 1. TokenId could be negative.
-    int mLastTokenId GUARDED_BY(mTokenMutex) = 0;
-    static constexpr size_t mTokenIdSize = sizeof(int);
-    // A map from token id to the token-buffer_client pair. Using token id as the key to reduce
-    // looking up time
-    std::map<int, std::pair<std::vector<uint8_t>, const wp<BufferClient>>> mTokenMap
-            GUARDED_BY(mTokenMutex);
-};
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferhub
-} // namespace frameworks
-} // namespace android
-
-#endif // ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
diff --git a/services/bufferhub/include/bufferhub/BufferNode.h b/services/bufferhub/include/bufferhub/BufferNode.h
deleted file mode 100644
index 62a8d63..0000000
--- a/services/bufferhub/include/bufferhub/BufferNode.h
+++ /dev/null
@@ -1,100 +0,0 @@
-#ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_NODE_H_
-#define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_NODE_H_
-
-#include <android/hardware_buffer.h>
-#include <bufferhub/BufferHubIdGenerator.h>
-#include <cutils/native_handle.h>
-#include <ui/BufferHubEventFd.h>
-#include <ui/BufferHubMetadata.h>
-
-namespace android {
-namespace frameworks {
-namespace bufferhub {
-namespace V1_0 {
-namespace implementation {
-
-class BufferNode {
-public:
-    // Allocates a new BufferNode.
-    BufferNode(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format,
-               uint64_t usage, size_t userMetadataSize, int id = -1);
-
-    ~BufferNode();
-
-    // Returns whether the object holds a valid metadata.
-    bool isValid() const { return mMetadata.isValid(); }
-
-    int id() const { return mId; }
-
-    size_t userMetadataSize() const { return mMetadata.userMetadataSize(); }
-
-    // Accessors of the buffer description and handle
-    const native_handle_t* bufferHandle() const { return mBufferHandle; }
-    const AHardwareBuffer_Desc& bufferDesc() const { return mBufferDesc; }
-
-    // Accessor of event fd.
-    const BufferHubEventFd& eventFd() const { return mEventFd; }
-
-    // Accessors of mMetadata.
-    const BufferHubMetadata& metadata() const { return mMetadata; }
-
-    // Gets the current value of mActiveClientsBitMask in mMetadata with
-    // std::memory_order_acquire, so that all previous releases of
-    // mActiveClientsBitMask from all threads will be returned here.
-    uint32_t getActiveClientsBitMask() const;
-
-    // Find and add a new client state mask to mActiveClientsBitMask in
-    // mMetadata.
-    // Return the new client state mask that is added to mActiveClientsBitMask.
-    // Return 0U if there are already 16 clients of the buffer.
-    uint32_t addNewActiveClientsBitToMask();
-
-    // Removes the value from active_clients_bit_mask in mMetadata with
-    // std::memory_order_release, so that the change will be visible to any
-    // acquire of mActiveClientsBitMask in any threads after the succeed of
-    // this operation.
-    void removeClientsBitFromMask(const uint32_t& value);
-
-private:
-    // Helper method for constructors to initialize atomic metadata header
-    // variables in shared memory.
-    void initializeMetadata();
-
-    // Gralloc buffer handles.
-    native_handle_t* mBufferHandle;
-    AHardwareBuffer_Desc mBufferDesc;
-
-    // Eventfd used for signalling buffer events among the clients of the buffer.
-    BufferHubEventFd mEventFd;
-
-    // Metadata in shared memory.
-    BufferHubMetadata mMetadata;
-
-    // A system-unique id generated by bufferhub from 0 to std::numeric_limits<int>::max().
-    // BufferNodes not created by bufferhub will have id < 0, meaning "not specified".
-    // TODO(b/118891412): remove default id = -1 and update comments after pdx is no longer in use
-    const int mId = -1;
-
-    // The following variables are atomic variables in mMetadata that are visible
-    // to Bn object and Bp objects. Please find more info in
-    // BufferHubDefs::MetadataHeader.
-
-    // mBufferState tracks the state of the buffer. Buffer can be in one of these
-    // four states: gained, posted, acquired, released.
-    std::atomic<uint32_t>* mBufferState = nullptr;
-
-    // TODO(b/112012161): add comments to mFenceState.
-    std::atomic<uint32_t>* mFenceState = nullptr;
-
-    // mActiveClientsBitMask tracks all the bp clients of the buffer. It is the
-    // union of all client_state_mask of all bp clients.
-    std::atomic<uint32_t>* mActiveClientsBitMask = nullptr;
-};
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferhub
-} // namespace frameworks
-} // namespace android
-
-#endif // ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_NODE_H_
diff --git a/services/bufferhub/main_bufferhub.cpp b/services/bufferhub/main_bufferhub.cpp
deleted file mode 100644
index 084460d..0000000
--- a/services/bufferhub/main_bufferhub.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <bufferhub/BufferHubService.h>
-#include <hidl/HidlTransportSupport.h>
-#include <hwbinder/IPCThreadState.h>
-#include <log/log.h>
-
-using android::sp;
-using android::frameworks::bufferhub::V1_0::IBufferHub;
-using android::frameworks::bufferhub::V1_0::implementation::BufferHubService;
-
-int main(int /*argc*/, char** /*argv*/) {
-    ALOGI("Bootstrap bufferhub HIDL service.");
-
-    android::hardware::configureRpcThreadpool(/*numThreads=*/1, /*willJoin=*/true);
-
-    sp<IBufferHub> service = new BufferHubService();
-    LOG_ALWAYS_FATAL_IF(service->registerAsService() != android::OK, "Failed to register service");
-
-    android::hardware::joinRpcThreadpool();
-
-    return 0;
-}
diff --git a/services/bufferhub/tests/Android.bp b/services/bufferhub/tests/Android.bp
deleted file mode 100644
index 3033e70..0000000
--- a/services/bufferhub/tests/Android.bp
+++ /dev/null
@@ -1,26 +0,0 @@
-cc_test {
-    name: "BufferHubServer_test",
-    srcs: [
-        "BufferNode_test.cpp",
-        "BufferHubIdGenerator_test.cpp",
-    ],
-    cflags: [
-        "-DLOG_TAG=\"BufferHubServer_test\"",
-        "-DTRACE=0",
-        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
-        "-Wall",
-        "-Werror",
-    ],
-    compile_multilib: "first",
-    header_libs: [
-        "libdvr_headers",
-        "libnativewindow_headers",
-    ],
-    shared_libs: [
-        "libbufferhubservice",
-        "libui",
-    ],
-    static_libs: [
-        "libgmock",
-    ],
-}
diff --git a/services/bufferhub/tests/BufferHubIdGenerator_test.cpp b/services/bufferhub/tests/BufferHubIdGenerator_test.cpp
deleted file mode 100644
index fb6de0d..0000000
--- a/services/bufferhub/tests/BufferHubIdGenerator_test.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#include <bufferhub/BufferHubIdGenerator.h>
-#include <gtest/gtest.h>
-
-namespace android {
-namespace frameworks {
-namespace bufferhub {
-namespace V1_0 {
-namespace implementation {
-
-namespace {
-
-class BufferHubIdGeneratorTest : public testing::Test {
-protected:
-    BufferHubIdGenerator* mIdGenerator = &BufferHubIdGenerator::getInstance();
-};
-
-TEST_F(BufferHubIdGeneratorTest, TestGenerateAndFreeID) {
-    int id = mIdGenerator->getId();
-    EXPECT_GE(id, 0);
-
-    mIdGenerator->freeId(id);
-}
-
-TEST_F(BufferHubIdGeneratorTest, TestGenerateUniqueIncrementalID) {
-    // 10 IDs should not overflow the UniqueIdGenerator to cause a roll back to start, so the
-    // resulting IDs should still keep incresing.
-    const int kTestSize = 10;
-    int ids[kTestSize];
-    for (int i = 0; i < kTestSize; ++i) {
-        ids[i] = mIdGenerator->getId();
-        EXPECT_GE(ids[i], 0);
-        if (i >= 1) {
-            EXPECT_GT(ids[i], ids[i - 1]);
-        }
-    }
-
-    for (int i = 0; i < kTestSize; ++i) {
-        mIdGenerator->freeId(ids[i]);
-    }
-}
-
-} // namespace
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferhub
-} // namespace frameworks
-} // namespace android
\ No newline at end of file
diff --git a/services/bufferhub/tests/BufferNode_test.cpp b/services/bufferhub/tests/BufferNode_test.cpp
deleted file mode 100644
index 2dfd4fc..0000000
--- a/services/bufferhub/tests/BufferNode_test.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-#include <errno.h>
-
-#include <bufferhub/BufferNode.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <ui/BufferHubDefs.h>
-#include <ui/GraphicBufferMapper.h>
-
-namespace android {
-namespace frameworks {
-namespace bufferhub {
-namespace V1_0 {
-namespace implementation {
-
-namespace {
-
-using testing::NotNull;
-
-const uint32_t kWidth = 640;
-const uint32_t kHeight = 480;
-const uint32_t kLayerCount = 1;
-const uint32_t kFormat = 1;
-const uint64_t kUsage = 0;
-const size_t kUserMetadataSize = 0;
-
-class BufferNodeTest : public ::testing::Test {
-protected:
-    void SetUp() override {
-        mBufferNode =
-                new BufferNode(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
-        ASSERT_TRUE(mBufferNode->isValid());
-    }
-
-    void TearDown() override {
-        if (mBufferNode != nullptr) {
-            delete mBufferNode;
-        }
-    }
-
-    BufferNode* mBufferNode = nullptr;
-};
-
-TEST_F(BufferNodeTest, TestCreateBufferNode) {
-    EXPECT_EQ(mBufferNode->userMetadataSize(), kUserMetadataSize);
-    // Test the handle just allocated is good (i.e. able to be imported)
-    GraphicBufferMapper& mapper = GraphicBufferMapper::get();
-    const native_handle_t* outHandle;
-    status_t ret =
-            mapper.importBuffer(mBufferNode->bufferHandle(), mBufferNode->bufferDesc().width,
-                                mBufferNode->bufferDesc().height, mBufferNode->bufferDesc().layers,
-                                mBufferNode->bufferDesc().format, mBufferNode->bufferDesc().usage,
-                                mBufferNode->bufferDesc().stride, &outHandle);
-    EXPECT_EQ(ret, OK);
-    EXPECT_THAT(outHandle, NotNull());
-}
-
-TEST_F(BufferNodeTest, TestaddNewActiveClientsBitToMask_twoNewClients) {
-    uint32_t newClientStateMask1 = mBufferNode->addNewActiveClientsBitToMask();
-    EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), newClientStateMask1);
-
-    // Request and add a new client_state_mask again.
-    // Active clients bit mask should be the union of the two new
-    // client_state_masks.
-    uint32_t newClientStateMask2 = mBufferNode->addNewActiveClientsBitToMask();
-    EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), newClientStateMask1 | newClientStateMask2);
-}
-
-TEST_F(BufferNodeTest, TestaddNewActiveClientsBitToMask_32NewClients) {
-    uint32_t newClientStateMask = 0U;
-    uint32_t currentMask = 0U;
-    uint32_t expectedMask = 0U;
-
-    for (int i = 0; i < BufferHubDefs::kMaxNumberOfClients; ++i) {
-        newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
-        EXPECT_NE(newClientStateMask, 0U);
-        EXPECT_FALSE(newClientStateMask & currentMask);
-        expectedMask = currentMask | newClientStateMask;
-        currentMask = mBufferNode->getActiveClientsBitMask();
-        EXPECT_EQ(currentMask, expectedMask);
-    }
-
-    // Method should fail upon requesting for more than maximum allowable clients.
-    newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
-    EXPECT_EQ(newClientStateMask, 0U);
-    EXPECT_EQ(errno, E2BIG);
-}
-
-TEST_F(BufferNodeTest, TestRemoveActiveClientsBitFromMask) {
-    mBufferNode->addNewActiveClientsBitToMask();
-    uint32_t currentMask = mBufferNode->getActiveClientsBitMask();
-    uint32_t newClientStateMask = mBufferNode->addNewActiveClientsBitToMask();
-    EXPECT_NE(mBufferNode->getActiveClientsBitMask(), currentMask);
-
-    mBufferNode->removeClientsBitFromMask(newClientStateMask);
-    EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), currentMask);
-
-    // Remove the test_mask again to the active client bit mask should not modify
-    // the value of active clients bit mask.
-    mBufferNode->removeClientsBitFromMask(newClientStateMask);
-    EXPECT_EQ(mBufferNode->getActiveClientsBitMask(), currentMask);
-}
-
-} // namespace
-
-} // namespace implementation
-} // namespace V1_0
-} // namespace bufferhub
-} // namespace frameworks
-} // namespace android
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 7b8e0f8..6eed24a 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -1,20 +1,6 @@
-filegroup {
-    name: "gpuservice_sources",
-    srcs: [
-        "GpuService.cpp",
-        "gpustats/GpuStats.cpp"
-    ],
-}
-
-filegroup {
-    name: "gpuservice_binary_sources",
-    srcs: ["main_gpuservice.cpp"],
-}
-
 cc_defaults {
     name: "gpuservice_defaults",
     cflags: [
-        "-DLOG_TAG=\"GpuService\"",
         "-Wall",
         "-Werror",
         "-Wformat",
@@ -22,31 +8,38 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
-    srcs: [
-        ":gpuservice_sources",
-    ],
-    include_dirs: [
-        "frameworks/native/vulkan/vkjson",
-        "frameworks/native/vulkan/include",
+}
+
+cc_defaults {
+    name: "libgpuservice_defaults",
+    defaults: ["gpuservice_defaults"],
+    cflags: [
+        "-DLOG_TAG=\"GpuService\"",
     ],
     shared_libs: [
         "libbase",
         "libbinder",
         "libcutils",
+        "libgfxstats",
         "libgraphicsenv",
         "liblog",
         "libutils",
-        "libvulkan",
+        "libvkjson",
     ],
     static_libs: [
         "libserviceutils",
-        "libvkjson",
+    ],
+    export_static_lib_headers: [
+        "libserviceutils",
+    ],
+    export_shared_lib_headers: [
+        "libgraphicsenv",
     ],
 }
 
 cc_defaults {
-    name: "gpuservice_production_defaults",
-    defaults: ["gpuservice_defaults"],
+    name: "libgpuservice_production_defaults",
+    defaults: ["libgpuservice_defaults"],
     cflags: [
         "-fvisibility=hidden",
         "-fwhole-program-vtables", // requires ThinLTO
@@ -56,8 +49,23 @@
     },
 }
 
+filegroup {
+    name: "libgpuservice_sources",
+    srcs: [
+        "GpuService.cpp",
+    ],
+}
+
+cc_library_shared {
+    name: "libgpuservice",
+    defaults: ["libgpuservice_production_defaults"],
+    srcs: [
+        ":libgpuservice_sources",
+    ],
+}
+
 cc_defaults {
-    name: "gpuservice_binary",
+    name: "libgpuservice_binary",
     defaults: ["gpuservice_defaults"],
     shared_libs: [
         "libbinder",
@@ -68,9 +76,17 @@
     ldflags: ["-Wl,--export-dynamic"],
 }
 
+filegroup {
+    name: "gpuservice_binary_sources",
+    srcs: ["main_gpuservice.cpp"],
+}
+
 cc_binary {
     name: "gpuservice",
-    defaults: ["gpuservice_binary"],
+    defaults: ["libgpuservice_binary"],
     init_rc: ["gpuservice.rc"],
     srcs: [":gpuservice_binary_sources"],
+    shared_libs: [
+        "libgpuservice",
+    ],
 }
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index c8253e0..81b0a46 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -24,14 +24,13 @@
 #include <binder/Parcel.h>
 #include <binder/PermissionCache.h>
 #include <cutils/properties.h>
+#include <gpustats/GpuStats.h>
 #include <private/android_filesystem_config.h>
 #include <utils/String8.h>
 #include <utils/Trace.h>
 
 #include <vkjson.h>
 
-#include "gpustats/GpuStats.h"
-
 namespace android {
 
 using base::StringAppendF;
@@ -51,27 +50,38 @@
 void GpuService::setGpuStats(const std::string& driverPackageName,
                              const std::string& driverVersionName, uint64_t driverVersionCode,
                              int64_t driverBuildTime, const std::string& appPackageName,
-                             const int32_t vulkanVersion, GraphicsEnv::Driver driver,
+                             const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
                              bool isDriverLoaded, int64_t driverLoadingTime) {
-    mGpuStats->insert(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime,
-                      appPackageName, vulkanVersion, driver, isDriverLoaded, driverLoadingTime);
-}
-
-status_t GpuService::getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const {
-    mGpuStats->pullGlobalStats(outStats);
-    return OK;
-}
-
-status_t GpuService::getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const {
-    mGpuStats->pullAppStats(outStats);
-    return OK;
+    mGpuStats->insertDriverStats(driverPackageName, driverVersionName, driverVersionCode,
+                                 driverBuildTime, appPackageName, vulkanVersion, driver,
+                                 isDriverLoaded, driverLoadingTime);
 }
 
 void GpuService::setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
-                                const GraphicsEnv::Stats stats, const uint64_t value) {
+                                const GpuStatsInfo::Stats stats, const uint64_t value) {
     mGpuStats->insertTargetStats(appPackageName, driverVersionCode, stats, value);
 }
 
+void GpuService::setUpdatableDriverPath(const std::string& driverPath) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+
+    // only system_server is allowed to set updatable driver path
+    if (uid != AID_SYSTEM) {
+        ALOGE("Permission Denial: can't set updatable driver path from pid=%d, uid=%d\n", pid, uid);
+        return;
+    }
+
+    std::lock_guard<std::mutex> lock(mLock);
+    mDeveloperDriverPath = driverPath;
+}
+
+std::string GpuService::getUpdatableDriverPath() {
+    std::lock_guard<std::mutex> lock(mLock);
+    return mDeveloperDriverPath;
+}
+
 status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<String16>& args) {
     ATRACE_CALL();
 
@@ -99,22 +109,27 @@
         StringAppendF(&result, "Permission Denial: can't dump gpu from pid=%d, uid=%d\n", pid, uid);
     } else {
         bool dumpAll = true;
-        size_t index = 0;
+        bool dumpDriverInfo = false;
+        bool dumpStats = false;
         size_t numArgs = args.size();
 
         if (numArgs) {
-            if ((index < numArgs) && (args[index] == String16("--gpustats"))) {
-                index++;
-                mGpuStats->dump(args, &result);
-                dumpAll = false;
+            for (size_t index = 0; index < numArgs; ++index) {
+                if (args[index] == String16("--gpustats")) {
+                    dumpStats = true;
+                } else if (args[index] == String16("--gpudriverinfo")) {
+                    dumpDriverInfo = true;
+                }
             }
+            dumpAll = !(dumpDriverInfo || dumpStats);
         }
 
-        if (dumpAll) {
+        if (dumpAll || dumpDriverInfo) {
             dumpGameDriverInfo(&result);
             result.append("\n");
-
-            mGpuStats->dump(Vector<String16>(), &result);
+        }
+        if (dumpAll || dumpStats) {
+            mGpuStats->dump(args, &result);
             result.append("\n");
         }
     }
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index 7d44a35..d1c3aab 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -46,12 +46,12 @@
     void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
                      uint64_t driverVersionCode, int64_t driverBuildTime,
                      const std::string& appPackageName, const int32_t vulkanVersion,
-                     GraphicsEnv::Driver driver, bool isDriverLoaded,
+                     GpuStatsInfo::Driver driver, bool isDriverLoaded,
                      int64_t driverLoadingTime) override;
-    status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const override;
-    status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const override;
     void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
-                        const GraphicsEnv::Stats stats, const uint64_t value) override;
+                        const GpuStatsInfo::Stats stats, const uint64_t value) override;
+    void setUpdatableDriverPath(const std::string& driverPath) override;
+    std::string getUpdatableDriverPath() override;
 
     /*
      * IBinder interface
@@ -75,6 +75,8 @@
      * Attributes
      */
     std::unique_ptr<GpuStats> mGpuStats;
+    std::mutex mLock;
+    std::string mDeveloperDriverPath;
 };
 
 } // namespace android
diff --git a/services/gpuservice/TEST_MAPPING b/services/gpuservice/TEST_MAPPING
new file mode 100644
index 0000000..b345355
--- /dev/null
+++ b/services/gpuservice/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "gpuservice_unittest"
+    }
+  ]
+}
diff --git a/services/gpuservice/gpustats/Android.bp b/services/gpuservice/gpustats/Android.bp
new file mode 100644
index 0000000..f52602a
--- /dev/null
+++ b/services/gpuservice/gpustats/Android.bp
@@ -0,0 +1,29 @@
+cc_library_shared {
+    name: "libgfxstats",
+    srcs: [
+        "GpuStats.cpp",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libgraphicsenv",
+        "liblog",
+        "libprotoutil",
+        "libstatslog",
+        "libstatspull",
+        "libstatssocket",
+        "libutils",
+    ],
+    export_include_dirs: ["include"],
+    export_shared_lib_headers: [
+        "libstatspull",
+        "libstatssocket",
+    ],
+    cppflags: [
+        "-Wall",
+        "-Werror",
+        "-Wformat",
+        "-Wthread-safety",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index 5d27e72..231d068 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -17,30 +17,40 @@
 #define LOG_TAG "GpuStats"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include "GpuStats.h"
+#include "gpustats/GpuStats.h"
 
+#include <android/util/ProtoOutputStream.h>
 #include <cutils/properties.h>
 #include <log/log.h>
+#include <stats_event.h>
+#include <statslog.h>
 #include <utils/Trace.h>
 
 #include <unordered_set>
 
 namespace android {
 
-static void addLoadingCount(GraphicsEnv::Driver driver, bool isDriverLoaded,
+GpuStats::~GpuStats() {
+    if (mStatsdRegistered) {
+        AStatsManager_clearPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO);
+        AStatsManager_clearPullAtomCallback(android::util::GPU_STATS_APP_INFO);
+    }
+}
+
+static void addLoadingCount(GpuStatsInfo::Driver driver, bool isDriverLoaded,
                             GpuStatsGlobalInfo* const outGlobalInfo) {
     switch (driver) {
-        case GraphicsEnv::Driver::GL:
-        case GraphicsEnv::Driver::GL_UPDATED:
+        case GpuStatsInfo::Driver::GL:
+        case GpuStatsInfo::Driver::GL_UPDATED:
             outGlobalInfo->glLoadingCount++;
             if (!isDriverLoaded) outGlobalInfo->glLoadingFailureCount++;
             break;
-        case GraphicsEnv::Driver::VULKAN:
-        case GraphicsEnv::Driver::VULKAN_UPDATED:
+        case GpuStatsInfo::Driver::VULKAN:
+        case GpuStatsInfo::Driver::VULKAN_UPDATED:
             outGlobalInfo->vkLoadingCount++;
             if (!isDriverLoaded) outGlobalInfo->vkLoadingFailureCount++;
             break;
-        case GraphicsEnv::Driver::ANGLE:
+        case GpuStatsInfo::Driver::ANGLE:
             outGlobalInfo->angleLoadingCount++;
             if (!isDriverLoaded) outGlobalInfo->angleLoadingFailureCount++;
             break;
@@ -49,22 +59,22 @@
     }
 }
 
-static void addLoadingTime(GraphicsEnv::Driver driver, int64_t driverLoadingTime,
+static void addLoadingTime(GpuStatsInfo::Driver driver, int64_t driverLoadingTime,
                            GpuStatsAppInfo* const outAppInfo) {
     switch (driver) {
-        case GraphicsEnv::Driver::GL:
-        case GraphicsEnv::Driver::GL_UPDATED:
+        case GpuStatsInfo::Driver::GL:
+        case GpuStatsInfo::Driver::GL_UPDATED:
             if (outAppInfo->glDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) {
                 outAppInfo->glDriverLoadingTime.emplace_back(driverLoadingTime);
             }
             break;
-        case GraphicsEnv::Driver::VULKAN:
-        case GraphicsEnv::Driver::VULKAN_UPDATED:
+        case GpuStatsInfo::Driver::VULKAN:
+        case GpuStatsInfo::Driver::VULKAN_UPDATED:
             if (outAppInfo->vkDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) {
                 outAppInfo->vkDriverLoadingTime.emplace_back(driverLoadingTime);
             }
             break;
-        case GraphicsEnv::Driver::ANGLE:
+        case GpuStatsInfo::Driver::ANGLE:
             if (outAppInfo->angleDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) {
                 outAppInfo->angleDriverLoadingTime.emplace_back(driverLoadingTime);
             }
@@ -74,13 +84,15 @@
     }
 }
 
-void GpuStats::insert(const std::string& driverPackageName, const std::string& driverVersionName,
-                      uint64_t driverVersionCode, int64_t driverBuildTime,
-                      const std::string& appPackageName, const int32_t vulkanVersion,
-                      GraphicsEnv::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime) {
+void GpuStats::insertDriverStats(const std::string& driverPackageName,
+                                 const std::string& driverVersionName, uint64_t driverVersionCode,
+                                 int64_t driverBuildTime, const std::string& appPackageName,
+                                 const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
+                                 bool isDriverLoaded, int64_t driverLoadingTime) {
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mLock);
+    registerStatsdCallbacksIfNeeded();
     ALOGV("Received:\n"
           "\tdriverPackageName[%s]\n"
           "\tdriverVersionName[%s]\n"
@@ -127,21 +139,28 @@
 }
 
 void GpuStats::insertTargetStats(const std::string& appPackageName,
-                                 const uint64_t driverVersionCode, const GraphicsEnv::Stats stats,
+                                 const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
                                  const uint64_t /*value*/) {
     ATRACE_CALL();
 
     const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
 
     std::lock_guard<std::mutex> lock(mLock);
+    registerStatsdCallbacksIfNeeded();
     if (!mAppStats.count(appStatsKey)) {
         return;
     }
 
     switch (stats) {
-        case GraphicsEnv::Stats::CPU_VULKAN_IN_USE:
+        case GpuStatsInfo::Stats::CPU_VULKAN_IN_USE:
             mAppStats[appStatsKey].cpuVulkanInUse = true;
             break;
+        case GpuStatsInfo::Stats::FALSE_PREROTATION:
+            mAppStats[appStatsKey].falsePrerotation = true;
+            break;
+        case GpuStatsInfo::Stats::GLES_1_IN_USE:
+            mAppStats[appStatsKey].gles1InUse = true;
+            break;
         default:
             break;
     }
@@ -157,6 +176,16 @@
     mGlobalStats[0].glesVersion = property_get_int32("ro.opengles.version", 0);
 }
 
+void GpuStats::registerStatsdCallbacksIfNeeded() {
+    if (!mStatsdRegistered) {
+        AStatsManager_setPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO, nullptr,
+                                         GpuStats::pullAtomCallback, this);
+        AStatsManager_setPullAtomCallback(android::util::GPU_STATS_APP_INFO, nullptr,
+                                         GpuStats::pullAtomCallback, this);
+        mStatsdRegistered = true;
+    }
+}
+
 void GpuStats::dump(const Vector<String16>& args, std::string* result) {
     ATRACE_CALL();
 
@@ -185,6 +214,11 @@
         dumpAll = false;
     }
 
+    if (dumpAll) {
+        dumpGlobalLocked(result);
+        dumpAppLocked(result);
+    }
+
     if (argsSet.count("--clear")) {
         bool clearAll = true;
 
@@ -202,13 +236,6 @@
             mGlobalStats.clear();
             mAppStats.clear();
         }
-
-        dumpAll = false;
-    }
-
-    if (dumpAll) {
-        dumpGlobalLocked(result);
-        dumpAppLocked(result);
     }
 }
 
@@ -228,34 +255,114 @@
     }
 }
 
-void GpuStats::pullGlobalStats(std::vector<GpuStatsGlobalInfo>* outStats) {
-    ATRACE_CALL();
+static std::string protoOutputStreamToByteString(android::util::ProtoOutputStream& proto) {
+    if (!proto.size()) return "";
 
-    std::lock_guard<std::mutex> lock(mLock);
-    outStats->clear();
-    outStats->reserve(mGlobalStats.size());
-
-    interceptSystemDriverStatsLocked();
-
-    for (const auto& ele : mGlobalStats) {
-        outStats->emplace_back(ele.second);
+    std::string byteString;
+    sp<android::util::ProtoReader> reader = proto.data();
+    while (reader->readBuffer() != nullptr) {
+        const size_t toRead = reader->currentToRead();
+        byteString.append((char*)reader->readBuffer(), toRead);
+        reader->move(toRead);
     }
 
-    mGlobalStats.clear();
+    if (byteString.size() != proto.size()) return "";
+
+    return byteString;
 }
 
-void GpuStats::pullAppStats(std::vector<GpuStatsAppInfo>* outStats) {
+static std::string int64VectorToProtoByteString(const std::vector<int64_t>& value) {
+    if (value.empty()) return "";
+
+    android::util::ProtoOutputStream proto;
+    for (const auto& ele : value) {
+        proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+                            1 /* field id */,
+                    (long long)ele);
+    }
+
+    return protoOutputStreamToByteString(proto);
+}
+
+AStatsManager_PullAtomCallbackReturn GpuStats::pullAppInfoAtom(AStatsEventList* data) {
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mLock);
-    outStats->clear();
-    outStats->reserve(mAppStats.size());
 
-    for (const auto& ele : mAppStats) {
-        outStats->emplace_back(ele.second);
+    if (data) {
+        for (const auto& ele : mAppStats) {
+            AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+            AStatsEvent_setAtomId(event, android::util::GPU_STATS_APP_INFO);
+            AStatsEvent_writeString(event, ele.second.appPackageName.c_str());
+            AStatsEvent_writeInt64(event, ele.second.driverVersionCode);
+
+            std::string bytes = int64VectorToProtoByteString(ele.second.glDriverLoadingTime);
+            AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
+
+            bytes = int64VectorToProtoByteString(ele.second.vkDriverLoadingTime);
+            AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
+
+            bytes = int64VectorToProtoByteString(ele.second.angleDriverLoadingTime);
+            AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
+
+            AStatsEvent_writeBool(event, ele.second.cpuVulkanInUse);
+            AStatsEvent_writeBool(event, ele.second.falsePrerotation);
+            AStatsEvent_writeBool(event, ele.second.gles1InUse);
+            AStatsEvent_build(event);
+        }
     }
 
     mAppStats.clear();
+
+    return AStatsManager_PULL_SUCCESS;
+}
+
+AStatsManager_PullAtomCallbackReturn GpuStats::pullGlobalInfoAtom(AStatsEventList* data) {
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lock(mLock);
+    // flush cpuVulkanVersion and glesVersion to builtin driver stats
+    interceptSystemDriverStatsLocked();
+
+    if (data) {
+        for (const auto& ele : mGlobalStats) {
+            AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+            AStatsEvent_setAtomId(event, android::util::GPU_STATS_GLOBAL_INFO);
+            AStatsEvent_writeString(event, ele.second.driverPackageName.c_str());
+            AStatsEvent_writeString(event, ele.second.driverVersionName.c_str());
+            AStatsEvent_writeInt64(event, ele.second.driverVersionCode);
+            AStatsEvent_writeInt64(event, ele.second.driverBuildTime);
+            AStatsEvent_writeInt64(event, ele.second.glLoadingCount);
+            AStatsEvent_writeInt64(event, ele.second.glLoadingFailureCount);
+            AStatsEvent_writeInt64(event, ele.second.vkLoadingCount);
+            AStatsEvent_writeInt64(event, ele.second.vkLoadingFailureCount);
+            AStatsEvent_writeInt32(event, ele.second.vulkanVersion);
+            AStatsEvent_writeInt32(event, ele.second.cpuVulkanVersion);
+            AStatsEvent_writeInt32(event, ele.second.glesVersion);
+            AStatsEvent_writeInt64(event, ele.second.angleLoadingCount);
+            AStatsEvent_writeInt64(event, ele.second.angleLoadingFailureCount);
+            AStatsEvent_build(event);
+        }
+    }
+
+    mGlobalStats.clear();
+
+    return AStatsManager_PULL_SUCCESS;
+}
+
+AStatsManager_PullAtomCallbackReturn GpuStats::pullAtomCallback(int32_t atomTag,
+                                                                AStatsEventList* data,
+                                                                void* cookie) {
+    ATRACE_CALL();
+
+    GpuStats* pGpuStats = reinterpret_cast<GpuStats*>(cookie);
+    if (atomTag == android::util::GPU_STATS_GLOBAL_INFO) {
+        return pGpuStats->pullGlobalInfoAtom(data);
+    } else if (atomTag == android::util::GPU_STATS_APP_INFO) {
+        return pGpuStats->pullAppInfoAtom(data);
+    }
+
+    return AStatsManager_PULL_SKIP;
 }
 
 } // namespace android
diff --git a/services/gpuservice/gpustats/GpuStats.h b/services/gpuservice/gpustats/GpuStats.h
deleted file mode 100644
index 378f7f4..0000000
--- a/services/gpuservice/gpustats/GpuStats.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <mutex>
-#include <unordered_map>
-#include <vector>
-
-#include <graphicsenv/GpuStatsInfo.h>
-#include <graphicsenv/GraphicsEnv.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-
-namespace android {
-
-class GpuStats {
-public:
-    GpuStats() = default;
-    ~GpuStats() = default;
-
-    // Insert new gpu stats into global stats and app stats.
-    void insert(const std::string& driverPackageName, const std::string& driverVersionName,
-                uint64_t driverVersionCode, int64_t driverBuildTime,
-                const std::string& appPackageName, const int32_t vulkanVersion,
-                GraphicsEnv::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime);
-    // Insert target stats into app stats or potentially global stats as well.
-    void insertTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
-                           const GraphicsEnv::Stats stats, const uint64_t value);
-    // dumpsys interface
-    void dump(const Vector<String16>& args, std::string* result);
-    // Pull gpu global stats
-    void pullGlobalStats(std::vector<GpuStatsGlobalInfo>* outStats);
-    // Pull gpu app stats
-    void pullAppStats(std::vector<GpuStatsAppInfo>* outStats);
-
-    // This limits the worst case number of loading times tracked.
-    static const size_t MAX_NUM_LOADING_TIMES = 50;
-
-private:
-    // Dump global stats
-    void dumpGlobalLocked(std::string* result);
-    // Dump app stats
-    void dumpAppLocked(std::string* result);
-    // Append cpuVulkanVersion and glesVersion to system driver stats
-    void interceptSystemDriverStatsLocked();
-
-    // Below limits the memory usage of GpuStats to be less than 10KB. This is
-    // the preferred number for statsd while maintaining nice data quality.
-    static const size_t MAX_NUM_APP_RECORDS = 100;
-    // GpuStats access should be guarded by mLock.
-    std::mutex mLock;
-    // Key is driver version code.
-    std::unordered_map<uint64_t, GpuStatsGlobalInfo> mGlobalStats;
-    // Key is <app package name>+<driver version code>.
-    std::unordered_map<std::string, GpuStatsAppInfo> mAppStats;
-};
-
-} // namespace android
diff --git a/services/gpuservice/gpustats/include/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
new file mode 100644
index 0000000..55f0da1
--- /dev/null
+++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <graphicsenv/GpuStatsInfo.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <stats_pull_atom_callback.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+
+class GpuStats {
+public:
+    ~GpuStats();
+
+    // Insert new gpu driver stats into global stats and app stats.
+    void insertDriverStats(const std::string& driverPackageName,
+                           const std::string& driverVersionName, uint64_t driverVersionCode,
+                           int64_t driverBuildTime, const std::string& appPackageName,
+                           const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
+                           bool isDriverLoaded, int64_t driverLoadingTime);
+    // Insert target stats into app stats or potentially global stats as well.
+    void insertTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
+                           const GpuStatsInfo::Stats stats, const uint64_t value);
+    // dumpsys interface
+    void dump(const Vector<String16>& args, std::string* result);
+
+    // This limits the worst case number of loading times tracked.
+    static const size_t MAX_NUM_LOADING_TIMES = 50;
+
+private:
+    // Friend class for testing.
+    friend class TestableGpuStats;
+
+    // Native atom puller callback registered in statsd.
+    static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag,
+                                                                 AStatsEventList* data,
+                                                                 void* cookie);
+    // Pull global into into global atom.
+    AStatsManager_PullAtomCallbackReturn pullGlobalInfoAtom(AStatsEventList* data);
+    // Pull app into into app atom.
+    AStatsManager_PullAtomCallbackReturn pullAppInfoAtom(AStatsEventList* data);
+    // Dump global stats
+    void dumpGlobalLocked(std::string* result);
+    // Dump app stats
+    void dumpAppLocked(std::string* result);
+    // Append cpuVulkanVersion and glesVersion to system driver stats
+    void interceptSystemDriverStatsLocked();
+    // Registers statsd callbacks if they have not already been registered
+    void registerStatsdCallbacksIfNeeded();
+
+    // Below limits the memory usage of GpuStats to be less than 10KB. This is
+    // the preferred number for statsd while maintaining nice data quality.
+    static const size_t MAX_NUM_APP_RECORDS = 100;
+    // GpuStats access should be guarded by mLock.
+    std::mutex mLock;
+    // True if statsd callbacks have been registered.
+    bool mStatsdRegistered = false;
+    // Key is driver version code.
+    std::unordered_map<uint64_t, GpuStatsGlobalInfo> mGlobalStats;
+    // Key is <app package name>+<driver version code>.
+    std::unordered_map<std::string, GpuStatsAppInfo> mAppStats;
+};
+
+} // namespace android
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
new file mode 100644
index 0000000..538506d
--- /dev/null
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -0,0 +1,36 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+    name: "gpuservice_unittest",
+    test_suites: ["device-tests"],
+    sanitize: {
+        address: true,
+    },
+    srcs: [
+        "GpuStatsTest.cpp",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libgfxstats",
+        "libgraphicsenv",
+        "liblog",
+        "libstatslog",
+        "libstatspull",
+        "libutils",
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+}
diff --git a/services/gpuservice/tests/unittests/AndroidTest.xml b/services/gpuservice/tests/unittests/AndroidTest.xml
new file mode 100644
index 0000000..66f51c7
--- /dev/null
+++ b/services/gpuservice/tests/unittests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for gpuservice_unittest">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="gpuservice_unittest->/data/local/tmp/gpuservice_unittest" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="gpuservice_unittest" />
+    </test>
+</configuration>
diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
new file mode 100644
index 0000000..37ebeae
--- /dev/null
+++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "gpuservice_unittest"
+
+#include <cutils/properties.h>
+#include <gmock/gmock.h>
+#include <gpustats/GpuStats.h>
+#include <gtest/gtest.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include "TestableGpuStats.h"
+
+namespace android {
+namespace {
+
+using testing::HasSubstr;
+
+// clang-format off
+#define BUILTIN_DRIVER_PKG_NAME   "system"
+#define BUILTIN_DRIVER_VER_NAME   "0"
+#define BUILTIN_DRIVER_VER_CODE   0
+#define BUILTIN_DRIVER_BUILD_TIME 123
+#define UPDATED_DRIVER_PKG_NAME   "updated"
+#define UPDATED_DRIVER_VER_NAME   "1"
+#define UPDATED_DRIVER_VER_CODE   1
+#define UPDATED_DRIVER_BUILD_TIME 234
+#define VULKAN_VERSION            345
+#define APP_PKG_NAME_1            "testapp1"
+#define APP_PKG_NAME_2            "testapp2"
+#define DRIVER_LOADING_TIME_1     678
+#define DRIVER_LOADING_TIME_2     789
+#define DRIVER_LOADING_TIME_3     891
+
+enum InputCommand : int32_t {
+    DUMP_ALL               = 0,
+    DUMP_GLOBAL            = 1,
+    DUMP_APP               = 2,
+    DUMP_ALL_THEN_CLEAR    = 3,
+    DUMP_GLOBAL_THEN_CLEAR = 4,
+    DUMP_APP_THEN_CLEAR    = 5,
+};
+// clang-format on
+
+class GpuStatsTest : public testing::Test {
+public:
+    GpuStatsTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    ~GpuStatsTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    std::string inputCommand(InputCommand cmd);
+
+    void SetUp() override {
+        mCpuVulkanVersion = property_get_int32("ro.cpuvulkan.version", 0);
+        mGlesVersion = property_get_int32("ro.opengles.version", 0);
+    }
+
+    std::unique_ptr<GpuStats> mGpuStats = std::make_unique<GpuStats>();
+    int32_t mCpuVulkanVersion = 0;
+    int32_t mGlesVersion = 0;
+};
+
+std::string GpuStatsTest::inputCommand(InputCommand cmd) {
+    std::string result;
+    Vector<String16> args;
+
+    switch (cmd) {
+        case InputCommand::DUMP_ALL:
+            break;
+        case InputCommand::DUMP_GLOBAL:
+            args.push_back(String16("--global"));
+            break;
+        case InputCommand::DUMP_APP:
+            args.push_back(String16("--app"));
+            break;
+        case InputCommand::DUMP_ALL_THEN_CLEAR:
+            args.push_back(String16("--clear"));
+            break;
+        case InputCommand::DUMP_GLOBAL_THEN_CLEAR:
+            args.push_back(String16("--global"));
+            args.push_back(String16("--clear"));
+            break;
+        case InputCommand::DUMP_APP_THEN_CLEAR:
+            args.push_back(String16("--app"));
+            args.push_back(String16("--clear"));
+            break;
+    }
+
+    mGpuStats->dump(args, &result);
+    return result;
+}
+
+TEST_F(GpuStatsTest, statsEmptyByDefault) {
+    ASSERT_TRUE(inputCommand(InputCommand::DUMP_ALL).empty());
+}
+
+TEST_F(GpuStatsTest, canInsertBuiltinDriverStats) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    std::string expectedResult = "driverPackageName = " + std::string(BUILTIN_DRIVER_PKG_NAME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverVersionName = " + std::string(BUILTIN_DRIVER_VER_NAME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverVersionCode = " + std::to_string(BUILTIN_DRIVER_VER_CODE);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverBuildTime = " + std::to_string(BUILTIN_DRIVER_BUILD_TIME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("glLoadingCount = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("glLoadingFailureCount = 0"));
+    expectedResult = "appPackageName = " + std::string(APP_PKG_NAME_1);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+    expectedResult = "driverVersionCode = " + std::to_string(BUILTIN_DRIVER_VER_CODE);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+    expectedResult = "glDriverLoadingTime: " + std::to_string(DRIVER_LOADING_TIME_1);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canInsertUpdatedDriverStats) {
+    mGpuStats->insertDriverStats(UPDATED_DRIVER_PKG_NAME, UPDATED_DRIVER_VER_NAME,
+                                 UPDATED_DRIVER_VER_CODE, UPDATED_DRIVER_BUILD_TIME, APP_PKG_NAME_2,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::VULKAN_UPDATED, false,
+                                 DRIVER_LOADING_TIME_2);
+
+    std::string expectedResult = "driverPackageName = " + std::string(UPDATED_DRIVER_PKG_NAME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverVersionName = " + std::string(UPDATED_DRIVER_VER_NAME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverVersionCode = " + std::to_string(UPDATED_DRIVER_VER_CODE);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverBuildTime = " + std::to_string(UPDATED_DRIVER_BUILD_TIME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("vkLoadingCount = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("vkLoadingFailureCount = 1"));
+    expectedResult = "appPackageName = " + std::string(APP_PKG_NAME_2);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+    expectedResult = "driverVersionCode = " + std::to_string(UPDATED_DRIVER_VER_CODE);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+    expectedResult = "vkDriverLoadingTime: " + std::to_string(DRIVER_LOADING_TIME_2);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canInsertAngleDriverStats) {
+    mGpuStats->insertDriverStats(UPDATED_DRIVER_PKG_NAME, UPDATED_DRIVER_VER_NAME,
+                                 UPDATED_DRIVER_VER_CODE, UPDATED_DRIVER_BUILD_TIME, APP_PKG_NAME_2,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::ANGLE, true,
+                                 DRIVER_LOADING_TIME_3);
+
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("angleLoadingCount = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("angleLoadingFailureCount = 0"));
+    std::string expectedResult = "angleDriverLoadingTime: " + std::to_string(DRIVER_LOADING_TIME_3);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canDump3dApiVersion) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    std::string expectedResult = "vulkanVersion = " + std::to_string(VULKAN_VERSION);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "cpuVulkanVersion = " + std::to_string(mCpuVulkanVersion);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "glesVersion = " + std::to_string(mGlesVersion);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canNotInsertTargetStatsBeforeProperSetup) {
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::CPU_VULKAN_IN_USE, 0);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canInsertTargetStatsAfterProperSetup) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::CPU_VULKAN_IN_USE, 0);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("falsePrerotation = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1"));
+}
+
+TEST_F(GpuStatsTest, canDumpAllBeforeClearAll) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_ALL_THEN_CLEAR).empty());
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_ALL).empty());
+}
+
+TEST_F(GpuStatsTest, canDumpGlobalBeforeClearGlobal) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL_THEN_CLEAR).empty());
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canDumpAppBeforeClearApp) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP_THEN_CLEAR).empty());
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+}
+
+TEST_F(GpuStatsTest, skipPullInvalidAtom) {
+    TestableGpuStats testableGpuStats(mGpuStats.get());
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+
+    EXPECT_TRUE(testableGpuStats.makePullAtomCallback(-1) == AStatsManager_PULL_SKIP);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canPullGlobalAtom) {
+    TestableGpuStats testableGpuStats(mGpuStats.get());
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+
+    EXPECT_TRUE(testableGpuStats.makePullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO) ==
+                AStatsManager_PULL_SUCCESS);
+
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canPullAppAtom) {
+    TestableGpuStats testableGpuStats(mGpuStats.get());
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+
+    EXPECT_TRUE(testableGpuStats.makePullAtomCallback(android::util::GPU_STATS_APP_INFO) ==
+                AStatsManager_PULL_SUCCESS);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/gpuservice/tests/unittests/TestableGpuStats.h b/services/gpuservice/tests/unittests/TestableGpuStats.h
new file mode 100644
index 0000000..4ea564c
--- /dev/null
+++ b/services/gpuservice/tests/unittests/TestableGpuStats.h
@@ -0,0 +1,36 @@
+/*
+ * 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 <gpustats/GpuStats.h>
+#include <stdint.h>
+
+namespace android {
+
+class TestableGpuStats {
+public:
+    explicit TestableGpuStats(GpuStats *gpuStats) : mGpuStats(gpuStats) {}
+
+    AStatsManager_PullAtomCallbackReturn makePullAtomCallback(int32_t atomTag) {
+        return mGpuStats->pullAtomCallback(atomTag, nullptr, mGpuStats);
+    }
+
+private:
+    GpuStats *mGpuStats;
+};
+
+} // namespace android
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 04f6472..f67c9d0 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// Default flags to be used throughout all libraries in inputflinger.
 cc_defaults {
     name: "inputflinger_defaults",
     cflags: [
@@ -23,83 +24,106 @@
     ],
 }
 
-cc_library_shared {
-    name: "libinputflinger",
-    defaults: ["inputflinger_defaults"],
+/////////////////////////////////////////////////
+// libinputflinger
+/////////////////////////////////////////////////
 
+filegroup {
+    name: "libinputflinger_sources",
     srcs: [
         "InputClassifier.cpp",
         "InputClassifierConverter.cpp",
         "InputManager.cpp",
     ],
+}
 
+cc_defaults {
+    name: "libinputflinger_defaults",
+    srcs: [":libinputflinger_sources"],
     shared_libs: [
         "android.hardware.input.classifier@1.0",
         "libbase",
-        "libinputflinger_base",
-        "libinputreporter",
-        "libinputreader",
         "libbinder",
+        "libcrypto",
         "libcutils",
         "libhidlbase",
         "libinput",
         "liblog",
+        "libstatslog",
         "libutils",
         "libui",
     ],
+}
 
-    static_libs: [
-        "libinputdispatcher",
+cc_library_shared {
+    name: "libinputflinger",
+    defaults: [
+        "inputflinger_defaults",
+        "libinputflinger_defaults",
     ],
-
     cflags: [
         // TODO(b/23084678): Move inputflinger to its own process and mark it hidden
         //-fvisibility=hidden
     ],
-
+    shared_libs: [
+        // This should consist only of dependencies from inputflinger. Other dependencies should be
+        // in cc_defaults so that they are included in the tests.
+        "libinputflinger_base",
+        "libinputreporter",
+        "libinputreader",
+    ],
+    static_libs: [
+        "libinputdispatcher",
+    ],
+    export_static_lib_headers: [
+        "libinputdispatcher",
+    ],
     export_include_dirs: [
         ".",
         "include",
     ],
-
-    export_static_lib_headers: [
-        "libinputdispatcher",
-    ],
 }
 
+/////////////////////////////////////////////////
+// libinputflinger_base
+/////////////////////////////////////////////////
+
 cc_library_headers {
     name: "libinputflinger_headers",
-    header_libs: ["libinputreporter_headers"],
     export_include_dirs: ["include"],
-    export_header_lib_headers: ["libinputreporter_headers"],
 }
 
-cc_library_shared {
-    name: "libinputflinger_base",
-    defaults: ["inputflinger_defaults"],
-
+filegroup {
+    name: "libinputflinger_base_sources",
     srcs: [
         "InputListener.cpp",
         "InputReaderBase.cpp",
+        "InputThread.cpp",
     ],
+}
 
+cc_defaults {
+    name: "libinputflinger_base_defaults",
+    srcs: [":libinputflinger_base_sources"],
     shared_libs: [
         "libbase",
+        "libcutils",
         "libinput",
         "liblog",
         "libutils",
     ],
-
     header_libs: [
         "libinputflinger_headers",
     ],
-
-    export_header_lib_headers: [
-        "libinputflinger_headers",
-    ],
 }
 
-subdirs = [
-    "host",
-    "tests",
-]
+cc_library_shared {
+    name: "libinputflinger_base",
+    defaults: [
+        "inputflinger_defaults",
+        "libinputflinger_base_defaults",
+    ],
+    export_header_lib_headers: [
+        "libinputflinger_headers",
+    ],
+}
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
index db9f26e..b612ca7 100644
--- a/services/inputflinger/BlockingQueue.h
+++ b/services/inputflinger/BlockingQueue.h
@@ -45,10 +45,7 @@
     T pop() {
         std::unique_lock lock(mLock);
         android::base::ScopedLockAssertion assumeLock(mLock);
-        mHasElements.wait(lock, [this]{
-                android::base::ScopedLockAssertion assumeLock(mLock);
-                return !this->mQueue.empty();
-        });
+        mHasElements.wait(lock, [this]() REQUIRES(mLock) { return !this->mQueue.empty(); });
         T t = std::move(mQueue.front());
         mQueue.erase(mQueue.begin());
         return t;
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index cda0e0c..77a0716 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -191,7 +191,7 @@
             case ClassifierEventType::DEVICE_RESET: {
                 const int32_t deviceId = *(event.getDeviceId());
                 halResponseOk = mService->resetDevice(deviceId).isOk();
-                setClassification(deviceId, MotionClassification::NONE);
+                clearDeviceState(deviceId);
                 break;
             }
             case ClassifierEventType::HAL_RESET: {
@@ -217,7 +217,7 @@
     bool eventAdded = mEvents.push(std::move(event));
     if (!eventAdded) {
         // If the queue is full, suspect the HAL is slow in processing the events.
-        ALOGE("Dropped event with eventTime %" PRId64, event.args->eventTime);
+        ALOGE("Could not add the event to the queue. Resetting");
         reset();
     }
 }
@@ -262,6 +262,12 @@
     mClassifications[deviceId] = MotionClassification::NONE;
 }
 
+void MotionClassifier::clearDeviceState(int32_t deviceId) {
+    std::scoped_lock lock(mLock);
+    mClassifications.erase(deviceId);
+    mLastDownTimes.erase(deviceId);
+}
+
 MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
     if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
         updateLastDownTime(args.deviceId, args.downTime);
@@ -408,7 +414,6 @@
 void InputClassifier::dump(std::string& dump) {
     std::scoped_lock lock(mLock);
     dump += "Input Classifier State:\n";
-
     dump += INDENT1 "Motion Classifier:\n";
     if (mMotionClassifier) {
         mMotionClassifier->dump(dump);
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 9ac8e2d..03510a6 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -202,6 +202,8 @@
 
     void updateLastDownTime(int32_t deviceId, nsecs_t downTime);
 
+    void clearDeviceState(int32_t deviceId);
+
     /**
      * Exit the InputClassifier HAL thread.
      * Useful for tests to ensure proper cleanup.
diff --git a/services/inputflinger/InputClassifierConverter.cpp b/services/inputflinger/InputClassifierConverter.cpp
index f82c8ef..fc8c7c3 100644
--- a/services/inputflinger/InputClassifierConverter.cpp
+++ b/services/inputflinger/InputClassifierConverter.cpp
@@ -358,6 +358,7 @@
     event.displayId = args.displayId;
     event.downTime = args.downTime;
     event.eventTime = args.eventTime;
+    event.deviceTimestamp = 0;
     event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK);
     event.actionIndex = getActionIndex(args.action);
     event.actionButton = getActionButton(args.actionButton);
@@ -375,7 +376,6 @@
     event.pointerProperties = pointerProperties;
     event.pointerCoords = pointerCoords;
 
-    event.deviceTimestamp = args.deviceTimestamp;
     event.frames = convertVideoFrames(args.videoFrames);
 
     return event;
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 423b69c..84838ec 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -16,28 +16,32 @@
 
 #define LOG_TAG "InputListener"
 
+#define ATRACE_TAG ATRACE_TAG_INPUT
+
 //#define LOG_NDEBUG 0
 
 #include "InputListener.h"
 
+#include <android-base/stringprintf.h>
 #include <android/log.h>
+#include <math.h>
+#include <utils/Trace.h>
+
+using android::base::StringPrintf;
 
 namespace android {
 
 // --- NotifyConfigurationChangedArgs ---
 
-NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(
-        uint32_t sequenceNum, nsecs_t eventTime) :
-        NotifyArgs(sequenceNum, eventTime) {
-}
+NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime)
+      : NotifyArgs(id, eventTime) {}
 
 NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(
-        const NotifyConfigurationChangedArgs& other) :
-        NotifyArgs(other.sequenceNum, other.eventTime) {
-}
+        const NotifyConfigurationChangedArgs& other)
+      : NotifyArgs(other.id, other.eventTime) {}
 
 bool NotifyConfigurationChangedArgs::operator==(const NotifyConfigurationChangedArgs& rhs) const {
-    return sequenceNum == rhs.sequenceNum && eventTime == rhs.eventTime;
+    return id == rhs.id && eventTime == rhs.eventTime;
 }
 
 void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const {
@@ -47,37 +51,39 @@
 
 // --- NotifyKeyArgs ---
 
-NotifyKeyArgs::NotifyKeyArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId,
-        uint32_t source, int32_t displayId, uint32_t policyFlags,
-        int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
-        int32_t metaState, nsecs_t downTime) :
-        NotifyArgs(sequenceNum, eventTime), deviceId(deviceId), source(source),
-        displayId(displayId), policyFlags(policyFlags),
-        action(action), flags(flags), keyCode(keyCode), scanCode(scanCode),
-        metaState(metaState), downTime(downTime) {
-}
+NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                             int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
+                             int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime)
+      : NotifyArgs(id, eventTime),
+        deviceId(deviceId),
+        source(source),
+        displayId(displayId),
+        policyFlags(policyFlags),
+        action(action),
+        flags(flags),
+        keyCode(keyCode),
+        scanCode(scanCode),
+        metaState(metaState),
+        downTime(downTime) {}
 
-NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other) :
-        NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId),
-        source(other.source), displayId(other.displayId), policyFlags(other.policyFlags),
-        action(other.action), flags(other.flags),
-        keyCode(other.keyCode), scanCode(other.scanCode),
-        metaState(other.metaState), downTime(other.downTime) {
-}
+NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other)
+      : NotifyArgs(other.id, other.eventTime),
+        deviceId(other.deviceId),
+        source(other.source),
+        displayId(other.displayId),
+        policyFlags(other.policyFlags),
+        action(other.action),
+        flags(other.flags),
+        keyCode(other.keyCode),
+        scanCode(other.scanCode),
+        metaState(other.metaState),
+        downTime(other.downTime) {}
 
 bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const {
-    return sequenceNum == rhs.sequenceNum
-            && eventTime == rhs.eventTime
-            && deviceId == rhs.deviceId
-            && source == rhs.source
-            && displayId == rhs.displayId
-            && policyFlags == rhs.policyFlags
-            && action == rhs.action
-            && flags == rhs.flags
-            && keyCode == rhs.keyCode
-            && scanCode == rhs.scanCode
-            && metaState == rhs.metaState
-            && downTime == rhs.downTime;
+    return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId &&
+            source == rhs.source && displayId == rhs.displayId && policyFlags == rhs.policyFlags &&
+            action == rhs.action && flags == rhs.flags && keyCode == rhs.keyCode &&
+            scanCode == rhs.scanCode && metaState == rhs.metaState && downTime == rhs.downTime;
 }
 
 void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
@@ -87,21 +93,34 @@
 
 // --- NotifyMotionArgs ---
 
-NotifyMotionArgs::NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId,
-        uint32_t source, int32_t displayId, uint32_t policyFlags,
-        int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
-        int32_t buttonState, MotionClassification classification,
-        int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount,
-        const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-        float xPrecision, float yPrecision, nsecs_t downTime,
-        const std::vector<TouchVideoFrame>& videoFrames) :
-        NotifyArgs(sequenceNum, eventTime), deviceId(deviceId), source(source),
-        displayId(displayId), policyFlags(policyFlags),
-        action(action), actionButton(actionButton),
-        flags(flags), metaState(metaState), buttonState(buttonState),
-        classification(classification), edgeFlags(edgeFlags), deviceTimestamp(deviceTimestamp),
+NotifyMotionArgs::NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                                   int32_t displayId, uint32_t policyFlags, int32_t action,
+                                   int32_t actionButton, int32_t flags, int32_t metaState,
+                                   int32_t buttonState, MotionClassification classification,
+                                   int32_t edgeFlags, uint32_t pointerCount,
+                                   const PointerProperties* pointerProperties,
+                                   const PointerCoords* pointerCoords, float xPrecision,
+                                   float yPrecision, float xCursorPosition, float yCursorPosition,
+                                   nsecs_t downTime,
+                                   const std::vector<TouchVideoFrame>& videoFrames)
+      : NotifyArgs(id, eventTime),
+        deviceId(deviceId),
+        source(source),
+        displayId(displayId),
+        policyFlags(policyFlags),
+        action(action),
+        actionButton(actionButton),
+        flags(flags),
+        metaState(metaState),
+        buttonState(buttonState),
+        classification(classification),
+        edgeFlags(edgeFlags),
         pointerCount(pointerCount),
-        xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime),
+        xPrecision(xPrecision),
+        yPrecision(yPrecision),
+        xCursorPosition(xCursorPosition),
+        yCursorPosition(yCursorPosition),
+        downTime(downTime),
         videoFrames(videoFrames) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
@@ -109,14 +128,25 @@
     }
 }
 
-NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) :
-        NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId),
-        source(other.source), displayId(other.displayId), policyFlags(other.policyFlags),
-        action(other.action), actionButton(other.actionButton), flags(other.flags),
-        metaState(other.metaState), buttonState(other.buttonState),
-        classification(other.classification), edgeFlags(other.edgeFlags),
-        deviceTimestamp(other.deviceTimestamp), pointerCount(other.pointerCount),
-        xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime),
+NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other)
+      : NotifyArgs(other.id, other.eventTime),
+        deviceId(other.deviceId),
+        source(other.source),
+        displayId(other.displayId),
+        policyFlags(other.policyFlags),
+        action(other.action),
+        actionButton(other.actionButton),
+        flags(other.flags),
+        metaState(other.metaState),
+        buttonState(other.buttonState),
+        classification(other.classification),
+        edgeFlags(other.edgeFlags),
+        pointerCount(other.pointerCount),
+        xPrecision(other.xPrecision),
+        yPrecision(other.yPrecision),
+        xCursorPosition(other.xCursorPosition),
+        yCursorPosition(other.yCursorPosition),
+        downTime(other.downTime),
         videoFrames(other.videoFrames) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         pointerProperties[i].copyFrom(other.pointerProperties[i]);
@@ -124,28 +154,22 @@
     }
 }
 
+static inline bool isCursorPositionEqual(float lhs, float rhs) {
+    return (isnan(lhs) && isnan(rhs)) || lhs == rhs;
+}
+
 bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
-    bool equal =
-            sequenceNum == rhs.sequenceNum
-            && eventTime == rhs.eventTime
-            && deviceId == rhs.deviceId
-            && source == rhs.source
-            && displayId == rhs.displayId
-            && policyFlags == rhs.policyFlags
-            && action == rhs.action
-            && actionButton == rhs.actionButton
-            && flags == rhs.flags
-            && metaState == rhs.metaState
-            && buttonState == rhs.buttonState
-            && classification == rhs.classification
-            && edgeFlags == rhs.edgeFlags
-            && deviceTimestamp == rhs.deviceTimestamp
-            && pointerCount == rhs.pointerCount
+    bool equal = id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId &&
+            source == rhs.source && displayId == rhs.displayId && policyFlags == rhs.policyFlags &&
+            action == rhs.action && actionButton == rhs.actionButton && flags == rhs.flags &&
+            metaState == rhs.metaState && buttonState == rhs.buttonState &&
+            classification == rhs.classification && edgeFlags == rhs.edgeFlags &&
+            pointerCount == rhs.pointerCount
             // PointerProperties and PointerCoords are compared separately below
-            && xPrecision == rhs.xPrecision
-            && yPrecision == rhs.yPrecision
-            && downTime == rhs.downTime
-            && videoFrames == rhs.videoFrames;
+            && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision &&
+            isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) &&
+            isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) &&
+            downTime == rhs.downTime && videoFrames == rhs.videoFrames;
     if (!equal) {
         return false;
     }
@@ -168,23 +192,22 @@
 
 // --- NotifySwitchArgs ---
 
-NotifySwitchArgs::NotifySwitchArgs(uint32_t sequenceNum, nsecs_t eventTime, uint32_t policyFlags,
-        uint32_t switchValues, uint32_t switchMask) :
-        NotifyArgs(sequenceNum, eventTime), policyFlags(policyFlags),
-        switchValues(switchValues), switchMask(switchMask) {
-}
+NotifySwitchArgs::NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags,
+                                   uint32_t switchValues, uint32_t switchMask)
+      : NotifyArgs(id, eventTime),
+        policyFlags(policyFlags),
+        switchValues(switchValues),
+        switchMask(switchMask) {}
 
-NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other) :
-        NotifyArgs(other.sequenceNum, other.eventTime), policyFlags(other.policyFlags),
-        switchValues(other.switchValues), switchMask(other.switchMask) {
-}
+NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other)
+      : NotifyArgs(other.id, other.eventTime),
+        policyFlags(other.policyFlags),
+        switchValues(other.switchValues),
+        switchMask(other.switchMask) {}
 
 bool NotifySwitchArgs::operator==(const NotifySwitchArgs rhs) const {
-    return sequenceNum == rhs.sequenceNum
-            && eventTime == rhs.eventTime
-            && policyFlags == rhs.policyFlags
-            && switchValues == rhs.switchValues
-            && switchMask == rhs.switchMask;
+    return id == rhs.id && eventTime == rhs.eventTime && policyFlags == rhs.policyFlags &&
+            switchValues == rhs.switchValues && switchMask == rhs.switchMask;
 }
 
 void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const {
@@ -194,19 +217,14 @@
 
 // --- NotifyDeviceResetArgs ---
 
-NotifyDeviceResetArgs::NotifyDeviceResetArgs(
-        uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) :
-        NotifyArgs(sequenceNum, eventTime), deviceId(deviceId) {
-}
+NotifyDeviceResetArgs::NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId)
+      : NotifyArgs(id, eventTime), deviceId(deviceId) {}
 
-NotifyDeviceResetArgs::NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) :
-        NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId) {
-}
+NotifyDeviceResetArgs::NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other)
+      : NotifyArgs(other.id, other.eventTime), deviceId(other.deviceId) {}
 
 bool NotifyDeviceResetArgs::operator==(const NotifyDeviceResetArgs& rhs) const {
-    return sequenceNum == rhs.sequenceNum
-            && eventTime == rhs.eventTime
-            && deviceId == rhs.deviceId;
+    return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId;
 }
 
 void NotifyDeviceResetArgs::notify(const sp<InputListenerInterface>& listener) const {
@@ -216,6 +234,13 @@
 
 // --- QueuedInputListener ---
 
+static inline void traceEvent(const char* functionName, int32_t id) {
+    if (ATRACE_ENABLED()) {
+        std::string message = StringPrintf("%s(id=0x%" PRIx32 ")", functionName, id);
+        ATRACE_NAME(message.c_str());
+    }
+}
+
 QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
         mInnerListener(innerListener) {
 }
@@ -229,22 +254,27 @@
 
 void QueuedInputListener::notifyConfigurationChanged(
         const NotifyConfigurationChangedArgs* args) {
+    traceEvent(__func__, args->id);
     mArgsQueue.push_back(new NotifyConfigurationChangedArgs(*args));
 }
 
 void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
+    traceEvent(__func__, args->id);
     mArgsQueue.push_back(new NotifyKeyArgs(*args));
 }
 
 void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
+    traceEvent(__func__, args->id);
     mArgsQueue.push_back(new NotifyMotionArgs(*args));
 }
 
 void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) {
+    traceEvent(__func__, args->id);
     mArgsQueue.push_back(new NotifySwitchArgs(*args));
 }
 
 void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+    traceEvent(__func__, args->id);
     mArgsQueue.push_back(new NotifyDeviceResetArgs(*args));
 }
 
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index d6cc603..e68946d 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -20,7 +20,6 @@
 
 #include "InputManager.h"
 #include "InputDispatcherFactory.h"
-#include "InputDispatcherThread.h"
 #include "InputReaderFactory.h"
 
 #include <binder/IPCThreadState.h>
@@ -38,30 +37,24 @@
     mDispatcher = createInputDispatcher(dispatcherPolicy);
     mClassifier = new InputClassifier(mDispatcher);
     mReader = createInputReader(readerPolicy, mClassifier);
-    initialize();
 }
 
 InputManager::~InputManager() {
     stop();
 }
 
-void InputManager::initialize() {
-    mReaderThread = new InputReaderThread(mReader);
-    mDispatcherThread = new InputDispatcherThread(mDispatcher);
-}
-
 status_t InputManager::start() {
-    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
+    status_t result = mDispatcher->start();
     if (result) {
         ALOGE("Could not start InputDispatcher thread due to error %d.", result);
         return result;
     }
 
-    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+    result = mReader->start();
     if (result) {
-        ALOGE("Could not start InputReader thread due to error %d.", result);
+        ALOGE("Could not start InputReader due to error %d.", result);
 
-        mDispatcherThread->requestExit();
+        mDispatcher->stop();
         return result;
     }
 
@@ -69,17 +62,21 @@
 }
 
 status_t InputManager::stop() {
-    status_t result = mReaderThread->requestExitAndWait();
+    status_t status = OK;
+
+    status_t result = mReader->stop();
     if (result) {
-        ALOGW("Could not stop InputReader thread due to error %d.", result);
+        ALOGW("Could not stop InputReader due to error %d.", result);
+        status = result;
     }
 
-    result = mDispatcherThread->requestExitAndWait();
+    result = mDispatcher->stop();
     if (result) {
         ALOGW("Could not stop InputDispatcher thread due to error %d.", result);
+        status = result;
     }
 
-    return OK;
+    return status;
 }
 
 sp<InputReaderInterface> InputManager::getReader() {
@@ -114,8 +111,10 @@
         handlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>());
         handlesPerDisplay[info.displayId].push_back(new BinderWindowHandle(info));
     }
-    for (auto const& i : handlesPerDisplay) {
-        mDispatcher->setInputWindows(i.second, i.first, setInputWindowsListener);
+    mDispatcher->setInputWindows(handlesPerDisplay);
+
+    if (setInputWindowsListener) {
+        setInputWindowsListener->onSetInputWindowsFinished();
     }
 }
 
@@ -128,7 +127,7 @@
                 "from non shell/root entity (PID: %d)", ipc->getCallingPid());
         return;
     }
-    mDispatcher->registerInputChannel(channel, false);
+    mDispatcher->registerInputChannel(channel);
 }
 
 void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) {
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 5c07706..0158441 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -43,15 +43,15 @@
 /*
  * The input manager is the core of the system event processing.
  *
- * The input manager uses two threads.
+ * The input manager has two components.
  *
- * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events,
- *    applies policy, and posts messages to a queue managed by the DispatcherThread.
- * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
+ * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies
+ *    policy, and posts messages to a queue managed by the InputDispatcherThread.
+ * 2. The InputDispatcher class starts a thread that waits for new events on the
  *    queue and asynchronously dispatches them to applications.
  *
- * By design, the InputReaderThread class and InputDispatcherThread class do not share any
- * internal state.  Moreover, all communication is done one way from the InputReaderThread
+ * By design, the InputReader class and InputDispatcher class do not share any
+ * internal state.  Moreover, all communication is done one way from the InputReader
  * into the InputDispatcherThread and never the reverse.  Both classes may interact with the
  * InputDispatchPolicy, however.
  *
@@ -65,10 +65,10 @@
     virtual ~InputManagerInterface() { }
 
 public:
-    /* Starts the input manager threads. */
+    /* Starts the input threads. */
     virtual status_t start() = 0;
 
-    /* Stops the input manager threads and waits for them to exit. */
+    /* Stops the input threads and waits for them to exit. */
     virtual status_t stop() = 0;
 
     /* Gets the input reader. */
@@ -104,14 +104,10 @@
 
 private:
     sp<InputReaderInterface> mReader;
-    sp<InputReaderThread> mReaderThread;
 
     sp<InputClassifierInterface> mClassifier;
 
     sp<InputDispatcherInterface> mDispatcher;
-    sp<InputDispatcherThread> mDispatcherThread;
-
-    void initialize();
 };
 
 } // namespace android
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 0e2a2e7..b2dadf8 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -33,22 +33,46 @@
 
 namespace android {
 
-// --- InputReaderThread ---
-
-InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
-        Thread(/*canCallJava*/ true), mReader(reader) {
-}
-
-InputReaderThread::~InputReaderThread() {
-}
-
-bool InputReaderThread::threadLoop() {
-    mReader->loopOnce();
-    return true;
-}
-
 // --- InputReaderConfiguration ---
 
+std::string InputReaderConfiguration::changesToString(uint32_t changes) {
+    if (changes == 0) {
+        return "<none>";
+    }
+    std::string result;
+    if (changes & CHANGE_POINTER_SPEED) {
+        result += "POINTER_SPEED | ";
+    }
+    if (changes & CHANGE_POINTER_GESTURE_ENABLEMENT) {
+        result += "POINTER_GESTURE_ENABLEMENT | ";
+    }
+    if (changes & CHANGE_DISPLAY_INFO) {
+        result += "DISPLAY_INFO | ";
+    }
+    if (changes & CHANGE_SHOW_TOUCHES) {
+        result += "SHOW_TOUCHES | ";
+    }
+    if (changes & CHANGE_KEYBOARD_LAYOUTS) {
+        result += "KEYBOARD_LAYOUTS | ";
+    }
+    if (changes & CHANGE_DEVICE_ALIAS) {
+        result += "DEVICE_ALIAS | ";
+    }
+    if (changes & CHANGE_TOUCH_AFFINE_TRANSFORMATION) {
+        result += "TOUCH_AFFINE_TRANSFORMATION | ";
+    }
+    if (changes & CHANGE_EXTERNAL_STYLUS_PRESENCE) {
+        result += "EXTERNAL_STYLUS_PRESENCE | ";
+    }
+    if (changes & CHANGE_ENABLED_STATE) {
+        result += "ENABLED_STATE | ";
+    }
+    if (changes & CHANGE_MUST_REOPEN) {
+        result += "MUST_REOPEN | ";
+    }
+    return result;
+}
+
 std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByUniqueId(
         const std::string& uniqueDisplayId) const {
     if (uniqueDisplayId.empty()) {
@@ -76,8 +100,10 @@
     std::optional<DisplayViewport> result = std::nullopt;
     for (const DisplayViewport& currentViewport : mDisplays) {
         // Return the first match
-        if (currentViewport.type == type && !result) {
-            result = std::make_optional(currentViewport);
+        if (currentViewport.type == type) {
+            if (!result) {
+                result = std::make_optional(currentViewport);
+            }
             count++;
         }
     }
diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp
new file mode 100644
index 0000000..b87f7a1
--- /dev/null
+++ b/services/inputflinger/InputThread.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputThread.h"
+
+namespace android {
+
+namespace {
+
+// Implementation of Thread from libutils.
+class InputThreadImpl : public Thread {
+public:
+    explicit InputThreadImpl(std::function<void()> loop)
+          : Thread(/* canCallJava */ true), mThreadLoop(loop) {}
+
+    ~InputThreadImpl() {}
+
+private:
+    std::function<void()> mThreadLoop;
+
+    bool threadLoop() override {
+        mThreadLoop();
+        return true;
+    }
+};
+
+} // namespace
+
+InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake)
+      : mName(name), mThreadWake(wake) {
+    mThread = new InputThreadImpl(loop);
+    mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
+}
+
+InputThread::~InputThread() {
+    mThread->requestExit();
+    if (mThreadWake) {
+        mThreadWake();
+    }
+    mThread->requestExitAndWait();
+}
+
+bool InputThread::isCallingThread() {
+    return gettid() == mThread->getTid();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
new file mode 100644
index 0000000..066a816
--- /dev/null
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -0,0 +1,23 @@
+cc_benchmark {
+    name: "inputflinger_benchmarks",
+    srcs: [
+        "InputDispatcher_benchmarks.cpp",
+    ],
+    defaults: ["inputflinger_defaults"],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcrypto",
+        "libcutils",
+        "libinput",
+        "libinputflinger_base",
+        "libinputreporter",
+        "liblog",
+        "libstatslog",
+        "libui",
+        "libutils",
+    ],
+    static_libs: [
+        "libinputdispatcher",
+    ],
+}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
new file mode 100644
index 0000000..5a14133
--- /dev/null
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <benchmark/benchmark.h>
+
+#include <binder/Binder.h>
+#include "../dispatcher/InputDispatcher.h"
+
+namespace android::inputdispatcher {
+
+// An arbitrary device id.
+static const int32_t DEVICE_ID = 1;
+
+// An arbitrary injector pid / uid pair that has permission to inject events.
+static const int32_t INJECTOR_PID = 999;
+static const int32_t INJECTOR_UID = 1001;
+
+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);
+}
+
+// --- FakeInputDispatcherPolicy ---
+
+class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
+public:
+    FakeInputDispatcherPolicy() {}
+
+protected:
+    virtual ~FakeInputDispatcherPolicy() {}
+
+private:
+    virtual void notifyConfigurationChanged(nsecs_t) override {}
+
+    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;
+    }
+
+    virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
+
+    virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+
+    virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
+        *outConfig = mConfig;
+    }
+
+    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+        return true;
+    }
+
+    virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+
+    virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+
+    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*) override {
+        return false;
+    }
+
+    virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
+
+    virtual void pokeUserActivity(nsecs_t, int32_t) override {}
+
+    virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
+        return false;
+    }
+
+    virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
+
+    InputDispatcherConfiguration mConfig;
+};
+
+class FakeApplicationHandle : public InputApplicationHandle {
+public:
+    FakeApplicationHandle() {}
+    virtual ~FakeApplicationHandle() {}
+
+    virtual bool updateInfo() {
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+        return true;
+    }
+};
+
+class FakeInputReceiver {
+public:
+    void consumeEvent() {
+        uint32_t consumeSeq;
+        InputEvent* event;
+
+        std::chrono::time_point start = std::chrono::steady_clock::now();
+        status_t result = WOULD_BLOCK;
+        while (result == WOULD_BLOCK) {
+            std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
+            if (elapsed > 10ms) {
+                ALOGE("Waited too long for consumer to produce an event, giving up");
+                break;
+            }
+            result = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq,
+                                        &event);
+        }
+        if (result != OK) {
+            ALOGE("Received result = %d from consume()", result);
+        }
+        result = mConsumer->sendFinishedSignal(consumeSeq, true);
+        if (result != OK) {
+            ALOGE("Received result = %d from sendFinishedSignal", result);
+        }
+    }
+
+protected:
+    explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
+          : mDispatcher(dispatcher) {
+        InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+        mConsumer = std::make_unique<InputConsumer>(mClientChannel);
+    }
+
+    virtual ~FakeInputReceiver() {}
+
+    sp<InputDispatcher> mDispatcher;
+    sp<InputChannel> mServerChannel, mClientChannel;
+    std::unique_ptr<InputConsumer> mConsumer;
+    PreallocatedInputEventFactory mEventFactory;
+};
+
+class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
+public:
+    static const int32_t WIDTH = 200;
+    static const int32_t HEIGHT = 200;
+
+    FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+                     const sp<InputDispatcher>& dispatcher, const std::string name)
+          : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
+        mDispatcher->registerInputChannel(mServerChannel);
+
+        inputApplicationHandle->updateInfo();
+        mInfo.applicationInfo = *inputApplicationHandle->getInfo();
+    }
+
+    virtual bool updateInfo() override {
+        mInfo.token = mServerChannel->getConnectionToken();
+        mInfo.name = "FakeWindowHandle";
+        mInfo.layoutParamsFlags = 0;
+        mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+        mInfo.frameLeft = mFrame.left;
+        mInfo.frameTop = mFrame.top;
+        mInfo.frameRight = mFrame.right;
+        mInfo.frameBottom = mFrame.bottom;
+        mInfo.globalScaleFactor = 1.0;
+        mInfo.touchableRegion.clear();
+        mInfo.addTouchableRegion(mFrame);
+        mInfo.visible = true;
+        mInfo.canReceiveKeys = true;
+        mInfo.hasFocus = true;
+        mInfo.hasWallpaper = false;
+        mInfo.paused = false;
+        mInfo.ownerPid = INJECTOR_PID;
+        mInfo.ownerUid = INJECTOR_UID;
+        mInfo.inputFeatures = 0;
+        mInfo.displayId = ADISPLAY_ID_DEFAULT;
+
+        return true;
+    }
+
+protected:
+    Rect mFrame;
+};
+
+static MotionEvent generateMotionEvent() {
+    PointerProperties pointerProperties[1];
+    PointerCoords pointerCoords[1];
+
+    pointerProperties[0].clear();
+    pointerProperties[0].id = 0;
+    pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+    pointerCoords[0].clear();
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 100);
+
+    const nsecs_t currentTime = now();
+
+    MotionEvent event;
+    event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+                     ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
+                     /* actionButton */ 0, /* flags */ 0,
+                     /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+                     1 /* xScale */, 1 /* yScale */,
+                     /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
+                     /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    return event;
+}
+
+static NotifyMotionArgs generateMotionArgs() {
+    PointerProperties pointerProperties[1];
+    PointerCoords pointerCoords[1];
+
+    pointerProperties[0].clear();
+    pointerProperties[0].id = 0;
+    pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+    pointerCoords[0].clear();
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 100);
+
+    const nsecs_t currentTime = now();
+    // Define a valid motion event.
+    NotifyMotionArgs args(/* id */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+                          ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER, AMOTION_EVENT_ACTION_DOWN,
+                          /* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0,
+                          MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                          pointerProperties, pointerCoords,
+                          /* xPrecision */ 0, /* yPrecision */ 0,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {});
+
+    return args;
+}
+
+static void benchmarkNotifyMotion(benchmark::State& state) {
+    // Create dispatcher
+    sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
+    sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy);
+    dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+    dispatcher->start();
+
+    // Create a window that will receive motion events
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
+
+    dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    NotifyMotionArgs motionArgs = generateMotionArgs();
+
+    for (auto _ : state) {
+        // Send ACTION_DOWN
+        motionArgs.action = AMOTION_EVENT_ACTION_DOWN;
+        motionArgs.id = 0;
+        motionArgs.downTime = now();
+        motionArgs.eventTime = motionArgs.downTime;
+        dispatcher->notifyMotion(&motionArgs);
+
+        // Send ACTION_UP
+        motionArgs.action = AMOTION_EVENT_ACTION_UP;
+        motionArgs.id = 1;
+        motionArgs.eventTime = now();
+        dispatcher->notifyMotion(&motionArgs);
+
+        window->consumeEvent();
+        window->consumeEvent();
+    }
+
+    dispatcher->stop();
+}
+
+static void benchmarkInjectMotion(benchmark::State& state) {
+    // Create dispatcher
+    sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
+    sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy);
+    dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+    dispatcher->start();
+
+    // Create a window that will receive motion events
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
+
+    dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    for (auto _ : state) {
+        MotionEvent event = generateMotionEvent();
+        // Send ACTION_DOWN
+        dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                     INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
+                                     POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+
+        // Send ACTION_UP
+        event.setAction(AMOTION_EVENT_ACTION_UP);
+        dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                     INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
+                                     POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+
+        window->consumeEvent();
+        window->consumeEvent();
+    }
+
+    dispatcher->stop();
+}
+
+BENCHMARK(benchmarkNotifyMotion);
+BENCHMARK(benchmarkInjectMotion);
+
+} // namespace android::inputdispatcher
+
+BENCHMARK_MAIN();
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index b8c3a80..390c6b8 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -12,31 +12,60 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library_static {
-    name: "libinputdispatcher",
-    defaults: ["inputflinger_defaults"],
+cc_library_headers {
+    name: "libinputdispatcher_headers",
+    export_include_dirs: [
+        "include",
+    ],
+}
+
+filegroup {
+    name: "libinputdispatcher_sources",
     srcs: [
+        "AnrTracker.cpp",
         "Connection.cpp",
         "Entry.cpp",
         "InjectionState.cpp",
         "InputDispatcher.cpp",
         "InputDispatcherFactory.cpp",
-        "InputDispatcherThread.cpp",
         "InputState.cpp",
         "InputTarget.cpp",
         "Monitor.cpp",
-        "TouchState.cpp"
+        "TouchState.cpp",
     ],
+}
+
+cc_defaults {
+    name: "libinputdispatcher_defaults",
+    srcs: [":libinputdispatcher_sources"],
     shared_libs: [
         "libbase",
+        "libcrypto",
         "libcutils",
         "libinput",
-        "libinputreporter",
-        "libinputflinger_base",
         "liblog",
+        "libstatslog",
         "libui",
         "libutils",
     ],
+    header_libs: [
+        "libinputdispatcher_headers",
+    ],
+}
 
-    export_include_dirs: ["include"],
+cc_library_static {
+    name: "libinputdispatcher",
+    defaults: [
+        "inputflinger_defaults",
+        "libinputdispatcher_defaults",
+    ],
+    shared_libs: [
+        // This should consist only of dependencies from inputflinger. Other dependencies should be
+        // in cc_defaults so that they are included in the tests.
+        "libinputreporter",
+        "libinputflinger_base",
+    ],
+    export_header_lib_headers: [
+        "libinputdispatcher_headers",
+    ],
 }
diff --git a/services/inputflinger/dispatcher/AnrTracker.cpp b/services/inputflinger/dispatcher/AnrTracker.cpp
new file mode 100644
index 0000000..c3f611e
--- /dev/null
+++ b/services/inputflinger/dispatcher/AnrTracker.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AnrTracker.h"
+
+namespace android::inputdispatcher {
+
+template <typename T>
+static T max(const T& a, const T& b) {
+    return a < b ? b : a;
+}
+
+void AnrTracker::insert(nsecs_t timeoutTime, sp<IBinder> token) {
+    mAnrTimeouts.insert(std::make_pair(timeoutTime, std::move(token)));
+}
+
+/**
+ * Erase a single entry only. If there are multiple duplicate entries
+ * (same time, same connection), then only remove one of them.
+ */
+void AnrTracker::erase(nsecs_t timeoutTime, const sp<IBinder>& token) {
+    auto pair = std::make_pair(timeoutTime, token);
+    auto it = mAnrTimeouts.find(pair);
+    if (it != mAnrTimeouts.end()) {
+        mAnrTimeouts.erase(it);
+    }
+}
+
+void AnrTracker::eraseToken(const sp<IBinder>& token) {
+    for (auto it = mAnrTimeouts.begin(); it != mAnrTimeouts.end();) {
+        if (it->second == token) {
+            it = mAnrTimeouts.erase(it);
+        } else {
+            ++it;
+        }
+    }
+}
+
+bool AnrTracker::empty() const {
+    return mAnrTimeouts.empty();
+}
+
+// If empty() is false, return the time at which the next connection should cause an ANR
+// If empty() is true, return LONG_LONG_MAX
+nsecs_t AnrTracker::firstTimeout() const {
+    if (mAnrTimeouts.empty()) {
+        return std::numeric_limits<nsecs_t>::max();
+    }
+    return mAnrTimeouts.begin()->first;
+}
+
+const sp<IBinder>& AnrTracker::firstToken() const {
+    return mAnrTimeouts.begin()->second;
+}
+
+void AnrTracker::clear() {
+    mAnrTimeouts.clear();
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/AnrTracker.h b/services/inputflinger/dispatcher/AnrTracker.h
new file mode 100644
index 0000000..097dba5
--- /dev/null
+++ b/services/inputflinger/dispatcher/AnrTracker.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
+#define _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
+
+#include <binder/IBinder.h>
+#include <utils/Timers.h>
+#include <set>
+
+namespace android::inputdispatcher {
+
+/**
+ * Keeps track of the times when each connection is going to ANR.
+ * Provides the ability to quickly find the connection that is going to cause ANR next.
+ */
+class AnrTracker {
+public:
+    void insert(nsecs_t timeoutTime, sp<IBinder> token);
+    void erase(nsecs_t timeoutTime, const sp<IBinder>& token);
+    void eraseToken(const sp<IBinder>& token);
+    void clear();
+
+    bool empty() const;
+    // If empty() is false, return the time at which the next connection should cause an ANR
+    // If empty() is true, return LONG_LONG_MAX
+    nsecs_t firstTimeout() const;
+    // Return the token of the next connection that should cause an ANR.
+    // Do not call this unless empty() is false, you will encounter undefined behaviour.
+    const sp<IBinder>& firstToken() const;
+
+private:
+    // Optimization: use a multiset to keep track of the event timeouts. When an event is sent
+    // to the InputConsumer, we add an entry to this structure. We look at the smallest value to
+    // determine if any of the connections is unresponsive, and to determine when we should wake
+    // next for the future ANR check.
+    // Using a multiset helps quickly look up the next timeout due.
+    //
+    // We must use a multi-set, because it is plausible (although highly unlikely) to have entries
+    // from the same connection and same timestamp, but different sequence numbers.
+    // We are not tracking sequence numbers, and just allow duplicates to exist.
+    std::multiset<std::pair<nsecs_t /*timeoutTime*/, sp<IBinder> /*connectionToken*/>> mAnrTimeouts;
+};
+
+} // namespace android::inputdispatcher
+
+#endif // _UI_INPUT_INPUTDISPATCHER_ANRTRACKER_H
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index ef7f650..f5ea563 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -20,12 +20,13 @@
 
 namespace android::inputdispatcher {
 
-Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor)
+Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor,
+                       const IdGenerator& idGenerator)
       : status(STATUS_NORMAL),
         inputChannel(inputChannel),
         monitor(monitor),
         inputPublisher(inputChannel),
-        inputPublisherBlocked(false) {}
+        inputState(idGenerator) {}
 
 Connection::~Connection() {}
 
@@ -52,13 +53,13 @@
     }
 }
 
-DispatchEntry* Connection::findWaitQueueEntry(uint32_t seq) {
-    for (DispatchEntry* entry = waitQueue.head; entry != nullptr; entry = entry->next) {
-        if (entry->seq == seq) {
-            return entry;
+std::deque<DispatchEntry*>::iterator Connection::findWaitQueueEntry(uint32_t seq) {
+    for (std::deque<DispatchEntry*>::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) {
+        if ((*it)->seq == seq) {
+            return it;
         }
     }
-    return nullptr;
+    return waitQueue.end();
 }
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index ed4eebd..3b33f29 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -18,7 +18,6 @@
 #define _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
 
 #include "InputState.h"
-#include "Queue.h"
 
 #include <input/InputTransport.h>
 #include <deque>
@@ -48,25 +47,26 @@
     InputPublisher inputPublisher;
     InputState inputState;
 
-    // True if the socket is full and no further events can be published until
-    // the application consumes some of the input.
-    bool inputPublisherBlocked;
+    // True if this connection is responsive.
+    // If this connection is not responsive, avoid publishing more events to it until the
+    // application consumes some of the input.
+    bool responsive = true;
 
     // Queue of events that need to be published to the connection.
-    Queue<DispatchEntry> outboundQueue;
+    std::deque<DispatchEntry*> outboundQueue;
 
     // Queue of events that have been published to the connection but that have not
     // yet received a "finished" response from the application.
-    Queue<DispatchEntry> waitQueue;
+    std::deque<DispatchEntry*> waitQueue;
 
-    explicit Connection(const sp<InputChannel>& inputChannel, bool monitor);
+    Connection(const sp<InputChannel>& inputChannel, bool monitor, const IdGenerator& idGenerator);
 
     inline const std::string getInputChannelName() const { return inputChannel->getName(); }
 
     const std::string getWindowName() const;
     const char* getStatusLabel() const;
 
-    DispatchEntry* findWaitQueueEntry(uint32_t seq);
+    std::deque<DispatchEntry*>::iterator findWaitQueueEntry(uint32_t seq);
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index a4d9e4e..fdbb1d1 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -28,40 +28,37 @@
 
 namespace android::inputdispatcher {
 
-static std::string motionActionToString(int32_t action) {
-    // Convert MotionEvent action to string
-    switch (action & AMOTION_EVENT_ACTION_MASK) {
-        case AMOTION_EVENT_ACTION_DOWN:
-            return "DOWN";
-        case AMOTION_EVENT_ACTION_MOVE:
-            return "MOVE";
-        case AMOTION_EVENT_ACTION_UP:
-            return "UP";
-        case AMOTION_EVENT_ACTION_POINTER_DOWN:
-            return "POINTER_DOWN";
-        case AMOTION_EVENT_ACTION_POINTER_UP:
-            return "POINTER_UP";
-    }
-    return StringPrintf("%" PRId32, action);
+VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) {
+    return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source,
+             entry.displayId},
+            entry.action,
+            entry.downTime,
+            entry.flags & VERIFIED_KEY_EVENT_FLAGS,
+            entry.keyCode,
+            entry.scanCode,
+            entry.metaState,
+            entry.repeatCount};
 }
 
-static std::string keyActionToString(int32_t action) {
-    // Convert KeyEvent action to string
-    switch (action) {
-        case AKEY_EVENT_ACTION_DOWN:
-            return "DOWN";
-        case AKEY_EVENT_ACTION_UP:
-            return "UP";
-        case AKEY_EVENT_ACTION_MULTIPLE:
-            return "MULTIPLE";
-    }
-    return StringPrintf("%" PRId32, action);
+VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry) {
+    const float rawX = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
+    const float rawY = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
+    const int actionMasked = entry.action & AMOTION_EVENT_ACTION_MASK;
+    return {{VerifiedInputEvent::Type::MOTION, entry.deviceId, entry.eventTime, entry.source,
+             entry.displayId},
+            rawX,
+            rawY,
+            actionMasked,
+            entry.downTime,
+            entry.flags & VERIFIED_MOTION_EVENT_FLAGS,
+            entry.metaState,
+            entry.buttonState};
 }
 
 // --- EventEntry ---
 
-EventEntry::EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags)
-      : sequenceNum(sequenceNum),
+EventEntry::EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags)
+      : id(id),
         refCount(1),
         type(type),
         eventTime(eventTime),
@@ -73,6 +70,12 @@
     releaseInjectionState();
 }
 
+std::string EventEntry::getDescription() const {
+    std::string result;
+    appendDescription(result);
+    return result;
+}
+
 void EventEntry::release() {
     refCount -= 1;
     if (refCount == 0) {
@@ -91,8 +94,8 @@
 
 // --- ConfigurationChangedEntry ---
 
-ConfigurationChangedEntry::ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime)
-      : EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {}
+ConfigurationChangedEntry::ConfigurationChangedEntry(int32_t id, nsecs_t eventTime)
+      : EventEntry(id, Type::CONFIGURATION_CHANGED, eventTime, 0) {}
 
 ConfigurationChangedEntry::~ConfigurationChangedEntry() {}
 
@@ -102,8 +105,8 @@
 
 // --- DeviceResetEntry ---
 
-DeviceResetEntry::DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId)
-      : EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0), deviceId(deviceId) {}
+DeviceResetEntry::DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId)
+      : EventEntry(id, Type::DEVICE_RESET, eventTime, 0), deviceId(deviceId) {}
 
 DeviceResetEntry::~DeviceResetEntry() {}
 
@@ -111,13 +114,27 @@
     msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
 }
 
+// --- FocusEntry ---
+
+// Focus notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
+FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus)
+      : EventEntry(id, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER),
+        connectionToken(connectionToken),
+        hasFocus(hasFocus) {}
+
+FocusEntry::~FocusEntry() {}
+
+void FocusEntry::appendDescription(std::string& msg) const {
+    msg += StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
+}
+
 // --- KeyEntry ---
 
-KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
                    int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
                    int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
                    nsecs_t downTime)
-      : EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags),
+      : EventEntry(id, Type::KEY, eventTime, policyFlags),
         deviceId(deviceId),
         source(source),
         displayId(displayId),
@@ -142,7 +159,7 @@
     msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, "
                         "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, "
                         "repeatCount=%d), policyFlags=0x%08x",
-                        deviceId, source, displayId, keyActionToString(action).c_str(), flags,
+                        deviceId, source, displayId, KeyEvent::actionToString(action), flags,
                         keyCode, scanCode, metaState, repeatCount, policyFlags);
 }
 
@@ -157,14 +174,15 @@
 
 // --- MotionEntry ---
 
-MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
                          int32_t displayId, uint32_t policyFlags, int32_t action,
                          int32_t actionButton, int32_t flags, int32_t metaState,
                          int32_t buttonState, MotionClassification classification,
-                         int32_t edgeFlags, float xPrecision, float yPrecision, nsecs_t downTime,
+                         int32_t edgeFlags, float xPrecision, float yPrecision,
+                         float xCursorPosition, float yCursorPosition, nsecs_t downTime,
                          uint32_t pointerCount, const PointerProperties* pointerProperties,
                          const PointerCoords* pointerCoords, float xOffset, float yOffset)
-      : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags),
+      : EventEntry(id, Type::MOTION, eventTime, policyFlags),
         eventTime(eventTime),
         deviceId(deviceId),
         source(source),
@@ -178,6 +196,8 @@
         edgeFlags(edgeFlags),
         xPrecision(xPrecision),
         yPrecision(yPrecision),
+        xCursorPosition(xCursorPosition),
+        yCursorPosition(yCursorPosition),
         downTime(downTime),
         pointerCount(pointerCount) {
     for (uint32_t i = 0; i < pointerCount; i++) {
@@ -200,11 +220,11 @@
                         ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
                         "buttonState=0x%08x, "
                         "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
-                        "pointers=[",
-                        deviceId, source, displayId, motionActionToString(action).c_str(),
+                        "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
+                        deviceId, source, displayId, MotionEvent::actionToString(action),
                         actionButton, flags, metaState, buttonState,
                         motionClassificationToString(classification), edgeFlags, xPrecision,
-                        yPrecision);
+                        yPrecision, xCursorPosition, yCursorPosition);
 
     for (uint32_t i = 0; i < pointerCount; i++) {
         if (i) {
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index b904caf..6b7697d 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -29,41 +29,70 @@
 
 namespace android::inputdispatcher {
 
-template <typename T>
-struct Link {
-    T* next;
-    T* prev;
+struct EventEntry {
+    enum class Type {
+        CONFIGURATION_CHANGED,
+        DEVICE_RESET,
+        FOCUS,
+        KEY,
+        MOTION,
+    };
 
-protected:
-    inline Link() : next(nullptr), prev(nullptr) {}
-};
+    static const char* typeToString(Type type) {
+        switch (type) {
+            case Type::CONFIGURATION_CHANGED:
+                return "CONFIGURATION_CHANGED";
+            case Type::DEVICE_RESET:
+                return "DEVICE_RESET";
+            case Type::FOCUS:
+                return "FOCUS";
+            case Type::KEY:
+                return "KEY";
+            case Type::MOTION:
+                return "MOTION";
+        }
+    }
 
-struct EventEntry : Link<EventEntry> {
-    enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION };
-
-    uint32_t sequenceNum;
+    int32_t id;
     mutable int32_t refCount;
-    int32_t type;
+    Type type;
     nsecs_t eventTime;
     uint32_t policyFlags;
     InjectionState* injectionState;
 
     bool dispatchInProgress; // initially false, set to true while dispatching
 
+    /**
+     * Injected keys are events from an external (probably untrusted) application
+     * and are not related to real hardware state. They come in via
+     * InputDispatcher::injectInputEvent, which sets policy flag POLICY_FLAG_INJECTED.
+     */
     inline bool isInjected() const { return injectionState != nullptr; }
 
+    /**
+     * Synthesized events are either injected events, or events that come
+     * from real hardware, but aren't directly attributable to a specific hardware event.
+     * Key repeat is a synthesized event, because it is related to an actual hardware state
+     * (a key is currently pressed), but the repeat itself is generated by the framework.
+     */
+    inline bool isSynthesized() const {
+        return isInjected() || IdGenerator::getSource(id) != IdGenerator::Source::INPUT_READER;
+    }
+
     void release();
 
     virtual void appendDescription(std::string& msg) const = 0;
 
+    std::string getDescription() const;
+
 protected:
-    EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags);
+    EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags);
     virtual ~EventEntry();
     void releaseInjectionState();
 };
 
 struct ConfigurationChangedEntry : EventEntry {
-    explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime);
+    explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime);
     virtual void appendDescription(std::string& msg) const;
 
 protected:
@@ -73,13 +102,24 @@
 struct DeviceResetEntry : EventEntry {
     int32_t deviceId;
 
-    DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId);
+    DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId);
     virtual void appendDescription(std::string& msg) const;
 
 protected:
     virtual ~DeviceResetEntry();
 };
 
+struct FocusEntry : EventEntry {
+    sp<IBinder> connectionToken;
+    bool hasFocus;
+
+    FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus);
+    virtual void appendDescription(std::string& msg) const;
+
+protected:
+    virtual ~FocusEntry();
+};
+
 struct KeyEntry : EventEntry {
     int32_t deviceId;
     uint32_t source;
@@ -103,10 +143,9 @@
     InterceptKeyResult interceptKeyResult; // set based on the interception result
     nsecs_t interceptKeyWakeupTime;        // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
 
-    KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-             int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
-             int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
-             nsecs_t downTime);
+    KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
+             uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+             int32_t metaState, int32_t repeatCount, nsecs_t downTime);
     virtual void appendDescription(std::string& msg) const;
     void recycle();
 
@@ -128,16 +167,18 @@
     int32_t edgeFlags;
     float xPrecision;
     float yPrecision;
+    float xCursorPosition;
+    float yCursorPosition;
     nsecs_t downTime;
     uint32_t pointerCount;
     PointerProperties pointerProperties[MAX_POINTERS];
     PointerCoords pointerCoords[MAX_POINTERS];
 
-    MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
-                int32_t flags, int32_t metaState, int32_t buttonState,
-                MotionClassification classification, int32_t edgeFlags, float xPrecision,
-                float yPrecision, nsecs_t downTime, uint32_t pointerCount,
+    MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
+                uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
+                int32_t metaState, int32_t buttonState, MotionClassification classification,
+                int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition,
+                float yCursorPosition, nsecs_t downTime, uint32_t pointerCount,
                 const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
                 float xOffset, float yOffset);
     virtual void appendDescription(std::string& msg) const;
@@ -147,7 +188,7 @@
 };
 
 // Tracks the progress of dispatching a particular event to a particular connection.
-struct DispatchEntry : Link<DispatchEntry> {
+struct DispatchEntry {
     const uint32_t seq; // unique sequence number, never 0
 
     EventEntry* eventEntry; // the event to dispatch
@@ -157,9 +198,14 @@
     float globalScaleFactor;
     float windowXScale = 1.0f;
     float windowYScale = 1.0f;
+    // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app,
+    // and will be undefined before that.
     nsecs_t deliveryTime; // time when the event was actually delivered
+    // An ANR will be triggered if a response for this entry is not received by timeoutTime
+    nsecs_t timeoutTime;
 
-    // Set to the resolved action and flags when the event is enqueued.
+    // Set to the resolved ID, action and flags when the event is enqueued.
+    int32_t resolvedEventId;
     int32_t resolvedAction;
     int32_t resolvedFlags;
 
@@ -177,6 +223,9 @@
     static uint32_t nextSeq();
 };
 
+VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry);
+VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry);
+
 class InputDispatcher;
 // A command entry captures state and behavior for an action to be performed in the
 // dispatch loop after the initial processing has taken place.  It is essentially
@@ -195,10 +244,10 @@
 //
 // Commands are implicitly 'LockedInterruptible'.
 struct CommandEntry;
-typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry);
+typedef std::function<void(InputDispatcher&, CommandEntry*)> Command;
 
 class Connection;
-struct CommandEntry : Link<CommandEntry> {
+struct CommandEntry {
     explicit CommandEntry(Command command);
     ~CommandEntry();
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 4410008..fe016af 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -17,7 +17,7 @@
 #define LOG_TAG "InputDispatcher"
 #define ATRACE_TAG ATRACE_TAG_INPUT
 
-#define LOG_NDEBUG 0
+#define LOG_NDEBUG 1
 
 // Log detailed debug messages about each inbound event notification to the dispatcher.
 #define DEBUG_INBOUND_EVENT_DETAILS 0
@@ -35,7 +35,7 @@
 #define DEBUG_INJECTION 0
 
 // Log debug messages about input focus tracking.
-#define DEBUG_FOCUS 0
+static constexpr bool DEBUG_FOCUS = false;
 
 // Log debug messages about the app switch latency optimization.
 #define DEBUG_APP_SWITCH 0
@@ -50,15 +50,20 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <statslog.h>
 #include <stddef.h>
 #include <time.h>
 #include <unistd.h>
+#include <queue>
 #include <sstream>
 
 #include <android-base/chrono_utils.h>
 #include <android-base/stringprintf.h>
 #include <binder/Binder.h>
+#include <input/InputDevice.h>
 #include <log/log.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
 #include <powermanager/PowerManager.h>
 #include <utils/Trace.h>
 
@@ -73,7 +78,7 @@
 
 // Default input dispatching timeout if there is no focused application or paused window
 // from which to determine an appropriate dispatching timeout.
-constexpr nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec
+constexpr std::chrono::nanoseconds DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s;
 
 // Amount of time to allow for all pending events to be processed when an app switch
 // key is on the way.  This is used to preempt input dispatch and drop input events
@@ -84,18 +89,17 @@
 // before considering it stale and dropping it.
 constexpr nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec
 
-// Amount of time to allow touch events to be streamed out to a connection before requiring
-// that the first event be finished.  This value extends the ANR timeout by the specified
-// amount.  For example, if streaming is allowed to get ahead by one second relative to the
-// queue of waiting unfinished events, then ANRs will similarly be delayed by one second.
-constexpr nsecs_t STREAM_AHEAD_EVENT_TIMEOUT = 500 * 1000000LL; // 0.5sec
-
 // Log a warning when an event takes longer than this to process, even if an ANR does not occur.
 constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec
 
 // Log a warning when an interception call takes longer than this to process.
 constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms;
 
+// Additional key latency in case a connection is still processing some motion events.
+// This will help with the case when a user touched a button that opens a new window,
+// and gives us the chance to dispatch the key to this new window.
+constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = 500ms;
+
 // Number of recent events to keep for debugging purposes.
 constexpr size_t RECENT_QUEUE_MAX_SIZE = 10;
 
@@ -203,10 +207,183 @@
     }
 }
 
-template <typename T, typename U>
-static T getValueByKey(std::unordered_map<U, T>& map, U key) {
-    typename std::unordered_map<U, T>::const_iterator it = map.find(key);
-    return it != map.end() ? it->second : T{};
+/**
+ * Find the entry in std::unordered_map by key, and return it.
+ * If the entry is not found, return a default constructed entry.
+ *
+ * Useful when the entries are vectors, since an empty vector will be returned
+ * if the entry is not found.
+ * Also useful when the entries are sp<>. If an entry is not found, nullptr is returned.
+ */
+template <typename K, typename V>
+static V getValueByKey(const std::unordered_map<K, V>& map, K key) {
+    auto it = map.find(key);
+    return it != map.end() ? it->second : V{};
+}
+
+/**
+ * Find the entry in std::unordered_map by value, and remove it.
+ * If more than one entry has the same value, then all matching
+ * key-value pairs will be removed.
+ *
+ * Return true if at least one value has been removed.
+ */
+template <typename K, typename V>
+static bool removeByValue(std::unordered_map<K, V>& map, const V& value) {
+    bool removed = false;
+    for (auto it = map.begin(); it != map.end();) {
+        if (it->second == value) {
+            it = map.erase(it);
+            removed = true;
+        } else {
+            it++;
+        }
+    }
+    return removed;
+}
+
+static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
+    if (first == second) {
+        return true;
+    }
+
+    if (first == nullptr || second == nullptr) {
+        return false;
+    }
+
+    return first->getToken() == second->getToken();
+}
+
+static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
+    return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
+}
+
+static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
+                                                          EventEntry* eventEntry,
+                                                          int32_t inputTargetFlags) {
+    if (inputTarget.useDefaultPointerInfo()) {
+        const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo();
+        return std::make_unique<DispatchEntry>(eventEntry, // increments ref
+                                               inputTargetFlags, pointerInfo.xOffset,
+                                               pointerInfo.yOffset, inputTarget.globalScaleFactor,
+                                               pointerInfo.windowXScale, pointerInfo.windowYScale);
+    }
+
+    ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
+    const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
+
+    PointerCoords pointerCoords[motionEntry.pointerCount];
+
+    // Use the first pointer information to normalize all other pointers. This could be any pointer
+    // as long as all other pointers are normalized to the same value and the final DispatchEntry
+    // uses the offset and scale for the normalized pointer.
+    const PointerInfo& firstPointerInfo =
+            inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()];
+
+    // Iterate through all pointers in the event to normalize against the first.
+    for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) {
+        const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
+        uint32_t pointerId = uint32_t(pointerProperties.id);
+        const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId];
+
+        // The scale factor is the ratio of the current pointers scale to the normalized scale.
+        float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale;
+        float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale;
+
+        pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]);
+        // First apply the current pointers offset to set the window at 0,0
+        pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset);
+        // Next scale the coordinates.
+        pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff);
+        // Lastly, offset the coordinates so they're in the normalized pointer's frame.
+        pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset,
+                                                -firstPointerInfo.yOffset);
+    }
+
+    MotionEntry* combinedMotionEntry =
+            new MotionEntry(motionEntry.id, motionEntry.eventTime, motionEntry.deviceId,
+                            motionEntry.source, motionEntry.displayId, motionEntry.policyFlags,
+                            motionEntry.action, motionEntry.actionButton, motionEntry.flags,
+                            motionEntry.metaState, motionEntry.buttonState,
+                            motionEntry.classification, motionEntry.edgeFlags,
+                            motionEntry.xPrecision, motionEntry.yPrecision,
+                            motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+                            motionEntry.downTime, motionEntry.pointerCount,
+                            motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */,
+                            0 /* yOffset */);
+
+    if (motionEntry.injectionState) {
+        combinedMotionEntry->injectionState = motionEntry.injectionState;
+        combinedMotionEntry->injectionState->refCount += 1;
+    }
+
+    std::unique_ptr<DispatchEntry> dispatchEntry =
+            std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref
+                                            inputTargetFlags, firstPointerInfo.xOffset,
+                                            firstPointerInfo.yOffset, inputTarget.globalScaleFactor,
+                                            firstPointerInfo.windowXScale,
+                                            firstPointerInfo.windowYScale);
+    combinedMotionEntry->release();
+    return dispatchEntry;
+}
+
+static void addGestureMonitors(const std::vector<Monitor>& monitors,
+                               std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
+                               float yOffset = 0) {
+    if (monitors.empty()) {
+        return;
+    }
+    outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
+    for (const Monitor& monitor : monitors) {
+        outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
+    }
+}
+
+static std::array<uint8_t, 128> getRandomKey() {
+    std::array<uint8_t, 128> key;
+    if (RAND_bytes(key.data(), key.size()) != 1) {
+        LOG_ALWAYS_FATAL("Can't generate HMAC key");
+    }
+    return key;
+}
+
+// --- HmacKeyManager ---
+
+HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {}
+
+std::array<uint8_t, 32> HmacKeyManager::sign(const VerifiedInputEvent& event) const {
+    size_t size;
+    switch (event.type) {
+        case VerifiedInputEvent::Type::KEY: {
+            size = sizeof(VerifiedKeyEvent);
+            break;
+        }
+        case VerifiedInputEvent::Type::MOTION: {
+            size = sizeof(VerifiedMotionEvent);
+            break;
+        }
+    }
+    const uint8_t* start = reinterpret_cast<const uint8_t*>(&event);
+    return sign(start, size);
+}
+
+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, size, hash.data(), &hashLen);
+    if (result == nullptr) {
+        ALOGE("Could not sign the data using HMAC");
+        return INVALID_HMAC;
+    }
+
+    if (hashLen != hash.size()) {
+        ALOGE("HMAC-SHA256 has unexpected length");
+        return INVALID_HMAC;
+    }
+
+    return hash;
 }
 
 // --- InputDispatcher ---
@@ -214,15 +391,19 @@
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
       : mPolicy(policy),
         mPendingEvent(nullptr),
-        mLastDropReason(DROP_REASON_NOT_DROPPED),
+        mLastDropReason(DropReason::NOT_DROPPED),
+        mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
         mAppSwitchSawKeyDown(false),
         mAppSwitchDueTime(LONG_LONG_MAX),
         mNextUnblockedEvent(nullptr),
         mDispatchEnabled(false),
         mDispatchFrozen(false),
         mInputFilterEnabled(false),
-        mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
-        mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
+        // mInTouchMode will be initialized by the WindowManager to the default device config.
+        // To avoid leaking stack in case that call never comes, and for tests,
+        // initialize it here anyways.
+        mInTouchMode(true),
+        mFocusedDisplayId(ADISPLAY_ID_DEFAULT) {
     mLooper = new Looper(false);
     mReporter = createInputReporter();
 
@@ -240,11 +421,30 @@
         drainInboundQueueLocked();
     }
 
-    while (mConnectionsByFd.size() != 0) {
-        unregisterInputChannel(mConnectionsByFd.valueAt(0)->inputChannel);
+    while (!mConnectionsByFd.empty()) {
+        sp<Connection> connection = mConnectionsByFd.begin()->second;
+        unregisterInputChannel(connection->inputChannel);
     }
 }
 
+status_t InputDispatcher::start() {
+    if (mThread) {
+        return ALREADY_EXISTS;
+    }
+    mThread = std::make_unique<InputThread>(
+            "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
+    return OK;
+}
+
+status_t InputDispatcher::stop() {
+    if (mThread && mThread->isCallingThread()) {
+        ALOGE("InputDispatcher cannot be stopped from its own thread!");
+        return INVALID_OPERATION;
+    }
+    mThread.reset();
+    return OK;
+}
+
 void InputDispatcher::dispatchOnce() {
     nsecs_t nextWakeupTime = LONG_LONG_MAX;
     { // acquire lock
@@ -262,6 +462,17 @@
         if (runCommandsLockedInterruptible()) {
             nextWakeupTime = LONG_LONG_MIN;
         }
+
+        // If we are still waiting for ack on some events,
+        // we might have to wake up earlier to check if an app is anr'ing.
+        const nsecs_t nextAnrCheck = processAnrsLocked();
+        nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
+
+        // We are about to enter an infinitely long sleep, because we have no commands or
+        // pending or queued events
+        if (nextWakeupTime == LONG_LONG_MAX) {
+            mDispatcherEnteredIdle.notify_all();
+        }
     } // release lock
 
     // Wait for callback or timeout or wake.  (make sure we round up, not down)
@@ -270,6 +481,55 @@
     mLooper->pollOnce(timeoutMillis);
 }
 
+/**
+ * Check if any of the connections' wait queues have events that are too old.
+ * If we waited for events to be ack'ed for more than the window timeout, raise an ANR.
+ * Return the time at which we should wake up next.
+ */
+nsecs_t InputDispatcher::processAnrsLocked() {
+    const nsecs_t currentTime = now();
+    nsecs_t nextAnrCheck = LONG_LONG_MAX;
+    // Check if we are waiting for a focused window to appear. Raise ANR if waited too long
+    if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) {
+        if (currentTime >= *mNoFocusedWindowTimeoutTime) {
+            onAnrLocked(mAwaitedFocusedApplication);
+            mAwaitedFocusedApplication.clear();
+            return LONG_LONG_MIN;
+        } else {
+            // Keep waiting
+            const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime);
+            ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining);
+            nextAnrCheck = *mNoFocusedWindowTimeoutTime;
+        }
+    }
+
+    // Check if any connection ANRs are due
+    nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
+    if (currentTime < nextAnrCheck) { // most likely scenario
+        return nextAnrCheck;          // everything is normal. Let's check again at nextAnrCheck
+    }
+
+    // If we reached here, we have an unresponsive connection.
+    sp<Connection> connection = getConnectionLocked(mAnrTracker.firstToken());
+    if (connection == nullptr) {
+        ALOGE("Could not find connection for entry %" PRId64, mAnrTracker.firstTimeout());
+        return nextAnrCheck;
+    }
+    connection->responsive = false;
+    // Stop waking up for this unresponsive connection
+    mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
+    onAnrLocked(connection);
+    return LONG_LONG_MIN;
+}
+
+nsecs_t InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) {
+    sp<InputWindowHandle> window = getWindowHandleLocked(token);
+    if (window != nullptr) {
+        return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT).count();
+    }
+    return DEFAULT_INPUT_DISPATCHING_TIMEOUT.count();
+}
+
 void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
     nsecs_t currentTime = now();
 
@@ -282,9 +542,9 @@
 
     // If dispatching is frozen, do not process timeouts or try to deliver any new events.
     if (mDispatchFrozen) {
-#if DEBUG_FOCUS
-        ALOGD("Dispatch frozen.  Waiting some more.");
-#endif
+        if (DEBUG_FOCUS) {
+            ALOGD("Dispatch frozen.  Waiting some more.");
+        }
         return;
     }
 
@@ -299,7 +559,7 @@
     // Ready to start a new event.
     // If we don't already have a pending event, go grab one.
     if (!mPendingEvent) {
-        if (mInboundQueue.isEmpty()) {
+        if (mInboundQueue.empty()) {
             if (isAppSwitchDue) {
                 // The inbound queue is empty so the app switch key we were waiting
                 // for will never arrive.  Stop waiting for it.
@@ -324,28 +584,26 @@
             }
         } else {
             // Inbound queue has at least one entry.
-            mPendingEvent = mInboundQueue.dequeueAtHead();
+            mPendingEvent = mInboundQueue.front();
+            mInboundQueue.pop_front();
             traceInboundQueueLengthLocked();
         }
 
         // Poke user activity for this event.
         if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
-            pokeUserActivityLocked(mPendingEvent);
+            pokeUserActivityLocked(*mPendingEvent);
         }
-
-        // Get ready to dispatch the event.
-        resetANRTimeoutsLocked();
     }
 
     // Now we have an event to dispatch.
     // All events are eventually dequeued and processed this way, even if we intend to drop them.
     ALOG_ASSERT(mPendingEvent != nullptr);
     bool done = false;
-    DropReason dropReason = DROP_REASON_NOT_DROPPED;
+    DropReason dropReason = DropReason::NOT_DROPPED;
     if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
-        dropReason = DROP_REASON_POLICY;
+        dropReason = DropReason::POLICY;
     } else if (!mDispatchEnabled) {
-        dropReason = DROP_REASON_DISABLED;
+        dropReason = DropReason::DISABLED;
     }
 
     if (mNextUnblockedEvent == mPendingEvent) {
@@ -353,64 +611,68 @@
     }
 
     switch (mPendingEvent->type) {
-        case EventEntry::TYPE_CONFIGURATION_CHANGED: {
+        case EventEntry::Type::CONFIGURATION_CHANGED: {
             ConfigurationChangedEntry* typedEntry =
                     static_cast<ConfigurationChangedEntry*>(mPendingEvent);
             done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
-            dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped
+            dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped
             break;
         }
 
-        case EventEntry::TYPE_DEVICE_RESET: {
+        case EventEntry::Type::DEVICE_RESET: {
             DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent);
             done = dispatchDeviceResetLocked(currentTime, typedEntry);
-            dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped
+            dropReason = DropReason::NOT_DROPPED; // device resets are never dropped
             break;
         }
 
-        case EventEntry::TYPE_KEY: {
+        case EventEntry::Type::FOCUS: {
+            FocusEntry* typedEntry = static_cast<FocusEntry*>(mPendingEvent);
+            dispatchFocusLocked(currentTime, typedEntry);
+            done = true;
+            dropReason = DropReason::NOT_DROPPED; // focus events are never dropped
+            break;
+        }
+
+        case EventEntry::Type::KEY: {
             KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
             if (isAppSwitchDue) {
-                if (isAppSwitchKeyEvent(typedEntry)) {
+                if (isAppSwitchKeyEvent(*typedEntry)) {
                     resetPendingAppSwitchLocked(true);
                     isAppSwitchDue = false;
-                } else if (dropReason == DROP_REASON_NOT_DROPPED) {
-                    dropReason = DROP_REASON_APP_SWITCH;
+                } else if (dropReason == DropReason::NOT_DROPPED) {
+                    dropReason = DropReason::APP_SWITCH;
                 }
             }
-            if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) {
-                dropReason = DROP_REASON_STALE;
+            if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
+                dropReason = DropReason::STALE;
             }
-            if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
-                dropReason = DROP_REASON_BLOCKED;
+            if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
+                dropReason = DropReason::BLOCKED;
             }
             done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
             break;
         }
 
-        case EventEntry::TYPE_MOTION: {
+        case EventEntry::Type::MOTION: {
             MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
-            if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
-                dropReason = DROP_REASON_APP_SWITCH;
+            if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
+                dropReason = DropReason::APP_SWITCH;
             }
-            if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) {
-                dropReason = DROP_REASON_STALE;
+            if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
+                dropReason = DropReason::STALE;
             }
-            if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
-                dropReason = DROP_REASON_BLOCKED;
+            if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
+                dropReason = DropReason::BLOCKED;
             }
             done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
             break;
         }
-
-        default:
-            ALOG_ASSERT(false);
-            break;
     }
 
     if (done) {
-        if (dropReason != DROP_REASON_NOT_DROPPED) {
-            dropInboundEventLocked(mPendingEvent, dropReason);
+        if (dropReason != DropReason::NOT_DROPPED) {
+            dropInboundEventLocked(*mPendingEvent, dropReason);
         }
         mLastDropReason = dropReason;
 
@@ -419,26 +681,86 @@
     }
 }
 
+/**
+ * Return true if the events preceding this incoming motion event should be dropped
+ * Return false otherwise (the default behaviour)
+ */
+bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) {
+    const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN &&
+            (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER);
+
+    // Optimize case where the current application is unresponsive and the user
+    // decides to touch a window in a different application.
+    // If the application takes too long to catch up then we drop all events preceding
+    // the touch into the other window.
+    if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) {
+        int32_t displayId = motionEntry.displayId;
+        int32_t x = static_cast<int32_t>(
+                motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+        int32_t y = static_cast<int32_t>(
+                motionEntry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+        sp<InputWindowHandle> touchedWindowHandle =
+                findTouchedWindowAtLocked(displayId, x, y, nullptr);
+        if (touchedWindowHandle != nullptr &&
+            touchedWindowHandle->getApplicationToken() !=
+                    mAwaitedFocusedApplication->getApplicationToken()) {
+            // User touched a different application than the one we are waiting on.
+            ALOGI("Pruning input queue because user touched a different application while waiting "
+                  "for %s",
+                  mAwaitedFocusedApplication->getName().c_str());
+            return true;
+        }
+
+        // Alternatively, maybe there's a gesture monitor that could handle this event
+        std::vector<TouchedMonitor> gestureMonitors =
+                findTouchedGestureMonitorsLocked(displayId, {});
+        for (TouchedMonitor& gestureMonitor : gestureMonitors) {
+            sp<Connection> connection =
+                    getConnectionLocked(gestureMonitor.monitor.inputChannel->getConnectionToken());
+            if (connection != nullptr && connection->responsive) {
+                // This monitor could take more input. Drop all events preceding this
+                // event, so that gesture monitor could get a chance to receive the stream
+                ALOGW("Pruning the input queue because %s is unresponsive, but we have a "
+                      "responsive gesture monitor that may handle the event",
+                      mAwaitedFocusedApplication->getName().c_str());
+                return true;
+            }
+        }
+    }
+
+    // Prevent getting stuck: if we have a pending key event, and some motion events that have not
+    // yet been processed by some connections, the dispatcher will wait for these motion
+    // events to be processed before dispatching the key event. This is because these motion events
+    // may cause a new window to be launched, which the user might expect to receive focus.
+    // To prevent waiting forever for such events, just send the key to the currently focused window
+    if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) {
+        ALOGD("Received a new pointer down event, stop waiting for events to process and "
+              "just send the pending key event to the focused window.");
+        mKeyIsWaitingForEventsTimeout = now();
+    }
+    return false;
+}
+
 bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
-    bool needWake = mInboundQueue.isEmpty();
-    mInboundQueue.enqueueAtTail(entry);
+    bool needWake = mInboundQueue.empty();
+    mInboundQueue.push_back(entry);
     traceInboundQueueLengthLocked();
 
     switch (entry->type) {
-        case EventEntry::TYPE_KEY: {
+        case EventEntry::Type::KEY: {
             // Optimize app switch latency.
             // If the application takes too long to catch up then we drop all events preceding
             // the app switch key.
-            KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
+            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*entry);
             if (isAppSwitchKeyEvent(keyEntry)) {
-                if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
+                if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
                     mAppSwitchSawKeyDown = true;
-                } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
+                } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
                     if (mAppSwitchSawKeyDown) {
 #if DEBUG_APP_SWITCH
                         ALOGD("App switch is pending!");
 #endif
-                        mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
+                        mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
                         mAppSwitchSawKeyDown = false;
                         needWake = true;
                     }
@@ -447,34 +769,22 @@
             break;
         }
 
-        case EventEntry::TYPE_MOTION: {
-            // Optimize case where the current application is unresponsive and the user
-            // decides to touch a window in a different application.
-            // If the application takes too long to catch up then we drop all events preceding
-            // the touch into the other window.
-            MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
-            if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN &&
-                (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
-                mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY &&
-                mInputTargetWaitApplicationToken != nullptr) {
-                int32_t displayId = motionEntry->displayId;
-                int32_t x =
-                        int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
-                int32_t y =
-                        int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
-                sp<InputWindowHandle> touchedWindowHandle =
-                        findTouchedWindowAtLocked(displayId, x, y);
-                if (touchedWindowHandle != nullptr &&
-                    touchedWindowHandle->getApplicationToken() !=
-                            mInputTargetWaitApplicationToken) {
-                    // User touched a different application than the one we are waiting on.
-                    // Flag the event, and start pruning the input queue.
-                    mNextUnblockedEvent = motionEntry;
-                    needWake = true;
-                }
+        case EventEntry::Type::MOTION: {
+            if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(*entry))) {
+                mNextUnblockedEvent = entry;
+                needWake = true;
             }
             break;
         }
+        case EventEntry::Type::FOCUS: {
+            LOG_ALWAYS_FATAL("Focus events should be inserted using enqueueFocusEventLocked");
+            break;
+        }
+        case EventEntry::Type::CONFIGURATION_CHANGED:
+        case EventEntry::Type::DEVICE_RESET: {
+            // nothing to do
+            break;
+        }
     }
 
     return needWake;
@@ -482,15 +792,21 @@
 
 void InputDispatcher::addRecentEventLocked(EventEntry* entry) {
     entry->refCount += 1;
-    mRecentQueue.enqueueAtTail(entry);
-    if (mRecentQueue.count() > RECENT_QUEUE_MAX_SIZE) {
-        mRecentQueue.dequeueAtHead()->release();
+    mRecentQueue.push_back(entry);
+    if (mRecentQueue.size() > RECENT_QUEUE_MAX_SIZE) {
+        mRecentQueue.front()->release();
+        mRecentQueue.pop_front();
     }
 }
 
 sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
-                                                                 int32_t y, bool addOutsideTargets,
+                                                                 int32_t y, TouchState* touchState,
+                                                                 bool addOutsideTargets,
                                                                  bool addPortalWindows) {
+    if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
+        LOG_ALWAYS_FATAL(
+                "Must provide a valid touch state if adding portal windows or outside targets");
+    }
     // Traverse windows from front to back to find touched window.
     const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
@@ -509,9 +825,9 @@
                             portalToDisplayId != displayId) {
                             if (addPortalWindows) {
                                 // For the monitoring channels of the display.
-                                mTempTouchState.addPortalWindow(windowHandle);
+                                touchState->addPortalWindow(windowHandle);
                             }
-                            return findTouchedWindowAtLocked(portalToDisplayId, x, y,
+                            return findTouchedWindowAtLocked(portalToDisplayId, x, y, touchState,
                                                              addOutsideTargets, addPortalWindows);
                         }
                         // Found window.
@@ -520,9 +836,9 @@
                 }
 
                 if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
-                    mTempTouchState.addOrUpdateWindow(windowHandle,
-                                                      InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
-                                                      BitSet32(0));
+                    touchState->addOrUpdateWindow(windowHandle,
+                                                  InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
+                                                  BitSet32(0));
                 }
             }
         }
@@ -531,7 +847,7 @@
 }
 
 std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked(
-        int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) {
+        int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const {
     std::vector<TouchedMonitor> touchedMonitors;
 
     std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId);
@@ -545,61 +861,50 @@
     return touchedMonitors;
 }
 
-void InputDispatcher::addGestureMonitors(const std::vector<Monitor>& monitors,
-                                         std::vector<TouchedMonitor>& outTouchedMonitors,
-                                         float xOffset, float yOffset) {
-    if (monitors.empty()) {
-        return;
-    }
-    outTouchedMonitors.reserve(monitors.size() + outTouchedMonitors.size());
-    for (const Monitor& monitor : monitors) {
-        outTouchedMonitors.emplace_back(monitor, xOffset, yOffset);
-    }
-}
-
-void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
+void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) {
     const char* reason;
     switch (dropReason) {
-        case DROP_REASON_POLICY:
+        case DropReason::POLICY:
 #if DEBUG_INBOUND_EVENT_DETAILS
             ALOGD("Dropped event because policy consumed it.");
 #endif
             reason = "inbound event was dropped because the policy consumed it";
             break;
-        case DROP_REASON_DISABLED:
-            if (mLastDropReason != DROP_REASON_DISABLED) {
+        case DropReason::DISABLED:
+            if (mLastDropReason != DropReason::DISABLED) {
                 ALOGI("Dropped event because input dispatch is disabled.");
             }
             reason = "inbound event was dropped because input dispatch is disabled";
             break;
-        case DROP_REASON_APP_SWITCH:
+        case DropReason::APP_SWITCH:
             ALOGI("Dropped event because of pending overdue app switch.");
             reason = "inbound event was dropped because of pending overdue app switch";
             break;
-        case DROP_REASON_BLOCKED:
+        case DropReason::BLOCKED:
             ALOGI("Dropped event because the current application is not responding and the user "
                   "has started interacting with a different application.");
             reason = "inbound event was dropped because the current application is not responding "
                      "and the user has started interacting with a different application";
             break;
-        case DROP_REASON_STALE:
+        case DropReason::STALE:
             ALOGI("Dropped event because it is stale.");
             reason = "inbound event was dropped because it is stale";
             break;
-        default:
-            ALOG_ASSERT(false);
+        case DropReason::NOT_DROPPED: {
+            LOG_ALWAYS_FATAL("Should not be dropping a NOT_DROPPED event");
             return;
+        }
     }
 
-    switch (entry->type) {
-        case EventEntry::TYPE_KEY: {
+    switch (entry.type) {
+        case EventEntry::Type::KEY: {
             CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
             synthesizeCancelationEventsForAllConnectionsLocked(options);
             break;
         }
-        case EventEntry::TYPE_MOTION: {
-            MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
-            if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
+        case EventEntry::Type::MOTION: {
+            const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
+            if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) {
                 CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
                 synthesizeCancelationEventsForAllConnectionsLocked(options);
             } else {
@@ -608,6 +913,12 @@
             }
             break;
         }
+        case EventEntry::Type::FOCUS:
+        case EventEntry::Type::CONFIGURATION_CHANGED:
+        case EventEntry::Type::DEVICE_RESET: {
+            LOG_ALWAYS_FATAL("Should not drop %s events", EventEntry::typeToString(entry.type));
+            break;
+        }
     }
 }
 
@@ -616,10 +927,10 @@
             keyCode == AKEYCODE_APP_SWITCH;
 }
 
-bool InputDispatcher::isAppSwitchKeyEvent(KeyEntry* keyEntry) {
-    return !(keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry->keyCode) &&
-            (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) &&
-            (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER);
+bool InputDispatcher::isAppSwitchKeyEvent(const KeyEntry& keyEntry) {
+    return !(keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry.keyCode) &&
+            (keyEntry.policyFlags & POLICY_FLAG_TRUSTED) &&
+            (keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER);
 }
 
 bool InputDispatcher::isAppSwitchPendingLocked() {
@@ -638,40 +949,34 @@
 #endif
 }
 
-bool InputDispatcher::isStaleEvent(nsecs_t currentTime, EventEntry* entry) {
-    return currentTime - entry->eventTime >= STALE_EVENT_TIMEOUT;
-}
-
 bool InputDispatcher::haveCommandsLocked() const {
-    return !mCommandQueue.isEmpty();
+    return !mCommandQueue.empty();
 }
 
 bool InputDispatcher::runCommandsLockedInterruptible() {
-    if (mCommandQueue.isEmpty()) {
+    if (mCommandQueue.empty()) {
         return false;
     }
 
     do {
-        CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
-
+        std::unique_ptr<CommandEntry> commandEntry = std::move(mCommandQueue.front());
+        mCommandQueue.pop_front();
         Command command = commandEntry->command;
-        (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'
+        command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible'
 
         commandEntry->connection.clear();
-        delete commandEntry;
-    } while (!mCommandQueue.isEmpty());
+    } while (!mCommandQueue.empty());
     return true;
 }
 
-CommandEntry* InputDispatcher::postCommandLocked(Command command) {
-    CommandEntry* commandEntry = new CommandEntry(command);
-    mCommandQueue.enqueueAtTail(commandEntry);
-    return commandEntry;
+void InputDispatcher::postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) {
+    mCommandQueue.push_back(std::move(commandEntry));
 }
 
 void InputDispatcher::drainInboundQueueLocked() {
-    while (!mInboundQueue.isEmpty()) {
-        EventEntry* entry = mInboundQueue.dequeueAtHead();
+    while (!mInboundQueue.empty()) {
+        EventEntry* entry = mInboundQueue.front();
+        mInboundQueue.pop_front();
         releaseInboundEventLocked(entry);
     }
     traceInboundQueueLengthLocked();
@@ -679,7 +984,6 @@
 
 void InputDispatcher::releasePendingEventLocked() {
     if (mPendingEvent) {
-        resetANRTimeoutsLocked();
         releaseInboundEventLocked(mPendingEvent);
         mPendingEvent = nullptr;
     }
@@ -715,14 +1019,15 @@
             (POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED);
     if (entry->refCount == 1) {
         entry->recycle();
+        entry->id = mIdGenerator.nextId();
         entry->eventTime = currentTime;
         entry->policyFlags = policyFlags;
         entry->repeatCount += 1;
     } else {
         KeyEntry* newEntry =
-                new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, entry->deviceId,
-                             entry->source, entry->displayId, policyFlags, entry->action,
-                             entry->flags, entry->keyCode, entry->scanCode, entry->metaState,
+                new KeyEntry(mIdGenerator.nextId(), currentTime, entry->deviceId, entry->source,
+                             entry->displayId, policyFlags, entry->action, entry->flags,
+                             entry->keyCode, entry->scanCode, entry->metaState,
                              entry->repeatCount + 1, entry->downTime);
 
         mKeyRepeatState.lastKeyEntry = newEntry;
@@ -750,9 +1055,10 @@
     resetKeyRepeatLocked();
 
     // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
-    CommandEntry* commandEntry =
-            postCommandLocked(&InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
     commandEntry->eventTime = entry->eventTime;
+    postCommandLocked(std::move(commandEntry));
     return true;
 }
 
@@ -768,6 +1074,40 @@
     return true;
 }
 
+void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) {
+    if (mPendingEvent != nullptr) {
+        // Move the pending event to the front of the queue. This will give the chance
+        // for the pending event to get dispatched to the newly focused window
+        mInboundQueue.push_front(mPendingEvent);
+        mPendingEvent = nullptr;
+    }
+
+    FocusEntry* focusEntry =
+            new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus);
+
+    // This event should go to the front of the queue, but behind all other focus events
+    // Find the last focus event, and insert right after it
+    std::deque<EventEntry*>::reverse_iterator it =
+            std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(),
+                         [](EventEntry* event) { return event->type == EventEntry::Type::FOCUS; });
+
+    // Maintain the order of focus events. Insert the entry after all other focus events.
+    mInboundQueue.insert(it.base(), focusEntry);
+}
+
+void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) {
+    sp<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+    if (channel == nullptr) {
+        return; // Window has gone away
+    }
+    InputTarget target;
+    target.inputChannel = channel;
+    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+    entry->dispatchInProgress = true;
+
+    dispatchEventLocked(currentTime, entry, {target});
+}
+
 bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
                                         DropReason* dropReason, nsecs_t* nextWakeupTime) {
     // Preprocessing.
@@ -803,7 +1143,7 @@
 
         entry->dispatchInProgress = true;
 
-        logOutboundKeyDetails("dispatchKey - ", entry);
+        logOutboundKeyDetails("dispatchKey - ", *entry);
     }
 
     // Handle case where the policy asked us to try again later last time.
@@ -821,38 +1161,39 @@
     // Give the policy a chance to intercept the key.
     if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
         if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
-            CommandEntry* commandEntry = postCommandLocked(
+            std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
                     &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
             sp<InputWindowHandle> focusedWindowHandle =
-                    getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry));
+                    getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(*entry));
             if (focusedWindowHandle != nullptr) {
                 commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken());
             }
             commandEntry->keyEntry = entry;
+            postCommandLocked(std::move(commandEntry));
             entry->refCount += 1;
             return false; // wait for the command to run
         } else {
             entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
         }
     } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
-        if (*dropReason == DROP_REASON_NOT_DROPPED) {
-            *dropReason = DROP_REASON_POLICY;
+        if (*dropReason == DropReason::NOT_DROPPED) {
+            *dropReason = DropReason::POLICY;
         }
     }
 
     // Clean up if dropping the event.
-    if (*dropReason != DROP_REASON_NOT_DROPPED) {
+    if (*dropReason != DropReason::NOT_DROPPED) {
         setInjectionResult(entry,
-                           *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
+                           *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
                                                              : INPUT_EVENT_INJECTION_FAILED);
-        mReporter->reportDroppedKey(entry->sequenceNum);
+        mReporter->reportDroppedKey(entry->id);
         return true;
     }
 
     // Identify targets.
     std::vector<InputTarget> inputTargets;
     int32_t injectionResult =
-            findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime);
+            findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
     if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
         return false;
     }
@@ -863,21 +1204,21 @@
     }
 
     // Add monitor channels from event's or focused display.
-    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
+    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
 
     // Dispatch the key.
     dispatchEventLocked(currentTime, entry, inputTargets);
     return true;
 }
 
-void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry* entry) {
+void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
           "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
           "metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
-          prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId,
-          entry->policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode,
-          entry->metaState, entry->repeatCount, entry->downTime);
+          prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags,
+          entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
+          entry.repeatCount, entry.downTime);
 #endif
 }
 
@@ -888,13 +1229,13 @@
     if (!entry->dispatchInProgress) {
         entry->dispatchInProgress = true;
 
-        logOutboundMotionDetails("dispatchMotion - ", entry);
+        logOutboundMotionDetails("dispatchMotion - ", *entry);
     }
 
     // Clean up if dropping the event.
-    if (*dropReason != DROP_REASON_NOT_DROPPED) {
+    if (*dropReason != DropReason::NOT_DROPPED) {
         setInjectionResult(entry,
-                           *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
+                           *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
                                                              : INPUT_EVENT_INJECTION_FAILED);
         return true;
     }
@@ -909,36 +1250,39 @@
     if (isPointerEvent) {
         // Pointer event.  (eg. touchscreen)
         injectionResult =
-                findTouchedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime,
+                findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
                                                &conflictingPointerActions);
     } else {
         // Non touch event.  (eg. trackball)
         injectionResult =
-                findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime);
+                findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
     }
     if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
         return false;
     }
 
     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;
     }
 
     // Add monitor channels from event's or focused display.
-    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
+    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
 
     if (isPointerEvent) {
-        ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId);
-        if (stateIndex >= 0) {
-            const TouchState& state = mTouchStatesByDisplay.valueAt(stateIndex);
+        std::unordered_map<int32_t, TouchState>::iterator it =
+                mTouchStatesByDisplay.find(entry->displayId);
+        if (it != mTouchStatesByDisplay.end()) {
+            const TouchState& state = it->second;
             if (!state.portalWindows.empty()) {
                 // The event has gone through these portal windows, so we add monitoring targets of
                 // the corresponding displays as well.
@@ -961,33 +1305,32 @@
     return true;
 }
 
-void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry* entry) {
+void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
           ", policyFlags=0x%x, "
           "action=0x%x, actionButton=0x%x, flags=0x%x, "
           "metaState=0x%x, buttonState=0x%x,"
           "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
-          prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId,
-          entry->policyFlags, entry->action, entry->actionButton, entry->flags, entry->metaState,
-          entry->buttonState, entry->edgeFlags, entry->xPrecision, entry->yPrecision,
-          entry->downTime);
+          prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, entry.policyFlags,
+          entry.action, entry.actionButton, entry.flags, entry.metaState, entry.buttonState,
+          entry.edgeFlags, entry.xPrecision, entry.yPrecision, entry.downTime);
 
-    for (uint32_t i = 0; i < entry->pointerCount; i++) {
+    for (uint32_t i = 0; i < entry.pointerCount; i++) {
         ALOGD("  Pointer %d: id=%d, toolType=%d, "
               "x=%f, y=%f, pressure=%f, size=%f, "
               "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
               "orientation=%f",
-              i, entry->pointerProperties[i].id, entry->pointerProperties[i].toolType,
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+              i, entry.pointerProperties[i].id, entry.pointerProperties[i].toolType,
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
     }
 #endif
 }
@@ -1001,142 +1344,46 @@
 
     ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
 
-    pokeUserActivityLocked(eventEntry);
+    pokeUserActivityLocked(*eventEntry);
 
     for (const InputTarget& inputTarget : inputTargets) {
-        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
-        if (connectionIndex >= 0) {
-            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
-            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
+        sp<Connection> connection =
+                getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
+        if (connection != nullptr) {
+            prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
         } else {
-#if DEBUG_FOCUS
-            ALOGD("Dropping event delivery to target with channel '%s' because it "
-                  "is no longer registered with the input dispatcher.",
-                  inputTarget.inputChannel->getName().c_str());
-#endif
-        }
-    }
-}
-
-int32_t InputDispatcher::handleTargetsNotReadyLocked(
-        nsecs_t currentTime, const EventEntry* entry,
-        const sp<InputApplicationHandle>& applicationHandle,
-        const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) {
-    if (applicationHandle == nullptr && windowHandle == nullptr) {
-        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) {
-#if DEBUG_FOCUS
-            ALOGD("Waiting for system to become ready for input.  Reason: %s", reason);
-#endif
-            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
-            mInputTargetWaitStartTime = currentTime;
-            mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
-            mInputTargetWaitTimeoutExpired = false;
-            mInputTargetWaitApplicationToken.clear();
-        }
-    } else {
-        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
-#if DEBUG_FOCUS
-            ALOGD("Waiting for application to become ready for input: %s.  Reason: %s",
-                  getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
-#endif
-            nsecs_t timeout;
-            if (windowHandle != nullptr) {
-                timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
-            } else if (applicationHandle != nullptr) {
-                timeout =
-                        applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
-            } else {
-                timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
-            }
-
-            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
-            mInputTargetWaitStartTime = currentTime;
-            mInputTargetWaitTimeoutTime = currentTime + timeout;
-            mInputTargetWaitTimeoutExpired = false;
-            mInputTargetWaitApplicationToken.clear();
-
-            if (windowHandle != nullptr) {
-                mInputTargetWaitApplicationToken = windowHandle->getApplicationToken();
-            }
-            if (mInputTargetWaitApplicationToken == nullptr && applicationHandle != nullptr) {
-                mInputTargetWaitApplicationToken = applicationHandle->getApplicationToken();
-            }
-        }
-    }
-
-    if (mInputTargetWaitTimeoutExpired) {
-        return INPUT_EVENT_INJECTION_TIMED_OUT;
-    }
-
-    if (currentTime >= mInputTargetWaitTimeoutTime) {
-        onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime,
-                    mInputTargetWaitStartTime, reason);
-
-        // Force poll loop to wake up immediately on next iteration once we get the
-        // ANR response back from the policy.
-        *nextWakeupTime = LONG_LONG_MIN;
-        return INPUT_EVENT_INJECTION_PENDING;
-    } else {
-        // Force poll loop to wake up when timeout is due.
-        if (mInputTargetWaitTimeoutTime < *nextWakeupTime) {
-            *nextWakeupTime = mInputTargetWaitTimeoutTime;
-        }
-        return INPUT_EVENT_INJECTION_PENDING;
-    }
-}
-
-void InputDispatcher::removeWindowByTokenLocked(const sp<IBinder>& token) {
-    for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
-        TouchState& state = mTouchStatesByDisplay.editValueAt(d);
-        state.removeWindowByToken(token);
-    }
-}
-
-void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(
-        nsecs_t newTimeout, const sp<InputChannel>& inputChannel) {
-    if (newTimeout > 0) {
-        // Extend the timeout.
-        mInputTargetWaitTimeoutTime = now() + newTimeout;
-    } else {
-        // Give up.
-        mInputTargetWaitTimeoutExpired = true;
-
-        // Input state will not be realistic.  Mark it out of sync.
-        if (inputChannel.get()) {
-            ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
-            if (connectionIndex >= 0) {
-                sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
-                sp<IBinder> token = connection->inputChannel->getToken();
-
-                if (token != nullptr) {
-                    removeWindowByTokenLocked(token);
-                }
-
-                if (connection->status == Connection::STATUS_NORMAL) {
-                    CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
-                                               "application not responding");
-                    synthesizeCancelationEventsForConnectionLocked(connection, options);
-                }
+            if (DEBUG_FOCUS) {
+                ALOGD("Dropping event delivery to target with channel '%s' because it "
+                      "is no longer registered with the input dispatcher.",
+                      inputTarget.inputChannel->getName().c_str());
             }
         }
     }
 }
 
-nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) {
-    if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
-        return currentTime - mInputTargetWaitStartTime;
+void InputDispatcher::cancelEventsForAnrLocked(const sp<Connection>& connection) {
+    // We will not be breaking any connections here, even if the policy wants us to abort dispatch.
+    // If the policy decides to close the app, we will get a channel removal event via
+    // unregisterInputChannel, and will clean up the connection that way. We are already not
+    // sending new pointers to the connection when it blocked, but focused events will continue to
+    // pile up.
+    ALOGW("Canceling events for %s because it is unresponsive",
+          connection->inputChannel->getName().c_str());
+    if (connection->status == Connection::STATUS_NORMAL) {
+        CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
+                                   "application not responding");
+        synthesizeCancelationEventsForConnectionLocked(connection, options);
     }
-    return 0;
 }
 
-void InputDispatcher::resetANRTimeoutsLocked() {
-#if DEBUG_FOCUS
-    ALOGD("Resetting ANR timeouts.");
-#endif
+void InputDispatcher::resetNoFocusedWindowTimeoutLocked() {
+    if (DEBUG_FOCUS) {
+        ALOGD("Resetting ANR timeouts.");
+    }
 
     // Reset input target wait timeout.
-    mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
-    mInputTargetWaitApplicationToken.clear();
+    mNoFocusedWindowTimeoutTime = std::nullopt;
+    mAwaitedFocusedApplication.clear();
 }
 
 /**
@@ -1144,32 +1391,63 @@
  * then it should be dispatched to that display. Otherwise, the event goes to the focused display.
  * Focused display is the display that the user most recently interacted with.
  */
-int32_t InputDispatcher::getTargetDisplayId(const EventEntry* entry) {
+int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) {
     int32_t displayId;
-    switch (entry->type) {
-        case EventEntry::TYPE_KEY: {
-            const KeyEntry* typedEntry = static_cast<const KeyEntry*>(entry);
-            displayId = typedEntry->displayId;
+    switch (entry.type) {
+        case EventEntry::Type::KEY: {
+            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
+            displayId = keyEntry.displayId;
             break;
         }
-        case EventEntry::TYPE_MOTION: {
-            const MotionEntry* typedEntry = static_cast<const MotionEntry*>(entry);
-            displayId = typedEntry->displayId;
+        case EventEntry::Type::MOTION: {
+            const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
+            displayId = motionEntry.displayId;
             break;
         }
-        default: {
-            ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type);
+        case EventEntry::Type::FOCUS:
+        case EventEntry::Type::CONFIGURATION_CHANGED:
+        case EventEntry::Type::DEVICE_RESET: {
+            ALOGE("%s events do not have a target display", EventEntry::typeToString(entry.type));
             return ADISPLAY_ID_NONE;
         }
     }
     return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId;
 }
 
+bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime,
+                                                const char* focusedWindowName) {
+    if (mAnrTracker.empty()) {
+        // already processed all events that we waited for
+        mKeyIsWaitingForEventsTimeout = std::nullopt;
+        return false;
+    }
+
+    if (!mKeyIsWaitingForEventsTimeout.has_value()) {
+        // Start the timer
+        ALOGD("Waiting to send key to %s because there are unprocessed events that may cause "
+              "focus to change",
+              focusedWindowName);
+        mKeyIsWaitingForEventsTimeout = currentTime + KEY_WAITING_FOR_EVENTS_TIMEOUT.count();
+        return true;
+    }
+
+    // We still have pending events, and already started the timer
+    if (currentTime < *mKeyIsWaitingForEventsTimeout) {
+        return true; // Still waiting
+    }
+
+    // Waited too long, and some connection still hasn't processed all motions
+    // Just send the key to the focused window
+    ALOGW("Dispatching key to %s even though there are other unprocessed events",
+          focusedWindowName);
+    mKeyIsWaitingForEventsTimeout = std::nullopt;
+    return false;
+}
+
 int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
-                                                        const EventEntry* entry,
+                                                        const EventEntry& entry,
                                                         std::vector<InputTarget>& inputTargets,
                                                         nsecs_t* nextWakeupTime) {
-    int32_t injectionResult;
     std::string reason;
 
     int32_t displayId = getTargetDisplayId(entry);
@@ -1180,62 +1458,109 @@
 
     // If there is no currently focused window and no focused application
     // 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;
-        }
-
-        ALOGI("Dropping event because there is no focused window or focused application in display "
-              "%" PRId32 ".",
-              displayId);
-        injectionResult = INPUT_EVENT_INJECTION_FAILED;
-        goto Failed;
+    if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
+        ALOGI("Dropping %s event because there is no focused window or focused application in "
+              "display %" PRId32 ".",
+              EventEntry::typeToString(entry.type), displayId);
+        return INPUT_EVENT_INJECTION_FAILED;
     }
 
+    // Compatibility behavior: raise ANR if there is a focused application, but no focused window.
+    // Only start counting when we have a focused event to dispatch. The ANR is canceled if we
+    // start interacting with another application via touch (app switch). This code can be removed
+    // if the "no focused window ANR" is moved to the policy. Input doesn't know whether
+    // an app is expected to have a focused window.
+    if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) {
+        if (!mNoFocusedWindowTimeoutTime.has_value()) {
+            // We just discovered that there's no focused window. Start the ANR timer
+            const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout(
+                    DEFAULT_INPUT_DISPATCHING_TIMEOUT.count());
+            mNoFocusedWindowTimeoutTime = currentTime + timeout;
+            mAwaitedFocusedApplication = focusedApplicationHandle;
+            ALOGW("Waiting because no window has focus but %s may eventually add a "
+                  "window when it finishes starting up. Will wait for %" PRId64 "ms",
+                  mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout));
+            *nextWakeupTime = *mNoFocusedWindowTimeoutTime;
+            return INPUT_EVENT_INJECTION_PENDING;
+        } else if (currentTime > *mNoFocusedWindowTimeoutTime) {
+            // Already raised ANR. Drop the event
+            ALOGE("Dropping %s event because there is no focused window",
+                  EventEntry::typeToString(entry.type));
+            return INPUT_EVENT_INJECTION_FAILED;
+        } else {
+            // Still waiting for the focused window
+            return INPUT_EVENT_INJECTION_PENDING;
+        }
+    }
+
+    // we have a valid, non-null focused window
+    resetNoFocusedWindowTimeoutLocked();
+
     // Check permissions.
-    if (!checkInjectionPermission(focusedWindowHandle, entry->injectionState)) {
-        injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
-        goto Failed;
+    if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
+        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;
+    if (focusedWindowHandle->getInfo()->paused) {
+        ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str());
+        return INPUT_EVENT_INJECTION_PENDING;
+    }
+
+    // If the event is a key event, then we must wait for all previous events to
+    // complete before delivering it because previous events may have the
+    // side-effect of transferring focus to a different window and we want to
+    // ensure that the following keys are sent to the new window.
+    //
+    // Suppose the user touches a button in a window then immediately presses "A".
+    // If the button causes a pop-up window to appear then we want to ensure that
+    // the "A" key is delivered to the new pop-up window.  This is because users
+    // often anticipate pending UI changes when typing on a keyboard.
+    // To obtain this behavior, we must serialize key events with respect to all
+    // prior input events.
+    if (entry.type == EventEntry::Type::KEY) {
+        if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) {
+            *nextWakeupTime = *mKeyIsWaitingForEventsTimeout;
+            return INPUT_EVENT_INJECTION_PENDING;
+        }
     }
 
     // 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);
-#endif
-    return injectionResult;
+    return INPUT_EVENT_INJECTION_SUCCEEDED;
+}
+
+/**
+ * Given a list of monitors, remove the ones we cannot find a connection for, and the ones
+ * that are currently unresponsive.
+ */
+std::vector<TouchedMonitor> InputDispatcher::selectResponsiveMonitorsLocked(
+        const std::vector<TouchedMonitor>& monitors) const {
+    std::vector<TouchedMonitor> responsiveMonitors;
+    std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors),
+                 [this](const TouchedMonitor& monitor) REQUIRES(mLock) {
+                     sp<Connection> connection = getConnectionLocked(
+                             monitor.monitor.inputChannel->getConnectionToken());
+                     if (connection == nullptr) {
+                         ALOGE("Could not find connection for monitor %s",
+                               monitor.monitor.inputChannel->getName().c_str());
+                         return false;
+                     }
+                     if (!connection->responsive) {
+                         ALOGW("Unresponsive monitor %s will not get the new gesture",
+                               connection->inputChannel->getName().c_str());
+                         return false;
+                     }
+                     return true;
+                 });
+    return responsiveMonitors;
 }
 
 int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
-                                                        const MotionEntry* entry,
+                                                        const MotionEntry& entry,
                                                         std::vector<InputTarget>& inputTargets,
                                                         nsecs_t* nextWakeupTime,
                                                         bool* outConflictingPointerActions) {
@@ -1248,8 +1573,8 @@
 
     // For security reasons, we defer updating the touch state until we are sure that
     // event injection will be allowed.
-    int32_t displayId = entry->displayId;
-    int32_t action = entry->action;
+    int32_t displayId = entry.displayId;
+    int32_t action = entry.action;
     int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
 
     // Update the touch state as needed based on the properties of the touch event.
@@ -1257,52 +1582,51 @@
     InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN;
     sp<InputWindowHandle> newHoverWindowHandle;
 
-    // Copy current touch state into mTempTouchState.
-    // This state is always reset at the end of this function, so if we don't find state
-    // for the specified display then our initial state will be empty.
+    // Copy current touch state into tempTouchState.
+    // This state will be used to update mTouchStatesByDisplay at the end of this function.
+    // If no state for the specified display exists, then our initial state will be empty.
     const TouchState* oldState = nullptr;
-    ssize_t oldStateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
-    if (oldStateIndex >= 0) {
-        oldState = &mTouchStatesByDisplay.valueAt(oldStateIndex);
-        mTempTouchState.copyFrom(*oldState);
+    TouchState tempTouchState;
+    std::unordered_map<int32_t, TouchState>::iterator oldStateIt =
+            mTouchStatesByDisplay.find(displayId);
+    if (oldStateIt != mTouchStatesByDisplay.end()) {
+        oldState = &(oldStateIt->second);
+        tempTouchState.copyFrom(*oldState);
     }
 
-    bool isSplit = mTempTouchState.split;
-    bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 &&
-            (mTempTouchState.deviceId != entry->deviceId ||
-             mTempTouchState.source != entry->source || mTempTouchState.displayId != displayId);
+    bool isSplit = tempTouchState.split;
+    bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 &&
+            (tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source ||
+             tempTouchState.displayId != displayId);
     bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
                           maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
                           maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
     bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
                        maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
+    const bool isFromMouse = entry.source == AINPUT_SOURCE_MOUSE;
     bool wrongDevice = false;
     if (newGesture) {
         bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
-        if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) {
-#if DEBUG_FOCUS
-            ALOGD("Dropping event because a pointer for a different device is already down "
+        if (switchedDevice && tempTouchState.down && !down && !isHoverAction) {
+            ALOGI("Dropping event because a pointer for a different device is already down "
                   "in display %" PRId32,
                   displayId);
-#endif
             // TODO: test multiple simultaneous input streams.
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             switchedDevice = false;
             wrongDevice = true;
             goto Failed;
         }
-        mTempTouchState.reset();
-        mTempTouchState.down = down;
-        mTempTouchState.deviceId = entry->deviceId;
-        mTempTouchState.source = entry->source;
-        mTempTouchState.displayId = displayId;
+        tempTouchState.reset();
+        tempTouchState.down = down;
+        tempTouchState.deviceId = entry.deviceId;
+        tempTouchState.source = entry.source;
+        tempTouchState.displayId = displayId;
         isSplit = false;
     } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
-#if DEBUG_FOCUS
         ALOGI("Dropping move event because a pointer for a different device is already active "
               "in display %" PRId32,
               displayId);
-#endif
         // TODO: test multiple simultaneous input streams.
         injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
         switchedDevice = false;
@@ -1313,23 +1637,31 @@
     if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
         /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
 
+        int32_t x;
+        int32_t y;
         int32_t pointerIndex = getMotionEventActionPointerIndex(action);
-        int32_t x = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
-        int32_t y = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
+        // Always dispatch mouse events to cursor position.
+        if (isFromMouse) {
+            x = int32_t(entry.xCursorPosition);
+            y = int32_t(entry.yCursorPosition);
+        } else {
+            x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
+            y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
+        }
         bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
         sp<InputWindowHandle> newTouchedWindowHandle =
-                findTouchedWindowAtLocked(displayId, x, y, isDown /*addOutsideTargets*/,
-                                          true /*addPortalWindows*/);
+                findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
+                                          isDown /*addOutsideTargets*/, true /*addPortalWindows*/);
 
         std::vector<TouchedMonitor> newGestureMonitors = isDown
-                ? findTouchedGestureMonitorsLocked(displayId, mTempTouchState.portalWindows)
+                ? findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows)
                 : std::vector<TouchedMonitor>{};
 
         // Figure out whether splitting will be allowed for this window.
         if (newTouchedWindowHandle != nullptr &&
             newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
-            // New window supports splitting.
-            isSplit = true;
+            // New window supports splitting, but we should never split mouse events.
+            isSplit = !isFromMouse;
         } else if (isSplit) {
             // New window does not support splitting but we have already split events.
             // Ignore the new window.
@@ -1339,9 +1671,32 @@
         // Handle the case where we did not find a window.
         if (newTouchedWindowHandle == nullptr) {
             // Try to assign the pointer to the first foreground window we find, if there is one.
-            newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle();
+            newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
         }
 
+        if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->paused) {
+            ALOGI("Not sending touch event to %s because it is paused",
+                  newTouchedWindowHandle->getName().c_str());
+            newTouchedWindowHandle = nullptr;
+        }
+
+        if (newTouchedWindowHandle != nullptr) {
+            sp<Connection> connection = getConnectionLocked(newTouchedWindowHandle->getToken());
+            if (connection == nullptr) {
+                ALOGI("Could not find connection for %s",
+                      newTouchedWindowHandle->getName().c_str());
+                newTouchedWindowHandle = nullptr;
+            } else if (!connection->responsive) {
+                // don't send the new touch to an unresponsive window
+                ALOGW("Unresponsive window %s will not get the new gesture at %" PRIu64,
+                      newTouchedWindowHandle->getName().c_str(), entry.eventTime);
+                newTouchedWindowHandle = nullptr;
+            }
+        }
+
+        // Also don't send the new touch event to unresponsive gesture monitors
+        newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);
+
         if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
             ALOGI("Dropping event because there is no touchable window or gesture monitor at "
                   "(%d, %d) in display %" PRId32 ".",
@@ -1372,48 +1727,48 @@
             // Update the temporary touch state.
             BitSet32 pointerIds;
             if (isSplit) {
-                uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
+                uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
                 pointerIds.markBit(pointerId);
             }
-            mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+            tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
         }
 
-        mTempTouchState.addGestureMonitors(newGestureMonitors);
+        tempTouchState.addGestureMonitors(newGestureMonitors);
     } else {
         /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
 
         // If the pointer is not currently down, then ignore the event.
-        if (!mTempTouchState.down) {
-#if DEBUG_FOCUS
-            ALOGD("Dropping event because the pointer is not down or we previously "
-                  "dropped the pointer down event in display %" PRId32,
-                  displayId);
-#endif
+        if (!tempTouchState.down) {
+            if (DEBUG_FOCUS) {
+                ALOGD("Dropping event because the pointer is not down or we previously "
+                      "dropped the pointer down event in display %" PRId32,
+                      displayId);
+            }
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             goto Failed;
         }
 
         // Check whether touches should slip outside of the current foreground window.
-        if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry->pointerCount == 1 &&
-            mTempTouchState.isSlippery()) {
-            int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
-            int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+        if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
+            tempTouchState.isSlippery()) {
+            int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+            int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
 
             sp<InputWindowHandle> oldTouchedWindowHandle =
-                    mTempTouchState.getFirstForegroundWindowHandle();
+                    tempTouchState.getFirstForegroundWindowHandle();
             sp<InputWindowHandle> newTouchedWindowHandle =
-                    findTouchedWindowAtLocked(displayId, x, y);
+                    findTouchedWindowAtLocked(displayId, x, y, &tempTouchState);
             if (oldTouchedWindowHandle != newTouchedWindowHandle &&
                 oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
-#if DEBUG_FOCUS
-                ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
-                      oldTouchedWindowHandle->getName().c_str(),
-                      newTouchedWindowHandle->getName().c_str(), displayId);
-#endif
+                if (DEBUG_FOCUS) {
+                    ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
+                          oldTouchedWindowHandle->getName().c_str(),
+                          newTouchedWindowHandle->getName().c_str(), displayId);
+                }
                 // Make a slippery exit from the old window.
-                mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
-                                                  InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
-                                                  BitSet32(0));
+                tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
+                                                 InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
+                                                 BitSet32(0));
 
                 // Make a slippery entrance into the new window.
                 if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
@@ -1431,9 +1786,9 @@
 
                 BitSet32 pointerIds;
                 if (isSplit) {
-                    pointerIds.markBit(entry->pointerProperties[0].id);
+                    pointerIds.markBit(entry.pointerProperties[0].id);
                 }
-                mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+                tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
             }
         }
     }
@@ -1445,9 +1800,8 @@
             ALOGD("Sending hover exit event to window %s.",
                   mLastHoverWindowHandle->getName().c_str());
 #endif
-            mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
-                                              InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT,
-                                              BitSet32(0));
+            tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
+                                             InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
         }
 
         // Let the new window know that the hover sequence is starting.
@@ -1456,9 +1810,9 @@
             ALOGD("Sending hover enter event to window %s.",
                   newHoverWindowHandle->getName().c_str());
 #endif
-            mTempTouchState.addOrUpdateWindow(newHoverWindowHandle,
-                                              InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
-                                              BitSet32(0));
+            tempTouchState.addOrUpdateWindow(newHoverWindowHandle,
+                                             InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
+                                             BitSet32(0));
         }
     }
 
@@ -1466,23 +1820,21 @@
     // is at least one touched foreground window.
     {
         bool haveForegroundWindow = false;
-        for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+        for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
             if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
                 haveForegroundWindow = true;
-                if (!checkInjectionPermission(touchedWindow.windowHandle, entry->injectionState)) {
+                if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
                     injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
                     injectionPermission = INJECTION_PERMISSION_DENIED;
                     goto Failed;
                 }
             }
         }
-        bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty();
+        bool hasGestureMonitor = !tempTouchState.gestureMonitors.empty();
         if (!haveForegroundWindow && !hasGestureMonitor) {
-#if DEBUG_FOCUS
-            ALOGD("Dropping event because there is no touched foreground window in display %" PRId32
-                  " or gesture monitor to receive it.",
+            ALOGI("Dropping event because there is no touched foreground window in display "
+                  "%" PRId32 " or gesture monitor to receive it.",
                   displayId);
-#endif
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             goto Failed;
         }
@@ -1495,38 +1847,22 @@
     // set the policy flag that we will not reveal coordinate information to this window.
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
         sp<InputWindowHandle> foregroundWindowHandle =
-                mTempTouchState.getFirstForegroundWindowHandle();
+                tempTouchState.getFirstForegroundWindowHandle();
         if (foregroundWindowHandle) {
             const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
-            for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+            for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
                 if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
                     sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle;
                     if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) {
-                        mTempTouchState.addOrUpdateWindow(inputWindowHandle,
-                                                          InputTarget::FLAG_ZERO_COORDS,
-                                                          BitSet32(0));
+                        tempTouchState.addOrUpdateWindow(inputWindowHandle,
+                                                         InputTarget::FLAG_ZERO_COORDS,
+                                                         BitSet32(0));
                     }
                 }
             }
         }
     }
 
-    // Ensure all touched foreground windows are ready for new input.
-    for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
-        if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
-            // Check whether the window is ready for more input.
-            std::string reason =
-                    checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle,
-                                                       entry, "touched");
-            if (!reason.empty()) {
-                injectionResult = handleTargetsNotReadyLocked(currentTime, entry, nullptr,
-                                                              touchedWindow.windowHandle,
-                                                              nextWakeupTime, reason.c_str());
-                goto Unresponsive;
-            }
-        }
-    }
-
     // If this is the first pointer going down and the touched window has a wallpaper
     // then also add the touched wallpaper windows so they are locked in for the duration
     // of the touch gesture.
@@ -1535,7 +1871,7 @@
     // to View.onGenericMotionEvent to enable wallpapers to handle these events.
     if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
         sp<InputWindowHandle> foregroundWindowHandle =
-                mTempTouchState.getFirstForegroundWindowHandle();
+                tempTouchState.getFirstForegroundWindowHandle();
         if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) {
             const std::vector<sp<InputWindowHandle>> windowHandles =
                     getWindowHandlesLocked(displayId);
@@ -1543,7 +1879,7 @@
                 const InputWindowInfo* info = windowHandle->getInfo();
                 if (info->displayId == displayId &&
                     windowHandle->getInfo()->layoutParamsType == InputWindowInfo::TYPE_WALLPAPER) {
-                    mTempTouchState
+                    tempTouchState
                             .addOrUpdateWindow(windowHandle,
                                                InputTarget::FLAG_WINDOW_IS_OBSCURED |
                                                        InputTarget::
@@ -1558,144 +1894,139 @@
     // Success!  Output targets.
     injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
 
-    for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
+    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
         addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                               touchedWindow.pointerIds, inputTargets);
     }
 
-    for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) {
+    for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) {
         addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
                                   touchedMonitor.yOffset, inputTargets);
     }
 
     // Drop the outside or hover touch windows since we will not care about them
     // in the next iteration.
-    mTempTouchState.filterNonAsIsTouchWindows();
+    tempTouchState.filterNonAsIsTouchWindows();
 
 Failed:
     // Check injection permission once and for all.
     if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
-        if (checkInjectionPermission(nullptr, entry->injectionState)) {
+        if (checkInjectionPermission(nullptr, entry.injectionState)) {
             injectionPermission = INJECTION_PERMISSION_GRANTED;
         } else {
             injectionPermission = INJECTION_PERMISSION_DENIED;
         }
     }
 
-    // Update final pieces of touch state if the injector had permission.
-    if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
-        if (!wrongDevice) {
-            if (switchedDevice) {
-#if DEBUG_FOCUS
-                ALOGD("Conflicting pointer actions: Switched to a different device.");
-#endif
-                *outConflictingPointerActions = true;
-            }
-
-            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.");
-#endif
-                    *outConflictingPointerActions = true;
-                }
-                mTempTouchState.reset();
-                if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
-                    maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-                    mTempTouchState.deviceId = entry->deviceId;
-                    mTempTouchState.source = entry->source;
-                    mTempTouchState.displayId = displayId;
-                }
-            } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
-                       maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
-                // All pointers up or canceled.
-                mTempTouchState.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.");
-#endif
-                    *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 < mTempTouchState.windows.size();) {
-                        TouchedWindow& touchedWindow = mTempTouchState.windows[i];
-                        if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
-                            touchedWindow.pointerIds.clearBit(pointerId);
-                            if (touchedWindow.pointerIds.isEmpty()) {
-                                mTempTouchState.windows.erase(mTempTouchState.windows.begin() + i);
-                                continue;
-                            }
-                        }
-                        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 (mTempTouchState.displayId >= 0) {
-                    if (oldStateIndex >= 0) {
-                        mTouchStatesByDisplay.editValueAt(oldStateIndex).copyFrom(mTempTouchState);
-                    } else {
-                        mTouchStatesByDisplay.add(displayId, mTempTouchState);
-                    }
-                } else if (oldStateIndex >= 0) {
-                    mTouchStatesByDisplay.removeItemsAt(oldStateIndex);
-                }
-            }
-
-            // Update hover state.
-            mLastHoverWindowHandle = newHoverWindowHandle;
-        }
-    } else {
-#if DEBUG_FOCUS
-        ALOGD("Not updating touch focus because injection was denied.");
-#endif
+    if (injectionPermission != INJECTION_PERMISSION_GRANTED) {
+        return injectionResult;
     }
 
-Unresponsive:
-    // Reset temporary touch state to ensure we release unnecessary references to input channels.
-    mTempTouchState.reset();
+    // Update final pieces of touch state if the injector had permission.
+    if (!wrongDevice) {
+        if (switchedDevice) {
+            if (DEBUG_FOCUS) {
+                ALOGD("Conflicting pointer actions: Switched to a different device.");
+            }
+            *outConflictingPointerActions = true;
+        }
 
-    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);
-#endif
+        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;
+                        }
+                    }
+                    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);
+            }
+        }
+
+        // Update hover state.
+        mLastHoverWindowHandle = newHoverWindowHandle;
+    }
+
     return injectionResult;
 }
 
 void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
                                             int32_t targetFlags, BitSet32 pointerIds,
                                             std::vector<InputTarget>& inputTargets) {
-    sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
-    if (inputChannel == nullptr) {
-        ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
-        return;
-    }
+    std::vector<InputTarget>::iterator it =
+            std::find_if(inputTargets.begin(), inputTargets.end(),
+                         [&windowHandle](const InputTarget& inputTarget) {
+                             return inputTarget.inputChannel->getConnectionToken() ==
+                                     windowHandle->getToken();
+                         });
 
     const InputWindowInfo* windowInfo = windowHandle->getInfo();
-    InputTarget target;
-    target.inputChannel = inputChannel;
-    target.flags = targetFlags;
-    target.xOffset = -windowInfo->frameLeft;
-    target.yOffset = -windowInfo->frameTop;
-    target.globalScaleFactor = windowInfo->globalScaleFactor;
-    target.windowXScale = windowInfo->windowXScale;
-    target.windowYScale = windowInfo->windowYScale;
-    target.pointerIds = pointerIds;
-    inputTargets.push_back(target);
+
+    if (it == inputTargets.end()) {
+        InputTarget inputTarget;
+        sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+        if (inputChannel == nullptr) {
+            ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
+            return;
+        }
+        inputTarget.inputChannel = inputChannel;
+        inputTarget.flags = targetFlags;
+        inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
+        inputTargets.push_back(inputTarget);
+        it = inputTargets.end() - 1;
+    }
+
+    ALOG_ASSERT(it->flags == targetFlags);
+    ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
+
+    it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop,
+                    windowInfo->windowXScale, windowInfo->windowYScale);
 }
 
 void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
@@ -1718,10 +2049,7 @@
     InputTarget target;
     target.inputChannel = monitor.inputChannel;
     target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
-    target.xOffset = xOffset;
-    target.yOffset = yOffset;
-    target.pointerIds.clear();
-    target.globalScaleFactor = 1.0f;
+    target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */);
     inputTargets.push_back(target);
 }
 
@@ -1745,18 +2073,44 @@
     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) {
+        // If ownerPid is the same we don't generate occlusion events as there
+        // is no in-process security boundary.
+        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 (otherHandle == windowHandle) {
-            break;
+        if (windowHandle == otherHandle) {
+            break; // All future windows are below us. Exit early.
         }
-
         const InputWindowInfo* otherInfo = otherHandle->getInfo();
-        if (otherInfo->displayId == displayId && otherInfo->visible &&
-            !otherInfo->isTrustedOverlay() && otherInfo->frameContainsPoint(x, y)) {
+          if (canBeObscuredBy(windowHandle, otherHandle) &&
+            otherInfo->frameContainsPoint(x, y)) {
             return true;
         }
     }
@@ -1768,127 +2122,40 @@
     const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
     const InputWindowInfo* windowInfo = windowHandle->getInfo();
     for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
-        if (otherHandle == windowHandle) {
-            break;
+        if (windowHandle == otherHandle) {
+            break; // All future windows are below us. Exit early.
         }
 
         const InputWindowInfo* otherInfo = otherHandle->getInfo();
-        if (otherInfo->displayId == displayId && otherInfo->visible &&
-            !otherInfo->isTrustedOverlay() && otherInfo->overlaps(windowInfo)) {
+        if (canBeObscuredBy(windowHandle, otherHandle) &&
+            otherInfo->overlaps(windowInfo)) {
             return true;
         }
     }
     return false;
 }
 
-std::string InputDispatcher::checkWindowReadyForMoreInputLocked(
-        nsecs_t currentTime, const sp<InputWindowHandle>& windowHandle,
-        const EventEntry* eventEntry, const char* targetType) {
-    // If the window is paused then keep waiting.
-    if (windowHandle->getInfo()->paused) {
-        return StringPrintf("Waiting because the %s window is paused.", targetType);
-    }
-
-    // If the window's connection is not registered then keep waiting.
-    ssize_t connectionIndex =
-            getConnectionIndexLocked(getInputChannelLocked(windowHandle->getToken()));
-    if (connectionIndex < 0) {
-        return StringPrintf("Waiting because the %s window's input channel is not "
-                            "registered with the input dispatcher.  The window may be in the "
-                            "process "
-                            "of being removed.",
-                            targetType);
-    }
-
-    // If the connection is dead then keep waiting.
-    sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
-    if (connection->status != Connection::STATUS_NORMAL) {
-        return StringPrintf("Waiting because the %s window's input connection is %s."
-                            "The window may be in the process of being removed.",
-                            targetType, connection->getStatusLabel());
-    }
-
-    // If the connection is backed up then keep waiting.
-    if (connection->inputPublisherBlocked) {
-        return StringPrintf("Waiting because the %s window's input channel is full.  "
-                            "Outbound queue length: %d.  Wait queue length: %d.",
-                            targetType, connection->outboundQueue.count(),
-                            connection->waitQueue.count());
-    }
-
-    // Ensure that the dispatch queues aren't too far backed up for this event.
-    if (eventEntry->type == EventEntry::TYPE_KEY) {
-        // If the event is a key event, then we must wait for all previous events to
-        // complete before delivering it because previous events may have the
-        // side-effect of transferring focus to a different window and we want to
-        // ensure that the following keys are sent to the new window.
-        //
-        // Suppose the user touches a button in a window then immediately presses "A".
-        // If the button causes a pop-up window to appear then we want to ensure that
-        // the "A" key is delivered to the new pop-up window.  This is because users
-        // often anticipate pending UI changes when typing on a keyboard.
-        // To obtain this behavior, we must serialize key events with respect to all
-        // prior input events.
-        if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty()) {
-            return StringPrintf("Waiting to send key event because the %s window has not "
-                                "finished processing all of the input events that were previously "
-                                "delivered to it.  Outbound queue length: %d.  Wait queue length: "
-                                "%d.",
-                                targetType, connection->outboundQueue.count(),
-                                connection->waitQueue.count());
-        }
-    } else {
-        // Touch events can always be sent to a window immediately because the user intended
-        // to touch whatever was visible at the time.  Even if focus changes or a new
-        // window appears moments later, the touch event was meant to be delivered to
-        // whatever window happened to be on screen at the time.
-        //
-        // Generic motion events, such as trackball or joystick events are a little trickier.
-        // Like key events, generic motion events are delivered to the focused window.
-        // Unlike key events, generic motion events don't tend to transfer focus to other
-        // windows and it is not important for them to be serialized.  So we prefer to deliver
-        // generic motion events as soon as possible to improve efficiency and reduce lag
-        // through batching.
-        //
-        // The one case where we pause input event delivery is when the wait queue is piling
-        // up with lots of events because the application is not responding.
-        // This condition ensures that ANRs are detected reliably.
-        if (!connection->waitQueue.isEmpty() &&
-            currentTime >= connection->waitQueue.head->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) {
-            return StringPrintf("Waiting to send non-key event because the %s window has not "
-                                "finished processing certain input events that were delivered to "
-                                "it over "
-                                "%0.1fms ago.  Wait queue length: %d.  Wait queue head age: "
-                                "%0.1fms.",
-                                targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
-                                connection->waitQueue.count(),
-                                (currentTime - connection->waitQueue.head->deliveryTime) *
-                                        0.000001f);
-        }
-    }
-    return "";
-}
-
 std::string InputDispatcher::getApplicationWindowLabel(
         const sp<InputApplicationHandle>& applicationHandle,
         const sp<InputWindowHandle>& windowHandle) {
     if (applicationHandle != nullptr) {
         if (windowHandle != nullptr) {
-            std::string label(applicationHandle->getName());
-            label += " - ";
-            label += windowHandle->getName();
-            return label;
+            return applicationHandle->getName() + " - " + windowHandle->getName();
         } else {
             return applicationHandle->getName();
         }
     } else if (windowHandle != nullptr) {
-        return windowHandle->getName();
+        return windowHandle->getInfo()->applicationInfo.name + " - " + windowHandle->getName();
     } else {
         return "<unknown application or window>";
     }
 }
 
-void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) {
+void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
+    if (eventEntry.type == EventEntry::Type::FOCUS) {
+        // Focus events are passed to apps, but do not represent user activity.
+        return;
+    }
     int32_t displayId = getTargetDisplayId(eventEntry);
     sp<InputWindowHandle> focusedWindowHandle =
             getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
@@ -1903,51 +2170,58 @@
     }
 
     int32_t eventType = USER_ACTIVITY_EVENT_OTHER;
-    switch (eventEntry->type) {
-        case EventEntry::TYPE_MOTION: {
-            const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry);
-            if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) {
+    switch (eventEntry.type) {
+        case EventEntry::Type::MOTION: {
+            const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
+            if (motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) {
                 return;
             }
 
-            if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) {
+            if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) {
                 eventType = USER_ACTIVITY_EVENT_TOUCH;
             }
             break;
         }
-        case EventEntry::TYPE_KEY: {
-            const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry);
-            if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) {
+        case EventEntry::Type::KEY: {
+            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
+            if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) {
                 return;
             }
             eventType = USER_ACTIVITY_EVENT_BUTTON;
             break;
         }
+        case EventEntry::Type::FOCUS:
+        case EventEntry::Type::CONFIGURATION_CHANGED:
+        case EventEntry::Type::DEVICE_RESET: {
+            LOG_ALWAYS_FATAL("%s events are not user activity",
+                             EventEntry::typeToString(eventEntry.type));
+            break;
+        }
     }
 
-    CommandEntry* commandEntry =
-            postCommandLocked(&InputDispatcher::doPokeUserActivityLockedInterruptible);
-    commandEntry->eventTime = eventEntry->eventTime;
+    std::unique_ptr<CommandEntry> commandEntry =
+            std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);
+    commandEntry->eventTime = eventEntry.eventTime;
     commandEntry->userActivityEventType = eventType;
+    postCommandLocked(std::move(commandEntry));
 }
 
 void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
                                                  const sp<Connection>& connection,
                                                  EventEntry* eventEntry,
-                                                 const InputTarget* inputTarget) {
+                                                 const InputTarget& inputTarget) {
     if (ATRACE_ENABLED()) {
         std::string message =
-                StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
-                             connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
+                StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, id=0x%" PRIx32 ")",
+                             connection->getInputChannelName().c_str(), eventEntry->id);
         ATRACE_NAME(message.c_str());
     }
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
-          "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
-          "windowScaleFactor=(%f, %f), pointerIds=0x%x",
-          connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset,
-          inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale,
-          inputTarget->windowYScale, inputTarget->pointerIds.value);
+          "globalScaleFactor=%f, pointerIds=0x%x %s",
+          connection->getInputChannelName().c_str(), inputTarget.flags,
+          inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
+          inputTarget.getPointerInfoString().c_str());
 #endif
 
     // Skip this event if the connection status is not normal.
@@ -1961,20 +2235,23 @@
     }
 
     // Split a motion event if needed.
-    if (inputTarget->flags & InputTarget::FLAG_SPLIT) {
-        ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION);
+    if (inputTarget.flags & InputTarget::FLAG_SPLIT) {
+        LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION,
+                            "Entry type %s should not have FLAG_SPLIT",
+                            EventEntry::typeToString(eventEntry->type));
 
-        MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
-        if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
+        const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
+        if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
             MotionEntry* splitMotionEntry =
-                    splitMotionEvent(originalMotionEntry, inputTarget->pointerIds);
+                    splitMotionEvent(originalMotionEntry, inputTarget.pointerIds);
             if (!splitMotionEntry) {
                 return; // split event was dropped
             }
-#if DEBUG_FOCUS
-            ALOGD("channel '%s' ~ Split motion event.", connection->getInputChannelName().c_str());
-            logOutboundMotionDetails("  ", splitMotionEntry);
-#endif
+            if (DEBUG_FOCUS) {
+                ALOGD("channel '%s' ~ Split motion event.",
+                      connection->getInputChannelName().c_str());
+                logOutboundMotionDetails("  ", *splitMotionEntry);
+            }
             enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget);
             splitMotionEntry->release();
             return;
@@ -1988,16 +2265,15 @@
 void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
                                                    const sp<Connection>& connection,
                                                    EventEntry* eventEntry,
-                                                   const InputTarget* inputTarget) {
+                                                   const InputTarget& inputTarget) {
     if (ATRACE_ENABLED()) {
         std::string message =
-                StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32
-                             ")",
-                             connection->getInputChannelName().c_str(), eventEntry->sequenceNum);
+                StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, id=0x%" PRIx32 ")",
+                             connection->getInputChannelName().c_str(), eventEntry->id);
         ATRACE_NAME(message.c_str());
     }
 
-    bool wasEmpty = connection->outboundQueue.isEmpty();
+    bool wasEmpty = connection->outboundQueue.empty();
 
     // Enqueue dispatch entries for the requested modes.
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
@@ -2014,14 +2290,14 @@
                                InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
 
     // If the outbound queue was previously empty, start the dispatch cycle going.
-    if (wasEmpty && !connection->outboundQueue.isEmpty()) {
+    if (wasEmpty && !connection->outboundQueue.empty()) {
         startDispatchCycleLocked(currentTime, connection);
     }
 }
 
 void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
                                                  EventEntry* eventEntry,
-                                                 const InputTarget* inputTarget,
+                                                 const InputTarget& inputTarget,
                                                  int32_t dispatchMode) {
     if (ATRACE_ENABLED()) {
         std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)",
@@ -2029,7 +2305,7 @@
                                            dispatchModeToString(dispatchMode).c_str());
         ATRACE_NAME(message.c_str());
     }
-    int32_t inputTargetFlags = inputTarget->flags;
+    int32_t inputTargetFlags = inputTarget.flags;
     if (!(inputTargetFlags & dispatchMode)) {
         return;
     }
@@ -2037,18 +2313,19 @@
 
     // This is a new event.
     // Enqueue a new dispatch entry onto the outbound queue for this connection.
-    DispatchEntry* dispatchEntry =
-            new DispatchEntry(eventEntry, // increments ref
-                              inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
-                              inputTarget->globalScaleFactor, inputTarget->windowXScale,
-                              inputTarget->windowYScale);
+    std::unique_ptr<DispatchEntry> dispatchEntry =
+            createDispatchEntry(inputTarget, eventEntry, inputTargetFlags);
 
+    // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
+    // different EventEntry than what was passed in.
+    EventEntry* newEntry = dispatchEntry->eventEntry;
     // Apply target flags and update the connection's input state.
-    switch (eventEntry->type) {
-        case EventEntry::TYPE_KEY: {
-            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
-            dispatchEntry->resolvedAction = keyEntry->action;
-            dispatchEntry->resolvedFlags = keyEntry->flags;
+    switch (newEntry->type) {
+        case EventEntry::Type::KEY: {
+            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*newEntry);
+            dispatchEntry->resolvedEventId = keyEntry.id;
+            dispatchEntry->resolvedAction = keyEntry.action;
+            dispatchEntry->resolvedFlags = keyEntry.flags;
 
             if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
                                                  dispatchEntry->resolvedFlags)) {
@@ -2056,14 +2333,18 @@
                 ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
                       connection->getInputChannelName().c_str());
 #endif
-                delete dispatchEntry;
                 return; // skip the inconsistent event
             }
             break;
         }
 
-        case EventEntry::TYPE_MOTION: {
-            MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+        case EventEntry::Type::MOTION: {
+            const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*newEntry);
+            // Assign a default value to dispatchEntry that will never be generated by InputReader,
+            // and assign a InputDispatcher value if it doesn't change in the if-else chain below.
+            constexpr int32_t DEFAULT_RESOLVED_EVENT_ID =
+                    static_cast<int32_t>(IdGenerator::Source::OTHER);
+            dispatchEntry->resolvedEventId = DEFAULT_RESOLVED_EVENT_ID;
             if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
                 dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
             } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
@@ -2075,11 +2356,12 @@
             } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
                 dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
             } else {
-                dispatchEntry->resolvedAction = motionEntry->action;
+                dispatchEntry->resolvedAction = motionEntry.action;
+                dispatchEntry->resolvedEventId = motionEntry.id;
             }
             if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
-                !connection->inputState.isHovering(motionEntry->deviceId, motionEntry->source,
-                                                   motionEntry->displayId)) {
+                !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
+                                                   motionEntry.displayId)) {
 #if DEBUG_DISPATCH_CYCLE
                 ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter "
                       "event",
@@ -2088,7 +2370,7 @@
                 dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
             }
 
-            dispatchEntry->resolvedFlags = motionEntry->flags;
+            dispatchEntry->resolvedFlags = motionEntry.flags;
             if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
                 dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
             }
@@ -2103,24 +2385,43 @@
                       "event",
                       connection->getInputChannelName().c_str());
 #endif
-                delete dispatchEntry;
                 return; // skip the inconsistent event
             }
 
-            dispatchPointerDownOutsideFocus(motionEntry->source, dispatchEntry->resolvedAction,
-                                            inputTarget->inputChannel->getToken());
+            dispatchEntry->resolvedEventId =
+                    dispatchEntry->resolvedEventId == DEFAULT_RESOLVED_EVENT_ID
+                    ? mIdGenerator.nextId()
+                    : motionEntry.id;
+            if (ATRACE_ENABLED() && dispatchEntry->resolvedEventId != motionEntry.id) {
+                std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32
+                                                   ") to MotionEvent(id=0x%" PRIx32 ").",
+                                                   motionEntry.id, dispatchEntry->resolvedEventId);
+                ATRACE_NAME(message.c_str());
+            }
 
+            dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,
+                                            inputTarget.inputChannel->getConnectionToken());
+
+            break;
+        }
+        case EventEntry::Type::FOCUS: {
+            break;
+        }
+        case EventEntry::Type::CONFIGURATION_CHANGED:
+        case EventEntry::Type::DEVICE_RESET: {
+            LOG_ALWAYS_FATAL("%s events should not go to apps",
+                             EventEntry::typeToString(newEntry->type));
             break;
         }
     }
 
     // Remember that we are waiting for this dispatch to complete.
     if (dispatchEntry->hasForegroundTarget()) {
-        incrementPendingForegroundDispatches(eventEntry);
+        incrementPendingForegroundDispatches(newEntry);
     }
 
     // Enqueue the dispatch entry.
-    connection->outboundQueue.enqueueAtTail(dispatchEntry);
+    connection->outboundQueue.push_back(dispatchEntry.release());
     traceOutboundQueueLength(connection);
 }
 
@@ -2146,9 +2447,10 @@
         return;
     }
 
-    CommandEntry* commandEntry =
-            postCommandLocked(&InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
     commandEntry->newToken = newToken;
+    postCommandLocked(std::move(commandEntry));
 }
 
 void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -2162,56 +2464,63 @@
     ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
 #endif
 
-    while (connection->status == Connection::STATUS_NORMAL &&
-           !connection->outboundQueue.isEmpty()) {
-        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
+    while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
+        DispatchEntry* dispatchEntry = connection->outboundQueue.front();
         dispatchEntry->deliveryTime = currentTime;
+        const nsecs_t timeout =
+                getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken());
+        dispatchEntry->timeoutTime = currentTime + timeout;
 
         // Publish the event.
         status_t status;
         EventEntry* eventEntry = dispatchEntry->eventEntry;
         switch (eventEntry->type) {
-            case EventEntry::TYPE_KEY: {
-                KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
+            case EventEntry::Type::KEY: {
+                const KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
+                std::array<uint8_t, 32> hmac = getSignature(*keyEntry, *dispatchEntry);
 
                 // Publish the key event.
-                status = connection->inputPublisher
-                                 .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId,
-                                                  keyEntry->source, keyEntry->displayId,
-                                                  dispatchEntry->resolvedAction,
-                                                  dispatchEntry->resolvedFlags, keyEntry->keyCode,
-                                                  keyEntry->scanCode, keyEntry->metaState,
-                                                  keyEntry->repeatCount, keyEntry->downTime,
-                                                  keyEntry->eventTime);
+                status =
+                        connection->inputPublisher
+                                .publishKeyEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId,
+                                                 keyEntry->deviceId, keyEntry->source,
+                                                 keyEntry->displayId, std::move(hmac),
+                                                 dispatchEntry->resolvedAction,
+                                                 dispatchEntry->resolvedFlags, keyEntry->keyCode,
+                                                 keyEntry->scanCode, keyEntry->metaState,
+                                                 keyEntry->repeatCount, keyEntry->downTime,
+                                                 keyEntry->eventTime);
                 break;
             }
 
-            case EventEntry::TYPE_MOTION: {
+            case EventEntry::Type::MOTION: {
                 MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
 
                 PointerCoords scaledCoords[MAX_POINTERS];
                 const PointerCoords* usingCoords = motionEntry->pointerCoords;
 
-                // Set the X and Y offset depending on the input source.
-                float xOffset, yOffset;
+                // Set the X and Y offset and X and Y scale depending on the input source.
+                float xOffset = 0.0f, yOffset = 0.0f;
+                float xScale = 1.0f, yScale = 1.0f;
                 if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
                     !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
                     float globalScaleFactor = dispatchEntry->globalScaleFactor;
-                    float wxs = dispatchEntry->windowXScale;
-                    float wys = dispatchEntry->windowYScale;
-                    xOffset = dispatchEntry->xOffset * wxs;
-                    yOffset = dispatchEntry->yOffset * wys;
-                    if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) {
+                    xScale = dispatchEntry->windowXScale;
+                    yScale = dispatchEntry->windowYScale;
+                    xOffset = dispatchEntry->xOffset * xScale;
+                    yOffset = dispatchEntry->yOffset * yScale;
+                    if (globalScaleFactor != 1.0f) {
                         for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
                             scaledCoords[i] = motionEntry->pointerCoords[i];
-                            scaledCoords[i].scale(globalScaleFactor, wxs, wys);
+                            // Don't apply window scale here since we don't want scale to affect raw
+                            // coordinates. The scale will be sent back to the client and applied
+                            // later when requesting relative coordinates.
+                            scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */,
+                                                  1 /* windowYScale */);
                         }
                         usingCoords = scaledCoords;
                     }
                 } else {
-                    xOffset = 0.0f;
-                    yOffset = 0.0f;
-
                     // We don't want the dispatch target to know.
                     if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
                         for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
@@ -2221,33 +2530,51 @@
                     }
                 }
 
+                std::array<uint8_t, 32> hmac = getSignature(*motionEntry, *dispatchEntry);
+
                 // Publish the motion event.
                 status = connection->inputPublisher
-                                 .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId,
-                                                     motionEntry->source, motionEntry->displayId,
+                                 .publishMotionEvent(dispatchEntry->seq,
+                                                     dispatchEntry->resolvedEventId,
+                                                     motionEntry->deviceId, motionEntry->source,
+                                                     motionEntry->displayId, std::move(hmac),
                                                      dispatchEntry->resolvedAction,
                                                      motionEntry->actionButton,
                                                      dispatchEntry->resolvedFlags,
                                                      motionEntry->edgeFlags, motionEntry->metaState,
                                                      motionEntry->buttonState,
-                                                     motionEntry->classification, xOffset, yOffset,
-                                                     motionEntry->xPrecision,
-                                                     motionEntry->yPrecision, motionEntry->downTime,
-                                                     motionEntry->eventTime,
+                                                     motionEntry->classification, xScale, yScale,
+                                                     xOffset, yOffset, motionEntry->xPrecision,
+                                                     motionEntry->yPrecision,
+                                                     motionEntry->xCursorPosition,
+                                                     motionEntry->yCursorPosition,
+                                                     motionEntry->downTime, motionEntry->eventTime,
                                                      motionEntry->pointerCount,
                                                      motionEntry->pointerProperties, usingCoords);
+                reportTouchEventForStatistics(*motionEntry);
+                break;
+            }
+            case EventEntry::Type::FOCUS: {
+                FocusEntry* focusEntry = static_cast<FocusEntry*>(eventEntry);
+                status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq,
+                                                                      focusEntry->id,
+                                                                      focusEntry->hasFocus,
+                                                                      mInTouchMode);
                 break;
             }
 
-            default:
-                ALOG_ASSERT(false);
+            case EventEntry::Type::CONFIGURATION_CHANGED:
+            case EventEntry::Type::DEVICE_RESET: {
+                LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
+                                 EventEntry::typeToString(eventEntry->type));
                 return;
+            }
         }
 
         // Check the result.
         if (status) {
             if (status == WOULD_BLOCK) {
-                if (connection->waitQueue.isEmpty()) {
+                if (connection->waitQueue.empty()) {
                     ALOGE("channel '%s' ~ Could not publish event because the pipe is full. "
                           "This is unexpected because the wait queue is empty, so the pipe "
                           "should be empty and we shouldn't have any problems writing an "
@@ -2262,7 +2589,6 @@
                           "waiting for the application to catch up",
                           connection->getInputChannelName().c_str());
 #endif
-                    connection->inputPublisherBlocked = true;
                 }
             } else {
                 ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, "
@@ -2274,13 +2600,41 @@
         }
 
         // Re-enqueue the event on the wait queue.
-        connection->outboundQueue.dequeue(dispatchEntry);
+        connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),
+                                                    connection->outboundQueue.end(),
+                                                    dispatchEntry));
         traceOutboundQueueLength(connection);
-        connection->waitQueue.enqueueAtTail(dispatchEntry);
+        connection->waitQueue.push_back(dispatchEntry);
+        if (connection->responsive) {
+            mAnrTracker.insert(dispatchEntry->timeoutTime,
+                               connection->inputChannel->getConnectionToken());
+        }
         traceWaitQueueLength(connection);
     }
 }
 
+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) {
@@ -2289,8 +2643,6 @@
           connection->getInputChannelName().c_str(), seq, toString(handled));
 #endif
 
-    connection->inputPublisherBlocked = false;
-
     if (connection->status == Connection::STATUS_BROKEN ||
         connection->status == Connection::STATUS_ZOMBIE) {
         return;
@@ -2309,9 +2661,9 @@
 #endif
 
     // Clear the dispatch queues.
-    drainDispatchQueue(&connection->outboundQueue);
+    drainDispatchQueue(connection->outboundQueue);
     traceOutboundQueueLength(connection);
-    drainDispatchQueue(&connection->waitQueue);
+    drainDispatchQueue(connection->waitQueue);
     traceWaitQueueLength(connection);
 
     // The connection appears to be unrecoverably broken.
@@ -2326,9 +2678,10 @@
     }
 }
 
-void InputDispatcher::drainDispatchQueue(Queue<DispatchEntry>* queue) {
-    while (!queue->isEmpty()) {
-        DispatchEntry* dispatchEntry = queue->dequeueAtHead();
+void InputDispatcher::drainDispatchQueue(std::deque<DispatchEntry*>& queue) {
+    while (!queue.empty()) {
+        DispatchEntry* dispatchEntry = queue.front();
+        queue.pop_front();
         releaseDispatchEntry(dispatchEntry);
     }
 }
@@ -2346,8 +2699,7 @@
     { // acquire lock
         std::scoped_lock _l(d->mLock);
 
-        ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
-        if (connectionIndex < 0) {
+        if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) {
             ALOGE("Received spurious receive callback for unknown input channel.  "
                   "fd=%d, events=0x%x",
                   fd, events);
@@ -2355,7 +2707,7 @@
         }
 
         bool notify;
-        sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
+        sp<Connection> connection = d->mConnectionsByFd[fd];
         if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
             if (!(events & ALOOPER_EVENT_INPUT)) {
                 ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
@@ -2393,7 +2745,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",
@@ -2409,8 +2764,8 @@
 
 void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
         const CancelationOptions& options) {
-    for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
-        synthesizeCancelationEventsForConnectionLocked(mConnectionsByFd.valueAt(i), options);
+    for (const auto& pair : mConnectionsByFd) {
+        synthesizeCancelationEventsForConnectionLocked(pair.second, options);
     }
 }
 
@@ -2433,10 +2788,12 @@
 
 void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
         const sp<InputChannel>& channel, const CancelationOptions& options) {
-    ssize_t index = getConnectionIndexLocked(channel);
-    if (index >= 0) {
-        synthesizeCancelationEventsForConnectionLocked(mConnectionsByFd.valueAt(index), options);
+    sp<Connection> connection = getConnectionLocked(channel->getConnectionToken());
+    if (connection == nullptr) {
+        return;
     }
+
+    synthesizeCancelationEventsForConnectionLocked(connection, options);
 }
 
 void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
@@ -2447,58 +2804,125 @@
 
     nsecs_t currentTime = now();
 
-    std::vector<EventEntry*> cancelationEvents;
-    connection->inputState.synthesizeCancelationEvents(currentTime, cancelationEvents, options);
+    std::vector<EventEntry*> cancelationEvents =
+            connection->inputState.synthesizeCancelationEvents(currentTime, options);
 
-    if (!cancelationEvents.empty()) {
+    if (cancelationEvents.empty()) {
+        return;
+    }
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-        ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
-              "with reality: %s, mode=%d.",
-              connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
-              options.mode);
+    ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
+          "with reality: %s, mode=%d.",
+          connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
+          options.mode);
 #endif
-        for (size_t i = 0; i < cancelationEvents.size(); i++) {
-            EventEntry* cancelationEventEntry = cancelationEvents[i];
-            switch (cancelationEventEntry->type) {
-                case EventEntry::TYPE_KEY:
-                    logOutboundKeyDetails("cancel - ",
-                                          static_cast<KeyEntry*>(cancelationEventEntry));
-                    break;
-                case EventEntry::TYPE_MOTION:
-                    logOutboundMotionDetails("cancel - ",
-                                             static_cast<MotionEntry*>(cancelationEventEntry));
-                    break;
+
+    InputTarget target;
+    sp<InputWindowHandle> windowHandle =
+            getWindowHandleLocked(connection->inputChannel->getConnectionToken());
+    if (windowHandle != nullptr) {
+        const InputWindowInfo* windowInfo = windowHandle->getInfo();
+        target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
+                                     windowInfo->windowXScale, windowInfo->windowYScale);
+        target.globalScaleFactor = windowInfo->globalScaleFactor;
+    }
+    target.inputChannel = connection->inputChannel;
+    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+
+    for (size_t i = 0; i < cancelationEvents.size(); i++) {
+        EventEntry* cancelationEventEntry = cancelationEvents[i];
+        switch (cancelationEventEntry->type) {
+            case EventEntry::Type::KEY: {
+                logOutboundKeyDetails("cancel - ",
+                                      static_cast<const KeyEntry&>(*cancelationEventEntry));
+                break;
             }
-
-            InputTarget target;
-            sp<InputWindowHandle> windowHandle =
-                    getWindowHandleLocked(connection->inputChannel->getToken());
-            if (windowHandle != nullptr) {
-                const InputWindowInfo* windowInfo = windowHandle->getInfo();
-                target.xOffset = -windowInfo->frameLeft;
-                target.yOffset = -windowInfo->frameTop;
-                target.globalScaleFactor = windowInfo->globalScaleFactor;
-                target.windowXScale = windowInfo->windowXScale;
-                target.windowYScale = windowInfo->windowYScale;
-            } else {
-                target.xOffset = 0;
-                target.yOffset = 0;
-                target.globalScaleFactor = 1.0f;
+            case EventEntry::Type::MOTION: {
+                logOutboundMotionDetails("cancel - ",
+                                         static_cast<const MotionEntry&>(*cancelationEventEntry));
+                break;
             }
-            target.inputChannel = connection->inputChannel;
-            target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
-
-            enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
-                                       &target, InputTarget::FLAG_DISPATCH_AS_IS);
-
-            cancelationEventEntry->release();
+            case EventEntry::Type::FOCUS: {
+                LOG_ALWAYS_FATAL("Canceling focus events is not supported");
+                break;
+            }
+            case EventEntry::Type::CONFIGURATION_CHANGED:
+            case EventEntry::Type::DEVICE_RESET: {
+                LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
+                                 EventEntry::typeToString(cancelationEventEntry->type));
+                break;
+            }
         }
 
-        startDispatchCycleLocked(currentTime, connection);
+        enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
+                                   target, InputTarget::FLAG_DISPATCH_AS_IS);
+
+        cancelationEventEntry->release();
     }
+
+    startDispatchCycleLocked(currentTime, connection);
 }
 
-MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry,
+void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
+        const sp<Connection>& connection) {
+    if (connection->status == Connection::STATUS_BROKEN) {
+        return;
+    }
+
+    nsecs_t currentTime = now();
+
+    std::vector<EventEntry*> downEvents =
+            connection->inputState.synthesizePointerDownEvents(currentTime);
+
+    if (downEvents.empty()) {
+        return;
+    }
+
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+        ALOGD("channel '%s' ~ Synthesized %zu down events to ensure consistent event stream.",
+              connection->getInputChannelName().c_str(), downEvents.size());
+#endif
+
+    InputTarget target;
+    sp<InputWindowHandle> windowHandle =
+            getWindowHandleLocked(connection->inputChannel->getConnectionToken());
+    if (windowHandle != nullptr) {
+        const InputWindowInfo* windowInfo = windowHandle->getInfo();
+        target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
+                                     windowInfo->windowXScale, windowInfo->windowYScale);
+        target.globalScaleFactor = windowInfo->globalScaleFactor;
+    }
+    target.inputChannel = connection->inputChannel;
+    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+
+    for (EventEntry* downEventEntry : downEvents) {
+        switch (downEventEntry->type) {
+            case EventEntry::Type::MOTION: {
+                logOutboundMotionDetails("down - ",
+                        static_cast<const MotionEntry&>(*downEventEntry));
+                break;
+            }
+
+            case EventEntry::Type::KEY:
+            case EventEntry::Type::FOCUS:
+            case EventEntry::Type::CONFIGURATION_CHANGED:
+            case EventEntry::Type::DEVICE_RESET: {
+                LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
+                                     EventEntry::typeToString(downEventEntry->type));
+                break;
+            }
+        }
+
+        enqueueDispatchEntryLocked(connection, downEventEntry, // increments ref
+                                   target, InputTarget::FLAG_DISPATCH_AS_IS);
+
+        downEventEntry->release();
+    }
+
+    startDispatchCycleLocked(currentTime, connection);
+}
+
+MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotionEntry,
                                                BitSet32 pointerIds) {
     ALOG_ASSERT(pointerIds.value != 0);
 
@@ -2506,19 +2930,19 @@
     PointerProperties splitPointerProperties[MAX_POINTERS];
     PointerCoords splitPointerCoords[MAX_POINTERS];
 
-    uint32_t originalPointerCount = originalMotionEntry->pointerCount;
+    uint32_t originalPointerCount = originalMotionEntry.pointerCount;
     uint32_t splitPointerCount = 0;
 
     for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
          originalPointerIndex++) {
         const PointerProperties& pointerProperties =
-                originalMotionEntry->pointerProperties[originalPointerIndex];
+                originalMotionEntry.pointerProperties[originalPointerIndex];
         uint32_t pointerId = uint32_t(pointerProperties.id);
         if (pointerIds.hasBit(pointerId)) {
             splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
             splitPointerProperties[splitPointerCount].copyFrom(pointerProperties);
             splitPointerCoords[splitPointerCount].copyFrom(
-                    originalMotionEntry->pointerCoords[originalPointerIndex]);
+                    originalMotionEntry.pointerCoords[originalPointerIndex]);
             splitPointerCount += 1;
         }
     }
@@ -2536,13 +2960,13 @@
         return nullptr;
     }
 
-    int32_t action = originalMotionEntry->action;
+    int32_t action = originalMotionEntry.action;
     int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
     if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN ||
         maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
         int32_t originalPointerIndex = getMotionEventActionPointerIndex(action);
         const PointerProperties& pointerProperties =
-                originalMotionEntry->pointerProperties[originalPointerIndex];
+                originalMotionEntry.pointerProperties[originalPointerIndex];
         uint32_t pointerId = uint32_t(pointerProperties.id);
         if (pointerIds.hasBit(pointerId)) {
             if (pointerIds.count() == 1) {
@@ -2565,19 +2989,27 @@
         }
     }
 
+    int32_t newId = mIdGenerator.nextId();
+    if (ATRACE_ENABLED()) {
+        std::string message = StringPrintf("Split MotionEvent(id=0x%" PRIx32
+                                           ") to MotionEvent(id=0x%" PRIx32 ").",
+                                           originalMotionEntry.id, newId);
+        ATRACE_NAME(message.c_str());
+    }
     MotionEntry* splitMotionEntry =
-            new MotionEntry(originalMotionEntry->sequenceNum, originalMotionEntry->eventTime,
-                            originalMotionEntry->deviceId, originalMotionEntry->source,
-                            originalMotionEntry->displayId, originalMotionEntry->policyFlags,
-                            action, originalMotionEntry->actionButton, originalMotionEntry->flags,
-                            originalMotionEntry->metaState, originalMotionEntry->buttonState,
-                            originalMotionEntry->classification, originalMotionEntry->edgeFlags,
-                            originalMotionEntry->xPrecision, originalMotionEntry->yPrecision,
-                            originalMotionEntry->downTime, splitPointerCount,
-                            splitPointerProperties, splitPointerCoords, 0, 0);
+            new MotionEntry(newId, originalMotionEntry.eventTime, originalMotionEntry.deviceId,
+                            originalMotionEntry.source, originalMotionEntry.displayId,
+                            originalMotionEntry.policyFlags, action,
+                            originalMotionEntry.actionButton, originalMotionEntry.flags,
+                            originalMotionEntry.metaState, originalMotionEntry.buttonState,
+                            originalMotionEntry.classification, originalMotionEntry.edgeFlags,
+                            originalMotionEntry.xPrecision, originalMotionEntry.yPrecision,
+                            originalMotionEntry.xCursorPosition,
+                            originalMotionEntry.yCursorPosition, originalMotionEntry.downTime,
+                            splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0);
 
-    if (originalMotionEntry->injectionState) {
-        splitMotionEntry->injectionState = originalMotionEntry->injectionState;
+    if (originalMotionEntry.injectionState) {
+        splitMotionEntry->injectionState = originalMotionEntry.injectionState;
         splitMotionEntry->injectionState->refCount += 1;
     }
 
@@ -2594,7 +3026,7 @@
         std::scoped_lock _l(mLock);
 
         ConfigurationChangedEntry* newEntry =
-                new ConfigurationChangedEntry(args->sequenceNum, args->eventTime);
+                new ConfigurationChangedEntry(args->id, args->eventTime);
         needWake = enqueueInboundEventLocked(newEntry);
     } // release lock
 
@@ -2621,7 +3053,7 @@
         if (newKeyCode != AKEYCODE_UNKNOWN) {
             std::scoped_lock _l(mLock);
             struct KeyReplacement replacement = {keyCode, deviceId};
-            mReplacedKeys.add(replacement, newKeyCode);
+            mReplacedKeys[replacement] = newKeyCode;
             keyCode = newKeyCode;
             metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
         }
@@ -2631,10 +3063,10 @@
         // even if the modifier was released between the down and the up events.
         std::scoped_lock _l(mLock);
         struct KeyReplacement replacement = {keyCode, deviceId};
-        ssize_t index = mReplacedKeys.indexOfKey(replacement);
-        if (index >= 0) {
-            keyCode = mReplacedKeys.valueAt(index);
-            mReplacedKeys.removeItemsAt(index);
+        auto replacementIt = mReplacedKeys.find(replacement);
+        if (replacementIt != mReplacedKeys.end()) {
+            keyCode = replacementIt->second;
+            mReplacedKeys.erase(replacementIt);
             metaState &= ~(AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON);
         }
     }
@@ -2673,8 +3105,9 @@
     accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState);
 
     KeyEvent event;
-    event.initialize(args->deviceId, args->source, args->displayId, args->action, flags, keyCode,
-                     args->scanCode, metaState, repeatCount, args->downTime, args->eventTime);
+    event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
+                     args->action, flags, keyCode, args->scanCode, metaState, repeatCount,
+                     args->downTime, args->eventTime);
 
     android::base::Timer t;
     mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
@@ -2699,7 +3132,7 @@
         }
 
         KeyEntry* newEntry =
-                new KeyEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source,
+                new KeyEntry(args->id, args->eventTime, args->deviceId, args->source,
                              args->displayId, policyFlags, args->action, flags, keyCode,
                              args->scanCode, metaState, repeatCount, args->downTime);
 
@@ -2718,13 +3151,15 @@
 
 void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
 #if DEBUG_INBOUND_EVENT_DETAILS
-    ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
-          ", policyFlags=0x%x, "
-          "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x,"
-          "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
-          args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags,
-          args->action, args->actionButton, args->flags, args->metaState, args->buttonState,
-          args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime);
+    ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, "
+          "displayId=%" PRId32 ", policyFlags=0x%x, "
+          "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
+          "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
+          "yCursorPosition=%f, downTime=%" PRId64,
+          args->id, args->eventTime, args->deviceId, args->source, args->displayId,
+          args->policyFlags, args->action, args->actionButton, args->flags, args->metaState,
+          args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision,
+          args->xCursorPosition, args->yCursorPosition, args->downTime);
     for (uint32_t i = 0; i < args->pointerCount; i++) {
         ALOGD("  Pointer %d: id=%d, toolType=%d, "
               "x=%f, y=%f, pressure=%f, size=%f, "
@@ -2765,10 +3200,12 @@
             mLock.unlock();
 
             MotionEvent event;
-            event.initialize(args->deviceId, args->source, args->displayId, args->action,
-                             args->actionButton, args->flags, args->edgeFlags, args->metaState,
-                             args->buttonState, args->classification, 0, 0, args->xPrecision,
-                             args->yPrecision, args->downTime, args->eventTime, args->pointerCount,
+            event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
+                             args->action, args->actionButton, args->flags, args->edgeFlags,
+                             args->metaState, args->buttonState, args->classification, 1 /*xScale*/,
+                             1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision,
+                             args->yPrecision, args->xCursorPosition, args->yCursorPosition,
+                             args->downTime, args->eventTime, args->pointerCount,
                              args->pointerProperties, args->pointerCoords);
 
             policyFlags |= POLICY_FLAG_FILTERED;
@@ -2781,12 +3218,13 @@
 
         // Just enqueue a new motion event.
         MotionEntry* newEntry =
-                new MotionEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source,
+                new MotionEntry(args->id, args->eventTime, args->deviceId, args->source,
                                 args->displayId, policyFlags, args->action, args->actionButton,
                                 args->flags, args->metaState, args->buttonState,
                                 args->classification, args->edgeFlags, args->xPrecision,
-                                args->yPrecision, args->downTime, args->pointerCount,
-                                args->pointerProperties, args->pointerCoords, 0, 0);
+                                args->yPrecision, args->xCursorPosition, args->yCursorPosition,
+                                args->downTime, args->pointerCount, args->pointerProperties,
+                                args->pointerCoords, 0, 0);
 
         needWake = enqueueInboundEventLocked(newEntry);
         mLock.unlock();
@@ -2824,7 +3262,7 @@
         std::scoped_lock _l(mLock);
 
         DeviceResetEntry* newEntry =
-                new DeviceResetEntry(args->sequenceNum, args->eventTime, args->deviceId);
+                new DeviceResetEntry(args->id, args->eventTime, args->deviceId);
         needWake = enqueueInboundEventLocked(newEntry);
     } // release lock
 
@@ -2835,40 +3273,39 @@
 
 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)) {
         policyFlags |= POLICY_FLAG_TRUSTED;
     }
 
-    EventEntry* firstInjectedEntry;
-    EventEntry* lastInjectedEntry;
+    std::queue<EventEntry*> injectedEntries;
     switch (event->getType()) {
         case AINPUT_EVENT_TYPE_KEY: {
-            KeyEvent keyEvent;
-            keyEvent.initialize(*static_cast<const KeyEvent*>(event));
-            int32_t action = keyEvent.getAction();
+            const KeyEvent& incomingKey = static_cast<const KeyEvent&>(*event);
+            int32_t action = incomingKey.getAction();
             if (!validateKeyEvent(action)) {
                 return INPUT_EVENT_INJECTION_FAILED;
             }
 
-            int32_t flags = keyEvent.getFlags();
-            int32_t keyCode = keyEvent.getKeyCode();
-            int32_t metaState = keyEvent.getMetaState();
-            accelerateMetaShortcuts(keyEvent.getDeviceId(), action,
+            int32_t flags = incomingKey.getFlags();
+            int32_t keyCode = incomingKey.getKeyCode();
+            int32_t metaState = incomingKey.getMetaState();
+            accelerateMetaShortcuts(VIRTUAL_KEYBOARD_ID, action,
                                     /*byref*/ keyCode, /*byref*/ metaState);
-            keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(),
-                                keyEvent.getDisplayId(), action, flags, keyCode,
-                                keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(),
-                                keyEvent.getDownTime(), keyEvent.getEventTime());
+            KeyEvent keyEvent;
+            keyEvent.initialize(incomingKey.getId(), VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
+                                incomingKey.getDisplayId(), INVALID_HMAC, action, flags, keyCode,
+                                incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(),
+                                incomingKey.getDownTime(), incomingKey.getEventTime());
 
             if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) {
                 policyFlags |= POLICY_FLAG_VIRTUAL;
@@ -2884,13 +3321,13 @@
             }
 
             mLock.lock();
-            firstInjectedEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM,
-                                              keyEvent.getEventTime(), keyEvent.getDeviceId(),
-                                              keyEvent.getSource(), keyEvent.getDisplayId(),
-                                              policyFlags, action, flags, keyEvent.getKeyCode(),
-                                              keyEvent.getScanCode(), keyEvent.getMetaState(),
-                                              keyEvent.getRepeatCount(), keyEvent.getDownTime());
-            lastInjectedEntry = firstInjectedEntry;
+            KeyEntry* injectedEntry =
+                    new KeyEntry(incomingKey.getId(), incomingKey.getEventTime(),
+                                 VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
+                                 incomingKey.getDisplayId(), policyFlags, action, flags, keyCode,
+                                 incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(),
+                                 incomingKey.getDownTime());
+            injectedEntries.push(injectedEntry);
             break;
         }
 
@@ -2918,40 +3355,43 @@
             mLock.lock();
             const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
             const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
-            firstInjectedEntry =
-                    new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
-                                    motionEvent->getDeviceId(), motionEvent->getSource(),
-                                    motionEvent->getDisplayId(), policyFlags, action, actionButton,
-                                    motionEvent->getFlags(), motionEvent->getMetaState(),
-                                    motionEvent->getButtonState(), motionEvent->getClassification(),
-                                    motionEvent->getEdgeFlags(), motionEvent->getXPrecision(),
-                                    motionEvent->getYPrecision(), motionEvent->getDownTime(),
-                                    uint32_t(pointerCount), pointerProperties, samplePointerCoords,
+            MotionEntry* injectedEntry =
+                    new MotionEntry(motionEvent->getId(), *sampleEventTimes, VIRTUAL_KEYBOARD_ID,
+                                    motionEvent->getSource(), motionEvent->getDisplayId(),
+                                    policyFlags, action, actionButton, motionEvent->getFlags(),
+                                    motionEvent->getMetaState(), motionEvent->getButtonState(),
+                                    motionEvent->getClassification(), motionEvent->getEdgeFlags(),
+                                    motionEvent->getXPrecision(), motionEvent->getYPrecision(),
+                                    motionEvent->getRawXCursorPosition(),
+                                    motionEvent->getRawYCursorPosition(),
+                                    motionEvent->getDownTime(), uint32_t(pointerCount),
+                                    pointerProperties, samplePointerCoords,
                                     motionEvent->getXOffset(), motionEvent->getYOffset());
-            lastInjectedEntry = firstInjectedEntry;
+            injectedEntries.push(injectedEntry);
             for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
                 sampleEventTimes += 1;
                 samplePointerCoords += pointerCount;
                 MotionEntry* nextInjectedEntry =
-                        new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
-                                        motionEvent->getDeviceId(), motionEvent->getSource(),
+                        new MotionEntry(motionEvent->getId(), *sampleEventTimes,
+                                        VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
                                         motionEvent->getDisplayId(), policyFlags, action,
                                         actionButton, motionEvent->getFlags(),
                                         motionEvent->getMetaState(), motionEvent->getButtonState(),
                                         motionEvent->getClassification(),
                                         motionEvent->getEdgeFlags(), motionEvent->getXPrecision(),
-                                        motionEvent->getYPrecision(), motionEvent->getDownTime(),
-                                        uint32_t(pointerCount), pointerProperties,
-                                        samplePointerCoords, motionEvent->getXOffset(),
-                                        motionEvent->getYOffset());
-                lastInjectedEntry->next = nextInjectedEntry;
-                lastInjectedEntry = nextInjectedEntry;
+                                        motionEvent->getYPrecision(),
+                                        motionEvent->getRawXCursorPosition(),
+                                        motionEvent->getRawYCursorPosition(),
+                                        motionEvent->getDownTime(), uint32_t(pointerCount),
+                                        pointerProperties, samplePointerCoords,
+                                        motionEvent->getXOffset(), motionEvent->getYOffset());
+                injectedEntries.push(nextInjectedEntry);
             }
             break;
         }
 
         default:
-            ALOGW("Cannot inject event of type %d", event->getType());
+            ALOGW("Cannot inject %s events", inputEventTypeToString(event->getType()));
             return INPUT_EVENT_INJECTION_FAILED;
     }
 
@@ -2961,13 +3401,12 @@
     }
 
     injectionState->refCount += 1;
-    lastInjectedEntry->injectionState = injectionState;
+    injectedEntries.back()->injectionState = injectionState;
 
     bool needWake = false;
-    for (EventEntry* entry = firstInjectedEntry; entry != nullptr;) {
-        EventEntry* nextEntry = entry->next;
-        needWake |= enqueueInboundEventLocked(entry);
-        entry = nextEntry;
+    while (!injectedEntries.empty()) {
+        needWake |= enqueueInboundEventLocked(injectedEntries.front());
+        injectedEntries.pop();
     }
 
     mLock.unlock();
@@ -3028,14 +3467,46 @@
     } // 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
 
     return injectionResult;
 }
 
+std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const InputEvent& event) {
+    std::array<uint8_t, 32> calculatedHmac;
+    std::unique_ptr<VerifiedInputEvent> result;
+    switch (event.getType()) {
+        case AINPUT_EVENT_TYPE_KEY: {
+            const KeyEvent& keyEvent = static_cast<const KeyEvent&>(event);
+            VerifiedKeyEvent verifiedKeyEvent = verifiedKeyEventFromKeyEvent(keyEvent);
+            result = std::make_unique<VerifiedKeyEvent>(verifiedKeyEvent);
+            calculatedHmac = mHmacKeyManager.sign(verifiedKeyEvent);
+            break;
+        }
+        case AINPUT_EVENT_TYPE_MOTION: {
+            const MotionEvent& motionEvent = static_cast<const MotionEvent&>(event);
+            VerifiedMotionEvent verifiedMotionEvent =
+                    verifiedMotionEventFromMotionEvent(motionEvent);
+            result = std::make_unique<VerifiedMotionEvent>(verifiedMotionEvent);
+            calculatedHmac = mHmacKeyManager.sign(verifiedMotionEvent);
+            break;
+        }
+        default: {
+            ALOGE("Cannot verify events of type %" PRId32, event.getType());
+            return nullptr;
+        }
+    }
+    if (calculatedHmac == INVALID_HMAC) {
+        return nullptr;
+    }
+    if (calculatedHmac != event.getHmac()) {
+        return nullptr;
+    }
+    return result;
+}
+
 bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) {
     return injectorUid == 0 ||
             mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
@@ -3093,18 +3564,15 @@
 
 std::vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(
         int32_t displayId) const {
-    std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>::const_iterator it =
-            mWindowHandlesByDisplay.find(displayId);
-    if (it != mWindowHandlesByDisplay.end()) {
-        return it->second;
-    }
-
-    // Return an empty one if nothing found.
-    return std::vector<sp<InputWindowHandle>>();
+    return getValueByKey(mWindowHandlesByDisplay, displayId);
 }
 
 sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
         const sp<IBinder>& windowHandleToken) const {
+    if (windowHandleToken == nullptr) {
+        return nullptr;
+    }
+
     for (auto& it : mWindowHandlesByDisplay) {
         const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
         for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
@@ -3120,7 +3588,8 @@
     for (auto& it : mWindowHandlesByDisplay) {
         const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
         for (const sp<InputWindowHandle>& handle : windowHandles) {
-            if (handle->getToken() == windowHandle->getToken()) {
+            if (handle->getId() == windowHandle->getId() &&
+                handle->getToken() == windowHandle->getToken()) {
                 if (windowHandle->getInfo()->displayId != it.first) {
                     ALOGE("Found window %s in display %" PRId32
                           ", but it should belong to display %" PRId32,
@@ -3142,6 +3611,76 @@
     return mInputChannelsByToken.at(token);
 }
 
+void InputDispatcher::updateWindowHandlesForDisplayLocked(
+        const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) {
+    if (inputWindowHandles.empty()) {
+        // Remove all handles on a display if there are no windows left.
+        mWindowHandlesByDisplay.erase(displayId);
+        return;
+    }
+
+    // Since we compare the pointer of input window handles across window updates, we need
+    // to make sure the handle object for the same window stays unchanged across updates.
+    const std::vector<sp<InputWindowHandle>>& oldHandles = getWindowHandlesLocked(displayId);
+    std::unordered_map<int32_t /*id*/, sp<InputWindowHandle>> oldHandlesById;
+    for (const sp<InputWindowHandle>& handle : oldHandles) {
+        oldHandlesById[handle->getId()] = handle;
+    }
+
+    std::vector<sp<InputWindowHandle>> newHandles;
+    for (const sp<InputWindowHandle>& handle : inputWindowHandles) {
+        if (!handle->updateInfo()) {
+            // handle no longer valid
+            continue;
+        }
+
+        const InputWindowInfo* info = handle->getInfo();
+        if ((getInputChannelLocked(handle->getToken()) == nullptr &&
+             info->portalToDisplayId == ADISPLAY_ID_NONE)) {
+            const bool noInputChannel =
+                    info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL;
+            const bool canReceiveInput =
+                    !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) ||
+                    !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE);
+            if (canReceiveInput && !noInputChannel) {
+                ALOGV("Window handle %s has no registered input channel",
+                      handle->getName().c_str());
+                continue;
+            }
+        }
+
+        if (info->displayId != displayId) {
+            ALOGE("Window %s updated by wrong display %d, should belong to display %d",
+                  handle->getName().c_str(), displayId, info->displayId);
+            continue;
+        }
+
+        if ((oldHandlesById.find(handle->getId()) != oldHandlesById.end()) &&
+                (oldHandlesById.at(handle->getId())->getToken() == handle->getToken())) {
+            const sp<InputWindowHandle>& oldHandle = oldHandlesById.at(handle->getId());
+            oldHandle->updateFrom(handle);
+            newHandles.push_back(oldHandle);
+        } else {
+            newHandles.push_back(handle);
+        }
+    }
+
+    // Insert or replace
+    mWindowHandlesByDisplay[displayId] = newHandles;
+}
+
+void InputDispatcher::setInputWindows(
+        const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& handlesPerDisplay) {
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+        for (auto const& i : handlesPerDisplay) {
+            setInputWindowsLocked(i.second, i.first);
+        }
+    }
+    // Wake up poll loop since it may need to make new input dispatching choices.
+    mLooper->wake();
+}
+
 /**
  * Called from InputManagerService, update window handle list by displayId that can receive input.
  * A window handle contains information about InputChannel, Touch Region, Types, Focused,...
@@ -3149,196 +3688,135 @@
  * For focused handle, check if need to change and send a cancel event to previous one.
  * For removed handle, check if need to send a cancel event if already in touch.
  */
-void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
-                                      int32_t displayId,
-                                      const sp<ISetInputWindowsListener>& setInputWindowsListener) {
-#if DEBUG_FOCUS
-    ALOGD("setInputWindows displayId=%" PRId32, displayId);
-#endif
-    { // acquire lock
-        std::scoped_lock _l(mLock);
-
-        // Copy old handles for release if they are no longer present.
-        const std::vector<sp<InputWindowHandle>> oldWindowHandles =
-                getWindowHandlesLocked(displayId);
-
-        sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
-        bool foundHoveredWindow = false;
-
-        if (inputWindowHandles.empty()) {
-            // Remove all handles on a display if there are no windows left.
-            mWindowHandlesByDisplay.erase(displayId);
-        } else {
-            // Since we compare the pointer of input window handles across window updates, we need
-            // to make sure the handle object for the same window stays unchanged across updates.
-            const std::vector<sp<InputWindowHandle>>& oldHandles =
-                    mWindowHandlesByDisplay[displayId];
-            std::unordered_map<sp<IBinder>, sp<InputWindowHandle>, IBinderHash> oldHandlesByTokens;
-            for (const sp<InputWindowHandle>& handle : oldHandles) {
-                oldHandlesByTokens[handle->getToken()] = handle;
-            }
-
-            std::vector<sp<InputWindowHandle>> newHandles;
-            for (const sp<InputWindowHandle>& handle : inputWindowHandles) {
-                if (!handle->updateInfo()) {
-                    // handle no longer valid
-                    continue;
-                }
-                const InputWindowInfo* info = handle->getInfo();
-
-                if ((getInputChannelLocked(handle->getToken()) == nullptr &&
-                     info->portalToDisplayId == ADISPLAY_ID_NONE)) {
-                    const bool noInputChannel =
-                            info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL;
-                    const bool canReceiveInput =
-                            !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) ||
-                            !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE);
-                    if (canReceiveInput && !noInputChannel) {
-                        ALOGE("Window handle %s has no registered input channel",
-                              handle->getName().c_str());
-                    }
-                    continue;
-                }
-
-                if (info->displayId != displayId) {
-                    ALOGE("Window %s updated by wrong display %d, should belong to display %d",
-                          handle->getName().c_str(), displayId, info->displayId);
-                    continue;
-                }
-
-                if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) {
-                    const sp<InputWindowHandle> oldHandle =
-                            oldHandlesByTokens.at(handle->getToken());
-                    oldHandle->updateFrom(handle);
-                    newHandles.push_back(oldHandle);
-                } else {
-                    newHandles.push_back(handle);
-                }
-            }
-
-            for (const sp<InputWindowHandle>& windowHandle : newHandles) {
-                // Set newFocusedWindowHandle to the top most focused window instead of the last one
-                if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus &&
-                    windowHandle->getInfo()->visible) {
-                    newFocusedWindowHandle = windowHandle;
-                }
-                if (windowHandle == mLastHoverWindowHandle) {
-                    foundHoveredWindow = true;
-                }
-            }
-
-            // Insert or replace
-            mWindowHandlesByDisplay[displayId] = newHandles;
+void InputDispatcher::setInputWindowsLocked(
+        const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) {
+    if (DEBUG_FOCUS) {
+        std::string windowList;
+        for (const sp<InputWindowHandle>& iwh : inputWindowHandles) {
+            windowList += iwh->getName() + " ";
         }
+        ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
+    }
 
-        if (!foundHoveredWindow) {
-            mLastHoverWindowHandle = nullptr;
+    // Copy old handles for release if they are no longer present.
+    const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
+
+    updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);
+
+    sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
+    bool foundHoveredWindow = false;
+    for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
+        // Set newFocusedWindowHandle to the top most focused window instead of the last one
+        if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus &&
+            windowHandle->getInfo()->visible) {
+            newFocusedWindowHandle = windowHandle;
         }
+        if (windowHandle == mLastHoverWindowHandle) {
+            foundHoveredWindow = true;
+        }
+    }
 
-        sp<InputWindowHandle> oldFocusedWindowHandle =
-                getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+    if (!foundHoveredWindow) {
+        mLastHoverWindowHandle = nullptr;
+    }
 
-        if (oldFocusedWindowHandle != newFocusedWindowHandle) {
-            if (oldFocusedWindowHandle != nullptr) {
-#if DEBUG_FOCUS
+    sp<InputWindowHandle> oldFocusedWindowHandle =
+            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+
+    if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
+        if (oldFocusedWindowHandle != nullptr) {
+            if (DEBUG_FOCUS) {
                 ALOGD("Focus left window: %s in display %" PRId32,
                       oldFocusedWindowHandle->getName().c_str(), displayId);
-#endif
-                sp<InputChannel> focusedInputChannel =
-                        getInputChannelLocked(oldFocusedWindowHandle->getToken());
-                if (focusedInputChannel != nullptr) {
-                    CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
-                                               "focus left window");
-                    synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
-                }
-                mFocusedWindowHandlesByDisplay.erase(displayId);
             }
-            if (newFocusedWindowHandle != nullptr) {
-#if DEBUG_FOCUS
+            sp<InputChannel> focusedInputChannel =
+                    getInputChannelLocked(oldFocusedWindowHandle->getToken());
+            if (focusedInputChannel != nullptr) {
+                CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
+                                           "focus left window");
+                synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
+                enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
+            }
+            mFocusedWindowHandlesByDisplay.erase(displayId);
+        }
+        if (newFocusedWindowHandle != nullptr) {
+            if (DEBUG_FOCUS) {
                 ALOGD("Focus entered window: %s in display %" PRId32,
                       newFocusedWindowHandle->getName().c_str(), displayId);
-#endif
-                mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
             }
-
-            if (mFocusedDisplayId == displayId) {
-                onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
-            }
+            mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
+            enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/);
         }
 
-        ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
-        if (stateIndex >= 0) {
-            TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
-            for (size_t i = 0; i < state.windows.size();) {
-                TouchedWindow& touchedWindow = state.windows[i];
-                if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
-#if DEBUG_FOCUS
+        if (mFocusedDisplayId == displayId) {
+            onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+        }
+    }
+
+    std::unordered_map<int32_t, TouchState>::iterator stateIt =
+            mTouchStatesByDisplay.find(displayId);
+    if (stateIt != mTouchStatesByDisplay.end()) {
+        TouchState& state = stateIt->second;
+        for (size_t i = 0; i < state.windows.size();) {
+            TouchedWindow& touchedWindow = state.windows[i];
+            if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
+                if (DEBUG_FOCUS) {
                     ALOGD("Touched window was removed: %s in display %" PRId32,
                           touchedWindow.windowHandle->getName().c_str(), displayId);
-#endif
-                    sp<InputChannel> touchedInputChannel =
-                            getInputChannelLocked(touchedWindow.windowHandle->getToken());
-                    if (touchedInputChannel != nullptr) {
-                        CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
-                                                   "touched window was removed");
-                        synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel,
-                                                                         options);
-                    }
-                    state.windows.erase(state.windows.begin() + i);
-                } else {
-                    ++i;
                 }
+                sp<InputChannel> touchedInputChannel =
+                        getInputChannelLocked(touchedWindow.windowHandle->getToken());
+                if (touchedInputChannel != nullptr) {
+                    CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+                                               "touched window was removed");
+                    synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
+                }
+                state.windows.erase(state.windows.begin() + i);
+            } else {
+                ++i;
             }
         }
+    }
 
-        // Release information for windows that are no longer present.
-        // This ensures that unused input channels are released promptly.
-        // Otherwise, they might stick around until the window handle is destroyed
-        // which might not happen until the next GC.
-        for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
-            if (!hasWindowHandleLocked(oldWindowHandle)) {
-#if DEBUG_FOCUS
+    // Release information for windows that are no longer present.
+    // This ensures that unused input channels are released promptly.
+    // Otherwise, they might stick around until the window handle is destroyed
+    // which might not happen until the next GC.
+    for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
+        if (!hasWindowHandleLocked(oldWindowHandle)) {
+            if (DEBUG_FOCUS) {
                 ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
-#endif
-                oldWindowHandle->releaseChannel();
             }
+            oldWindowHandle->releaseChannel();
         }
-    } // release lock
-
-    // Wake up poll loop since it may need to make new input dispatching choices.
-    mLooper->wake();
-
-    if (setInputWindowsListener) {
-        setInputWindowsListener->onSetInputWindowsFinished();
     }
 }
 
 void InputDispatcher::setFocusedApplication(
         int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) {
-#if DEBUG_FOCUS
-    ALOGD("setFocusedApplication displayId=%" PRId32, displayId);
-#endif
+    if (DEBUG_FOCUS) {
+        ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId,
+              inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>");
+    }
     { // acquire lock
         std::scoped_lock _l(mLock);
 
         sp<InputApplicationHandle> oldFocusedApplicationHandle =
                 getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
+
+        if (oldFocusedApplicationHandle == mAwaitedFocusedApplication &&
+            inputApplicationHandle != oldFocusedApplicationHandle) {
+            resetNoFocusedWindowTimeoutLocked();
+        }
+
         if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) {
             if (oldFocusedApplicationHandle != inputApplicationHandle) {
-                if (oldFocusedApplicationHandle != nullptr) {
-                    resetANRTimeoutsLocked();
-                }
                 mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
             }
         } else if (oldFocusedApplicationHandle != nullptr) {
-            resetANRTimeoutsLocked();
             oldFocusedApplicationHandle.clear();
             mFocusedApplicationHandlesByDisplay.erase(displayId);
         }
-
-#if DEBUG_FOCUS
-        // logDispatchStateLocked();
-#endif
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
@@ -3355,9 +3833,9 @@
  * display. The display-specified events won't be affected.
  */
 void InputDispatcher::setFocusedDisplay(int32_t displayId) {
-#if DEBUG_FOCUS
-    ALOGD("setFocusedDisplay displayId=%" PRId32, displayId);
-#endif
+    if (DEBUG_FOCUS) {
+        ALOGD("setFocusedDisplay displayId=%" PRId32, displayId);
+    }
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -3396,9 +3874,9 @@
             }
         }
 
-#if DEBUG_FOCUS
-        logDispatchStateLocked();
-#endif
+        if (DEBUG_FOCUS) {
+            logDispatchStateLocked();
+        }
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
@@ -3406,9 +3884,9 @@
 }
 
 void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
-#if DEBUG_FOCUS
-    ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
-#endif
+    if (DEBUG_FOCUS) {
+        ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
+    }
 
     bool changed;
     { // acquire lock
@@ -3416,7 +3894,7 @@
 
         if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
             if (mDispatchFrozen && !frozen) {
-                resetANRTimeoutsLocked();
+                resetNoFocusedWindowTimeoutLocked();
             }
 
             if (mDispatchEnabled && !enabled) {
@@ -3430,9 +3908,9 @@
             changed = false;
         }
 
-#if DEBUG_FOCUS
-        logDispatchStateLocked();
-#endif
+        if (DEBUG_FOCUS) {
+            logDispatchStateLocked();
+        }
     } // release lock
 
     if (changed) {
@@ -3442,9 +3920,9 @@
 }
 
 void InputDispatcher::setInputFilterEnabled(bool enabled) {
-#if DEBUG_FOCUS
-    ALOGD("setInputFilterEnabled: enabled=%d", enabled);
-#endif
+    if (DEBUG_FOCUS) {
+        ALOGD("setInputFilterEnabled: enabled=%d", enabled);
+    }
 
     { // acquire lock
         std::scoped_lock _l(mLock);
@@ -3461,11 +3939,16 @@
     mLooper->wake();
 }
 
+void InputDispatcher::setInTouchMode(bool inTouchMode) {
+    std::scoped_lock lock(mLock);
+    mInTouchMode = inTouchMode;
+}
+
 bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
     if (fromToken == toToken) {
-#if DEBUG_FOCUS
-        ALOGD("Trivial transfer to same window.");
-#endif
+        if (DEBUG_FOCUS) {
+            ALOGD("Trivial transfer to same window.");
+        }
         return true;
     }
 
@@ -3478,20 +3961,20 @@
             ALOGW("Cannot transfer focus because from or to window not found.");
             return false;
         }
-#if DEBUG_FOCUS
-        ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
-              fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
-#endif
+        if (DEBUG_FOCUS) {
+            ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
+                  fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
+        }
         if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
-#if DEBUG_FOCUS
-            ALOGD("Cannot transfer focus because windows are on different displays.");
-#endif
+            if (DEBUG_FOCUS) {
+                ALOGD("Cannot transfer focus because windows are on different displays.");
+            }
             return false;
         }
 
         bool found = false;
-        for (size_t d = 0; d < mTouchStatesByDisplay.size(); d++) {
-            TouchState& state = mTouchStatesByDisplay.editValueAt(d);
+        for (std::pair<const int32_t, TouchState>& pair : mTouchStatesByDisplay) {
+            TouchState& state = pair.second;
             for (size_t i = 0; i < state.windows.size(); i++) {
                 const TouchedWindow& touchedWindow = state.windows[i];
                 if (touchedWindow.windowHandle == fromWindowHandle) {
@@ -3513,30 +3996,26 @@
     Found:
 
         if (!found) {
-#if DEBUG_FOCUS
-            ALOGD("Focus transfer failed because from window did not have focus.");
-#endif
+            if (DEBUG_FOCUS) {
+                ALOGD("Focus transfer failed because from window did not have focus.");
+            }
             return false;
         }
 
-        sp<InputChannel> fromChannel = getInputChannelLocked(fromToken);
-        sp<InputChannel> toChannel = getInputChannelLocked(toToken);
-        ssize_t fromConnectionIndex = getConnectionIndexLocked(fromChannel);
-        ssize_t toConnectionIndex = getConnectionIndexLocked(toChannel);
-        if (fromConnectionIndex >= 0 && toConnectionIndex >= 0) {
-            sp<Connection> fromConnection = mConnectionsByFd.valueAt(fromConnectionIndex);
-            sp<Connection> toConnection = mConnectionsByFd.valueAt(toConnectionIndex);
-
-            fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
+        sp<Connection> fromConnection = getConnectionLocked(fromToken);
+        sp<Connection> toConnection = getConnectionLocked(toToken);
+        if (fromConnection != nullptr && toConnection != nullptr) {
+            fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
             CancelationOptions
                     options(CancelationOptions::CANCEL_POINTER_EVENTS,
                             "transferring touch focus from this window to another window");
             synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
+            synthesizePointerDownEventsForConnectionLocked(toConnection);
         }
 
-#if DEBUG_FOCUS
-        logDispatchStateLocked();
-#endif
+        if (DEBUG_FOCUS) {
+            logDispatchStateLocked();
+        }
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
@@ -3545,9 +4024,9 @@
 }
 
 void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
-#if DEBUG_FOCUS
-    ALOGD("Resetting and dropping all events (%s).", reason);
-#endif
+    if (DEBUG_FOCUS) {
+        ALOGD("Resetting and dropping all events (%s).", reason);
+    }
 
     CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason);
     synthesizeCancelationEventsForAllConnectionsLocked(options);
@@ -3555,8 +4034,9 @@
     resetKeyRepeatLocked();
     releasePendingEventLocked();
     drainInboundQueueLocked();
-    resetANRTimeoutsLocked();
+    resetNoFocusedWindowTimeoutLocked();
 
+    mAnrTracker.clear();
     mTouchStatesByDisplay.clear();
     mLastHoverWindowHandle.clear();
     mReplacedKeys.clear();
@@ -3586,11 +4066,12 @@
             const int32_t displayId = it.first;
             const sp<InputApplicationHandle>& applicationHandle = it.second;
             dump += StringPrintf(INDENT2 "displayId=%" PRId32
-                                         ", name='%s', dispatchingTimeout=%0.3fms\n",
+                                         ", name='%s', dispatchingTimeout=%" PRId64 "ms\n",
                                  displayId, applicationHandle->getName().c_str(),
-                                 applicationHandle->getDispatchingTimeout(
-                                         DEFAULT_INPUT_DISPATCHING_TIMEOUT) /
-                                         1000000.0);
+                                 ns2ms(applicationHandle
+                                               ->getDispatchingTimeout(
+                                                       DEFAULT_INPUT_DISPATCHING_TIMEOUT)
+                                               .count()));
         }
     } else {
         dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
@@ -3608,10 +4089,10 @@
         dump += StringPrintf(INDENT "FocusedWindows: <none>\n");
     }
 
-    if (!mTouchStatesByDisplay.isEmpty()) {
+    if (!mTouchStatesByDisplay.empty()) {
         dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
-        for (size_t i = 0; i < mTouchStatesByDisplay.size(); i++) {
-            const TouchState& state = mTouchStatesByDisplay.valueAt(i);
+        for (const std::pair<int32_t, TouchState>& pair : mTouchStatesByDisplay) {
+            const TouchState& state = pair.second;
             dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n",
                                  state.displayId, toString(state.down), toString(state.split),
                                  state.deviceId, state.source);
@@ -3652,12 +4133,10 @@
 
                     dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
                                                  "portalToDisplayId=%d, paused=%s, hasFocus=%s, "
-                                                 "hasWallpaper=%s, "
-                                                 "visible=%s, canReceiveKeys=%s, flags=0x%08x, "
-                                                 "type=0x%08x, layer=%d, "
+                                                 "hasWallpaper=%s, visible=%s, canReceiveKeys=%s, "
+                                                 "flags=0x%08x, type=0x%08x, "
                                                  "frame=[%d,%d][%d,%d], globalScale=%f, "
-                                                 "windowScale=(%f,%f), "
-                                                 "touchableRegion=",
+                                                 "windowScale=(%f,%f), touchableRegion=",
                                          i, windowInfo->name.c_str(), windowInfo->displayId,
                                          windowInfo->portalToDisplayId,
                                          toString(windowInfo->paused),
@@ -3666,16 +4145,16 @@
                                          toString(windowInfo->visible),
                                          toString(windowInfo->canReceiveKeys),
                                          windowInfo->layoutParamsFlags,
-                                         windowInfo->layoutParamsType, windowInfo->layer,
-                                         windowInfo->frameLeft, windowInfo->frameTop,
-                                         windowInfo->frameRight, windowInfo->frameBottom,
-                                         windowInfo->globalScaleFactor, windowInfo->windowXScale,
-                                         windowInfo->windowYScale);
+                                         windowInfo->layoutParamsType, windowInfo->frameLeft,
+                                         windowInfo->frameTop, windowInfo->frameRight,
+                                         windowInfo->frameBottom, windowInfo->globalScaleFactor,
+                                         windowInfo->windowXScale, windowInfo->windowYScale);
                     dumpRegion(dump, windowInfo->touchableRegion);
                     dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
-                    dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
+                    dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64
+                                         "ms\n",
                                          windowInfo->ownerPid, windowInfo->ownerUid,
-                                         windowInfo->dispatchingTimeout / 1000000.0);
+                                         ns2ms(windowInfo->dispatchingTimeout));
                 }
             } else {
                 dump += INDENT2 "Windows: <none>\n";
@@ -3703,12 +4182,12 @@
     nsecs_t currentTime = now();
 
     // Dump recently dispatched or dropped events from oldest to newest.
-    if (!mRecentQueue.isEmpty()) {
-        dump += StringPrintf(INDENT "RecentQueue: length=%u\n", mRecentQueue.count());
-        for (EventEntry* entry = mRecentQueue.head; entry; entry = entry->next) {
+    if (!mRecentQueue.empty()) {
+        dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size());
+        for (EventEntry* entry : mRecentQueue) {
             dump += INDENT2;
             entry->appendDescription(dump);
-            dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
+            dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
         }
     } else {
         dump += INDENT "RecentQueue: <empty>\n";
@@ -3719,74 +4198,72 @@
         dump += INDENT "PendingEvent:\n";
         dump += INDENT2;
         mPendingEvent->appendDescription(dump);
-        dump += StringPrintf(", age=%0.1fms\n",
-                             (currentTime - mPendingEvent->eventTime) * 0.000001f);
+        dump += StringPrintf(", age=%" PRId64 "ms\n",
+                             ns2ms(currentTime - mPendingEvent->eventTime));
     } else {
         dump += INDENT "PendingEvent: <none>\n";
     }
 
     // Dump inbound events from oldest to newest.
-    if (!mInboundQueue.isEmpty()) {
-        dump += StringPrintf(INDENT "InboundQueue: length=%u\n", mInboundQueue.count());
-        for (EventEntry* entry = mInboundQueue.head; entry; entry = entry->next) {
+    if (!mInboundQueue.empty()) {
+        dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size());
+        for (EventEntry* entry : mInboundQueue) {
             dump += INDENT2;
             entry->appendDescription(dump);
-            dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
+            dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime));
         }
     } else {
         dump += INDENT "InboundQueue: <empty>\n";
     }
 
-    if (!mReplacedKeys.isEmpty()) {
+    if (!mReplacedKeys.empty()) {
         dump += INDENT "ReplacedKeys:\n";
-        for (size_t i = 0; i < mReplacedKeys.size(); i++) {
-            const KeyReplacement& replacement = mReplacedKeys.keyAt(i);
-            int32_t newKeyCode = mReplacedKeys.valueAt(i);
-            dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n", i,
+        for (const std::pair<KeyReplacement, int32_t>& pair : mReplacedKeys) {
+            const KeyReplacement& replacement = pair.first;
+            int32_t newKeyCode = pair.second;
+            dump += StringPrintf(INDENT2 "originalKeyCode=%d, deviceId=%d -> newKeyCode=%d\n",
                                  replacement.keyCode, replacement.deviceId, newKeyCode);
         }
     } else {
         dump += INDENT "ReplacedKeys: <empty>\n";
     }
 
-    if (!mConnectionsByFd.isEmpty()) {
+    if (!mConnectionsByFd.empty()) {
         dump += INDENT "Connections:\n";
-        for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
-            const sp<Connection>& connection = mConnectionsByFd.valueAt(i);
-            dump += StringPrintf(INDENT2 "%zu: channelName='%s', windowName='%s', "
-                                         "status=%s, monitor=%s, inputPublisherBlocked=%s\n",
-                                 i, connection->getInputChannelName().c_str(),
+        for (const auto& pair : mConnectionsByFd) {
+            const sp<Connection>& connection = pair.second;
+            dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', "
+                                         "status=%s, monitor=%s, responsive=%s\n",
+                                 pair.first, connection->getInputChannelName().c_str(),
                                  connection->getWindowName().c_str(), connection->getStatusLabel(),
-                                 toString(connection->monitor),
-                                 toString(connection->inputPublisherBlocked));
+                                 toString(connection->monitor), toString(connection->responsive));
 
-            if (!connection->outboundQueue.isEmpty()) {
-                dump += StringPrintf(INDENT3 "OutboundQueue: length=%u\n",
-                                     connection->outboundQueue.count());
-                for (DispatchEntry* entry = connection->outboundQueue.head; entry;
-                     entry = entry->next) {
+            if (!connection->outboundQueue.empty()) {
+                dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n",
+                                     connection->outboundQueue.size());
+                for (DispatchEntry* entry : connection->outboundQueue) {
                     dump.append(INDENT4);
                     entry->eventEntry->appendDescription(dump);
-                    dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n",
+                    dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64
+                                         "ms\n",
                                          entry->targetFlags, entry->resolvedAction,
-                                         (currentTime - entry->eventEntry->eventTime) * 0.000001f);
+                                         ns2ms(currentTime - entry->eventEntry->eventTime));
                 }
             } else {
                 dump += INDENT3 "OutboundQueue: <empty>\n";
             }
 
-            if (!connection->waitQueue.isEmpty()) {
-                dump += StringPrintf(INDENT3 "WaitQueue: length=%u\n",
-                                     connection->waitQueue.count());
-                for (DispatchEntry* entry = connection->waitQueue.head; entry;
-                     entry = entry->next) {
+            if (!connection->waitQueue.empty()) {
+                dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n",
+                                     connection->waitQueue.size());
+                for (DispatchEntry* entry : connection->waitQueue) {
                     dump += INDENT4;
                     entry->eventEntry->appendDescription(dump);
                     dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, "
-                                         "age=%0.1fms, wait=%0.1fms\n",
+                                         "age=%" PRId64 "ms, wait=%" PRId64 "ms\n",
                                          entry->targetFlags, entry->resolvedAction,
-                                         (currentTime - entry->eventEntry->eventTime) * 0.000001f,
-                                         (currentTime - entry->deliveryTime) * 0.000001f);
+                                         ns2ms(currentTime - entry->eventEntry->eventTime),
+                                         ns2ms(currentTime - entry->deliveryTime));
                 }
             } else {
                 dump += INDENT3 "WaitQueue: <empty>\n";
@@ -3797,16 +4274,16 @@
     }
 
     if (isAppSwitchPendingLocked()) {
-        dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n",
-                             (mAppSwitchDueTime - now()) / 1000000.0);
+        dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n",
+                             ns2ms(mAppSwitchDueTime - now()));
     } else {
         dump += INDENT "AppSwitch: not pending\n";
     }
 
     dump += INDENT "Configuration:\n";
-    dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f);
-    dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n",
-                         mConfig.keyRepeatTimeout * 0.000001f);
+    dump += StringPrintf(INDENT2 "KeyRepeatDelay: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatDelay));
+    dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n",
+                         ns2ms(mConfig.keyRepeatTimeout));
 }
 
 void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) {
@@ -3819,27 +4296,25 @@
     }
 }
 
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
-                                               int32_t displayId) {
+status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
 #if DEBUG_REGISTRATION
-    ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32,
-          inputChannel->getName().c_str(), displayId);
+    ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
 #endif
 
     { // acquire lock
         std::scoped_lock _l(mLock);
-
-        if (getConnectionIndexLocked(inputChannel) >= 0) {
+        sp<Connection> existingConnection = getConnectionLocked(inputChannel->getConnectionToken());
+        if (existingConnection != nullptr) {
             ALOGW("Attempted to register already registered input channel '%s'",
                   inputChannel->getName().c_str());
             return BAD_VALUE;
         }
 
-        sp<Connection> connection = new Connection(inputChannel, false /*monitor*/);
+        sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);
 
         int fd = inputChannel->getFd();
-        mConnectionsByFd.add(fd, connection);
-        mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
+        mConnectionsByFd[fd] = connection;
+        mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
 
         mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
     } // release lock
@@ -3859,16 +4334,16 @@
             return BAD_VALUE;
         }
 
-        if (inputChannel->getToken() == nullptr) {
+        if (inputChannel->getConnectionToken() == nullptr) {
             ALOGW("Attempted to register input monitor without an identifying token.");
             return BAD_VALUE;
         }
 
-        sp<Connection> connection = new Connection(inputChannel, true /*monitor*/);
+        sp<Connection> connection = new Connection(inputChannel, true /*monitor*/, mIdGenerator);
 
         const int fd = inputChannel->getFd();
-        mConnectionsByFd.add(fd, connection);
-        mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
+        mConnectionsByFd[fd] = connection;
+        mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
 
         auto& monitorsByDisplay =
                 isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
@@ -3903,17 +4378,15 @@
 
 status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
                                                        bool notify) {
-    ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
-    if (connectionIndex < 0) {
+    sp<Connection> connection = getConnectionLocked(inputChannel->getConnectionToken());
+    if (connection == nullptr) {
         ALOGW("Attempted to unregister already unregistered input channel '%s'",
               inputChannel->getName().c_str());
         return BAD_VALUE;
     }
 
-    sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
-    mConnectionsByFd.removeItemsAt(connectionIndex);
-
-    mInputChannelsByToken.erase(inputChannel->getToken());
+    removeConnectionLocked(connection);
+    mInputChannelsByToken.erase(inputChannel->getConnectionToken());
 
     if (connection->monitor) {
         removeMonitorChannelLocked(inputChannel);
@@ -3964,16 +4437,17 @@
         }
         int32_t displayId = foundDisplayId.value();
 
-        ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId);
-        if (stateIndex < 0) {
+        std::unordered_map<int32_t, TouchState>::iterator stateIt =
+                mTouchStatesByDisplay.find(displayId);
+        if (stateIt == mTouchStatesByDisplay.end()) {
             ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId);
             return BAD_VALUE;
         }
 
-        TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
+        TouchState& state = stateIt->second;
         std::optional<int32_t> foundDeviceId;
         for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) {
-            if (touchedMonitor.monitor.inputChannel->getToken() == token) {
+            if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) {
                 foundDeviceId = state.deviceId;
             }
         }
@@ -3991,7 +4465,9 @@
         options.displayId = displayId;
         for (const TouchedWindow& window : state.windows) {
             sp<InputChannel> channel = getInputChannelLocked(window.windowHandle->getToken());
-            synthesizeCancelationEventsForInputChannelLocked(channel, options);
+            if (channel != nullptr) {
+                synthesizeCancelationEventsForInputChannelLocked(channel, options);
+            }
         }
         // Then clear the current touch state so we stop dispatching to them as well.
         state.filterNonMonitors();
@@ -4004,7 +4480,7 @@
     for (const auto& it : mGestureMonitorsByDisplay) {
         const std::vector<Monitor>& monitors = it.second;
         for (const Monitor& monitor : monitors) {
-            if (monitor.inputChannel->getToken() == token) {
+            if (monitor.inputChannel->getConnectionToken() == token) {
                 return it.first;
             }
         }
@@ -4012,30 +4488,36 @@
     return std::nullopt;
 }
 
-ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
-    if (inputChannel == nullptr) {
-        return -1;
+sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const {
+    if (inputConnectionToken == nullptr) {
+        return nullptr;
     }
 
-    for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
-        sp<Connection> connection = mConnectionsByFd.valueAt(i);
-        if (connection->inputChannel->getToken() == inputChannel->getToken()) {
-            return i;
+    for (const auto& pair : mConnectionsByFd) {
+        const sp<Connection>& connection = pair.second;
+        if (connection->inputChannel->getConnectionToken() == inputConnectionToken) {
+            return connection;
         }
     }
 
-    return -1;
+    return nullptr;
+}
+
+void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) {
+    mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken());
+    removeByValue(mConnectionsByFd, connection);
 }
 
 void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
                                                     const sp<Connection>& connection, uint32_t seq,
                                                     bool handled) {
-    CommandEntry* commandEntry =
-            postCommandLocked(&InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
     commandEntry->connection = connection;
     commandEntry->eventTime = currentTime;
     commandEntry->seq = seq;
     commandEntry->handled = handled;
+    postCommandLocked(std::move(commandEntry));
 }
 
 void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime,
@@ -4043,55 +4525,98 @@
     ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
           connection->getInputChannelName().c_str());
 
-    CommandEntry* commandEntry =
-            postCommandLocked(&InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
     commandEntry->connection = connection;
+    postCommandLocked(std::move(commandEntry));
 }
 
 void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
                                            const sp<InputWindowHandle>& newFocus) {
     sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr;
     sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr;
-    CommandEntry* commandEntry =
-            postCommandLocked(&InputDispatcher::doNotifyFocusChangedLockedInterruptible);
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doNotifyFocusChangedLockedInterruptible);
     commandEntry->oldToken = oldToken;
     commandEntry->newToken = newToken;
+    postCommandLocked(std::move(commandEntry));
 }
 
-void InputDispatcher::onANRLocked(nsecs_t currentTime,
-                                  const sp<InputApplicationHandle>& applicationHandle,
-                                  const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
-                                  nsecs_t waitStartTime, const char* reason) {
-    float dispatchLatency = (currentTime - eventTime) * 0.000001f;
-    float waitDuration = (currentTime - waitStartTime) * 0.000001f;
-    ALOGI("Application is not responding: %s.  "
-          "It has been %0.1fms since event, %0.1fms since wait started.  Reason: %s",
-          getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency,
-          waitDuration, reason);
+void InputDispatcher::onAnrLocked(const sp<Connection>& connection) {
+    // Since we are allowing the policy to extend the timeout, maybe the waitQueue
+    // is already healthy again. Don't raise ANR in this situation
+    if (connection->waitQueue.empty()) {
+        ALOGI("Not raising ANR because the connection %s has recovered",
+              connection->inputChannel->getName().c_str());
+        return;
+    }
+    /**
+     * The "oldestEntry" is the entry that was first sent to the application. That entry, however,
+     * may not be the one that caused the timeout to occur. One possibility is that window timeout
+     * has changed. This could cause newer entries to time out before the already dispatched
+     * entries. In that situation, the newest entries caused ANR. But in all likelihood, the app
+     * processes the events linearly. So providing information about the oldest entry seems to be
+     * most useful.
+     */
+    DispatchEntry* oldestEntry = *connection->waitQueue.begin();
+    const nsecs_t currentWait = now() - oldestEntry->deliveryTime;
+    std::string reason =
+            android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s",
+                                        connection->inputChannel->getName().c_str(),
+                                        ns2ms(currentWait),
+                                        oldestEntry->eventEntry->getDescription().c_str());
 
+    updateLastAnrStateLocked(getWindowHandleLocked(connection->inputChannel->getConnectionToken()),
+                             reason);
+
+    std::unique_ptr<CommandEntry> commandEntry =
+            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
+    commandEntry->inputApplicationHandle = nullptr;
+    commandEntry->inputChannel = connection->inputChannel;
+    commandEntry->reason = std::move(reason);
+    postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::onAnrLocked(const sp<InputApplicationHandle>& application) {
+    std::string reason = android::base::StringPrintf("%s does not have a focused window",
+                                                     application->getName().c_str());
+
+    updateLastAnrStateLocked(application, reason);
+
+    std::unique_ptr<CommandEntry> commandEntry =
+            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyAnrLockedInterruptible);
+    commandEntry->inputApplicationHandle = application;
+    commandEntry->inputChannel = nullptr;
+    commandEntry->reason = std::move(reason);
+    postCommandLocked(std::move(commandEntry));
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const sp<InputWindowHandle>& window,
+                                               const std::string& reason) {
+    const std::string windowLabel = getApplicationWindowLabel(nullptr, window);
+    updateLastAnrStateLocked(windowLabel, reason);
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const sp<InputApplicationHandle>& application,
+                                               const std::string& reason) {
+    const std::string windowLabel = getApplicationWindowLabel(application, nullptr);
+    updateLastAnrStateLocked(windowLabel, reason);
+}
+
+void InputDispatcher::updateLastAnrStateLocked(const std::string& windowLabel,
+                                               const std::string& reason) {
     // Capture a record of the InputDispatcher state at the time of the ANR.
     time_t t = time(nullptr);
     struct tm tm;
     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 +=
-            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);
-
-    CommandEntry* commandEntry =
-            postCommandLocked(&InputDispatcher::doNotifyANRLockedInterruptible);
-    commandEntry->inputApplicationHandle = applicationHandle;
-    commandEntry->inputChannel =
-            windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr;
-    commandEntry->reason = reason;
+    mLastAnrState.clear();
+    mLastAnrState += INDENT "ANR:\n";
+    mLastAnrState += StringPrintf(INDENT2 "Time: %s\n", timestr);
+    mLastAnrState += StringPrintf(INDENT2 "Reason: %s\n", reason.c_str());
+    mLastAnrState += StringPrintf(INDENT2 "Window: %s\n", windowLabel.c_str());
+    dumpDispatchStateLocked(mLastAnrState);
 }
 
 void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) {
@@ -4108,7 +4633,7 @@
     if (connection->status != Connection::STATUS_ZOMBIE) {
         mLock.unlock();
 
-        mPolicy->notifyInputChannelBroken(connection->inputChannel->getToken());
+        mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
 
         mLock.lock();
     }
@@ -4122,32 +4647,67 @@
     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,
-                               commandEntry->inputChannel ? commandEntry->inputChannel->getToken()
-                                                          : nullptr,
-                               commandEntry->reason);
+    const nsecs_t timeoutExtension =
+            mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason);
 
     mLock.lock();
 
-    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel);
+    if (timeoutExtension > 0) {
+        extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension);
+    } else {
+        // stop waking up for events in this connection, it is already not responding
+        sp<Connection> connection = getConnectionLocked(token);
+        if (connection == nullptr) {
+            return;
+        }
+        cancelEventsForAnrLocked(connection);
+    }
+}
+
+void InputDispatcher::extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
+                                              const sp<IBinder>& connectionToken,
+                                              nsecs_t timeoutExtension) {
+    sp<Connection> connection = getConnectionLocked(connectionToken);
+    if (connection == nullptr) {
+        if (mNoFocusedWindowTimeoutTime.has_value() && application != nullptr) {
+            // Maybe ANR happened because there's no focused window?
+            mNoFocusedWindowTimeoutTime = now() + timeoutExtension;
+            mAwaitedFocusedApplication = application;
+        } else {
+            // It's also possible that the connection already disappeared. No action necessary.
+        }
+        return;
+    }
+
+    ALOGI("Raised ANR, but the policy wants to keep waiting on %s for %" PRId64 "ms longer",
+          connection->inputChannel->getName().c_str(), ns2ms(timeoutExtension));
+
+    connection->responsive = true;
+    const nsecs_t newTimeout = now() + timeoutExtension;
+    for (DispatchEntry* entry : connection->waitQueue) {
+        if (newTimeout >= entry->timeoutTime) {
+            // Already removed old entries when connection was marked unresponsive
+            entry->timeoutTime = newTimeout;
+            mAnrTracker.insert(entry->timeoutTime, connectionToken);
+        }
+    }
 }
 
 void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
         CommandEntry* commandEntry) {
     KeyEntry* entry = commandEntry->keyEntry;
-
-    KeyEvent event;
-    initializeKeyEvent(&event, entry);
+    KeyEvent event = createKeyEvent(*entry);
 
     mLock.unlock();
 
     android::base::Timer t;
     sp<IBinder> token = commandEntry->inputChannel != nullptr
-            ? commandEntry->inputChannel->getToken()
+            ? commandEntry->inputChannel->getConnectionToken()
             : nullptr;
     nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags);
     if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
@@ -4174,55 +4734,76 @@
     mLock.lock();
 }
 
+/**
+ * Connection is responsive if it has no events in the waitQueue that are older than the
+ * current time.
+ */
+static bool isConnectionResponsive(const Connection& connection) {
+    const nsecs_t currentTime = now();
+    for (const DispatchEntry* entry : connection.waitQueue) {
+        if (entry->timeoutTime < currentTime) {
+            return false;
+        }
+    }
+    return true;
+}
+
 void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
     sp<Connection> connection = commandEntry->connection;
-    nsecs_t finishTime = commandEntry->eventTime;
+    const nsecs_t finishTime = commandEntry->eventTime;
     uint32_t seq = commandEntry->seq;
-    bool handled = commandEntry->handled;
+    const bool handled = commandEntry->handled;
 
     // Handle post-event policy actions.
-    DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);
-    if (dispatchEntry) {
-        nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
-        if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
-            std::string msg =
-                    StringPrintf("Window '%s' spent %0.1fms processing the last input event: ",
-                                 connection->getWindowName().c_str(), eventDuration * 0.000001f);
-            dispatchEntry->eventEntry->appendDescription(msg);
-            ALOGI("%s", msg.c_str());
-        }
-
-        bool restartEvent;
-        if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
-            KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
-            restartEvent =
-                    afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
-        } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
-            MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
-            restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry,
-                                                               motionEntry, handled);
-        } else {
-            restartEvent = false;
-        }
-
-        // Dequeue the event and start the next cycle.
-        // Note that because the lock might have been released, it is possible that the
-        // contents of the wait queue to have been drained, so we need to double-check
-        // a few things.
-        if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
-            connection->waitQueue.dequeue(dispatchEntry);
-            traceWaitQueueLength(connection);
-            if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
-                connection->outboundQueue.enqueueAtHead(dispatchEntry);
-                traceOutboundQueueLength(connection);
-            } else {
-                releaseDispatchEntry(dispatchEntry);
-            }
-        }
-
-        // Start the next dispatch cycle for this connection.
-        startDispatchCycleLocked(now(), connection);
+    std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
+    if (dispatchEntryIt == connection->waitQueue.end()) {
+        return;
     }
+    DispatchEntry* dispatchEntry = *dispatchEntryIt;
+    const nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
+    if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
+        ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(),
+              ns2ms(eventDuration), dispatchEntry->eventEntry->getDescription().c_str());
+    }
+    reportDispatchStatistics(std::chrono::nanoseconds(eventDuration), *connection, handled);
+
+    bool restartEvent;
+    if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
+        KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+        restartEvent =
+                afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
+    } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
+        MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+        restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
+                                                           handled);
+    } else {
+        restartEvent = false;
+    }
+
+    // Dequeue the event and start the next cycle.
+    // Because the lock might have been released, it is possible that the
+    // contents of the wait queue to have been drained, so we need to double-check
+    // a few things.
+    dispatchEntryIt = connection->findWaitQueueEntry(seq);
+    if (dispatchEntryIt != connection->waitQueue.end()) {
+        dispatchEntry = *dispatchEntryIt;
+        connection->waitQueue.erase(dispatchEntryIt);
+        mAnrTracker.erase(dispatchEntry->timeoutTime,
+                          connection->inputChannel->getConnectionToken());
+        if (!connection->responsive) {
+            connection->responsive = isConnectionResponsive(*connection);
+        }
+        traceWaitQueueLength(connection);
+        if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
+            connection->outboundQueue.push_front(dispatchEntry);
+            traceOutboundQueueLength(connection);
+        } else {
+            releaseDispatchEntry(dispatchEntry);
+        }
+    }
+
+    // Start the next dispatch cycle for this connection.
+    startDispatchCycleLocked(now(), connection);
 }
 
 bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
@@ -4231,7 +4812,7 @@
     if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) {
         if (!handled) {
             // Report the key as unhandled, since the fallback was not handled.
-            mReporter->reportUnhandledKey(keyEntry->sequenceNum);
+            mReporter->reportUnhandledKey(keyEntry->id);
         }
         return false;
     }
@@ -4256,13 +4837,12 @@
                   keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
                   keyEntry->policyFlags);
 #endif
-            KeyEvent event;
-            initializeKeyEvent(&event, keyEntry);
+            KeyEvent event = createKeyEvent(*keyEntry);
             event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
 
             mLock.unlock();
 
-            mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event,
+            mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), &event,
                                           keyEntry->policyFlags, &event);
 
             mLock.lock();
@@ -4299,13 +4879,13 @@
               "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
               keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
 #endif
-        KeyEvent event;
-        initializeKeyEvent(&event, keyEntry);
+        KeyEvent event = createKeyEvent(*keyEntry);
 
         mLock.unlock();
 
-        bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event,
-                                                      keyEntry->policyFlags, &event);
+        bool fallback =
+                mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(),
+                                              &event, keyEntry->policyFlags, &event);
 
         mLock.lock();
 
@@ -4397,7 +4977,7 @@
 #endif
 
             // Report the key as unhandled, since there is no fallback key.
-            mReporter->reportUnhandledKey(keyEntry->sequenceNum);
+            mReporter->reportUnhandledKey(keyEntry->id);
         }
     }
     return false;
@@ -4417,21 +4997,50 @@
     mLock.lock();
 }
 
-void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) {
-    event->initialize(entry->deviceId, entry->source, entry->displayId, entry->action, entry->flags,
-                      entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
-                      entry->downTime, entry->eventTime);
+KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) {
+    KeyEvent event;
+    event.initialize(entry.id, entry.deviceId, entry.source, entry.displayId, INVALID_HMAC,
+                     entry.action, entry.flags, entry.keyCode, entry.scanCode, entry.metaState,
+                     entry.repeatCount, entry.downTime, entry.eventTime);
+    return event;
 }
 
-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.
 }
 
+/**
+ * Report the touch event latency to the statsd server.
+ * Input events are reported for statistics if:
+ * - This is a touchscreen event
+ * - InputFilter is not enabled
+ * - Event is not injected or synthesized
+ *
+ * Statistics should be reported before calling addValue, to prevent a fresh new sample
+ * from getting aggregated with the "old" data.
+ */
+void InputDispatcher::reportTouchEventForStatistics(const MotionEntry& motionEntry)
+        REQUIRES(mLock) {
+    const bool reportForStatistics = (motionEntry.source == AINPUT_SOURCE_TOUCHSCREEN) &&
+            !(motionEntry.isSynthesized()) && !mInputFilterEnabled;
+    if (!reportForStatistics) {
+        return;
+    }
+
+    if (mTouchStatistics.shouldReport()) {
+        android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mTouchStatistics.getMin(),
+                                   mTouchStatistics.getMax(), mTouchStatistics.getMean(),
+                                   mTouchStatistics.getStDev(), mTouchStatistics.getCount());
+        mTouchStatistics.reset();
+    }
+    const float latencyMicros = nanoseconds_to_microseconds(now() - motionEntry.eventTime);
+    mTouchStatistics.addValue(latencyMicros);
+}
+
 void InputDispatcher::traceInboundQueueLengthLocked() {
     if (ATRACE_ENABLED()) {
-        ATRACE_INT("iq", mInboundQueue.count());
+        ATRACE_INT("iq", mInboundQueue.size());
     }
 }
 
@@ -4439,7 +5048,7 @@
     if (ATRACE_ENABLED()) {
         char counterName[40];
         snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName().c_str());
-        ATRACE_INT(counterName, connection->outboundQueue.count());
+        ATRACE_INT(counterName, connection->outboundQueue.size());
     }
 }
 
@@ -4447,7 +5056,7 @@
     if (ATRACE_ENABLED()) {
         char counterName[40];
         snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName().c_str());
-        ATRACE_INT(counterName, connection->waitQueue.count());
+        ATRACE_INT(counterName, connection->waitQueue.size());
     }
 }
 
@@ -4457,9 +5066,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;
     }
 }
 
@@ -4470,4 +5079,22 @@
     mDispatcherIsAlive.wait(_l);
 }
 
+/**
+ * Wake up the dispatcher and wait until it processes all events and commands.
+ * The notification of mDispatcherEnteredIdle is guaranteed to happen after wake(), so
+ * this method can be safely called from any thread, as long as you've ensured that
+ * the work you are interested in completing has already been queued.
+ */
+bool InputDispatcher::waitForIdle() {
+    /**
+     * Timeout should represent the longest possible time that a device might spend processing
+     * events and commands.
+     */
+    constexpr std::chrono::duration TIMEOUT = 100ms;
+    std::unique_lock lock(mLock);
+    mLooper->wake();
+    std::cv_status result = mDispatcherEnteredIdle.wait_for(lock, TIMEOUT);
+    return result == std::cv_status::no_timeout;
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 67bf199..e679c6b 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -17,6 +17,7 @@
 #ifndef _UI_INPUT_DISPATCHER_H
 #define _UI_INPUT_DISPATCHER_H
 
+#include "AnrTracker.h"
 #include "CancelationOptions.h"
 #include "Entry.h"
 #include "InjectionState.h"
@@ -25,16 +26,16 @@
 #include "InputDispatcherPolicyInterface.h"
 #include "InputState.h"
 #include "InputTarget.h"
+#include "InputThread.h"
 #include "Monitor.h"
-#include "Queue.h"
 #include "TouchState.h"
 #include "TouchedWindow.h"
 
-#include <cutils/atomic.h>
 #include <input/Input.h>
 #include <input/InputApplication.h>
 #include <input/InputTransport.h>
 #include <input/InputWindow.h>
+#include <input/LatencyStatistics.h>
 #include <limits.h>
 #include <stddef.h>
 #include <ui/Region.h>
@@ -56,6 +57,16 @@
 
 class Connection;
 
+class HmacKeyManager {
+public:
+    HmacKeyManager();
+    std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
+
+private:
+    std::array<uint8_t, 32> sign(const uint8_t* data, size_t size) const;
+    const std::array<uint8_t, 128> mHmacKey;
+};
+
 /* Dispatches events to input targets.  Some functions of the input dispatcher, such as
  * identifying input targets, are controlled by a separate policy object.
  *
@@ -82,8 +93,9 @@
 
     virtual void dump(std::string& dump) override;
     virtual void monitor() override;
-
-    virtual void dispatchOnce() override;
+    virtual bool waitForIdle() override;
+    virtual status_t start() override;
+    virtual status_t stop() override;
 
     virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
     virtual void notifyKey(const NotifyKeyArgs* args) override;
@@ -92,62 +104,77 @@
     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;
+
     virtual void setInputWindows(
-            const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId,
-            const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) override;
+            const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
+                    handlesPerDisplay) override;
     virtual void setFocusedApplication(
             int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) override;
     virtual void setFocusedDisplay(int32_t displayId) override;
     virtual void setInputDispatchMode(bool enabled, bool frozen) override;
     virtual void setInputFilterEnabled(bool enabled) override;
+    virtual void setInTouchMode(bool inTouchMode) override;
 
     virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
                                     const sp<IBinder>& toToken) override;
 
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
-                                          int32_t displayId) override;
+    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) override;
     virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
                                           bool isGestureMonitor) override;
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override;
     virtual status_t pilferPointers(const sp<IBinder>& token) override;
 
 private:
-
-    enum DropReason {
-        DROP_REASON_NOT_DROPPED = 0,
-        DROP_REASON_POLICY = 1,
-        DROP_REASON_APP_SWITCH = 2,
-        DROP_REASON_DISABLED = 3,
-        DROP_REASON_BLOCKED = 4,
-        DROP_REASON_STALE = 5,
+    enum class DropReason {
+        NOT_DROPPED,
+        POLICY,
+        APP_SWITCH,
+        DISABLED,
+        BLOCKED,
+        STALE,
     };
 
+    std::unique_ptr<InputThread> mThread;
+
     sp<InputDispatcherPolicyInterface> mPolicy;
     android::InputDispatcherConfiguration mConfig;
 
     std::mutex mLock;
 
     std::condition_variable mDispatcherIsAlive;
+    std::condition_variable mDispatcherEnteredIdle;
 
     sp<Looper> mLooper;
 
     EventEntry* mPendingEvent GUARDED_BY(mLock);
-    Queue<EventEntry> mInboundQueue GUARDED_BY(mLock);
-    Queue<EventEntry> mRecentQueue GUARDED_BY(mLock);
-    Queue<CommandEntry> mCommandQueue GUARDED_BY(mLock);
+    std::deque<EventEntry*> mInboundQueue GUARDED_BY(mLock);
+    std::deque<EventEntry*> mRecentQueue GUARDED_BY(mLock);
+    std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock);
 
     DropReason mLastDropReason GUARDED_BY(mLock);
 
+    const IdGenerator mIdGenerator;
+
+    // With each iteration, InputDispatcher nominally processes one queued event,
+    // a timeout, or a response from an input consumer.
+    // This method should only be called on the input dispatcher's own thread.
+    void dispatchOnce();
+
     void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock);
 
     // Enqueues an inbound event.  Returns true if mLooper->wake() should be called.
     bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
 
     // Cleans up input state when dropping an inbound event.
-    void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock);
+    void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock);
+
+    // Enqueues a focus event.
+    void enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) REQUIRES(mLock);
 
     // Adds an event to a queue of recent events for debugging purposes.
     void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
@@ -156,23 +183,26 @@
     bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
     nsecs_t mAppSwitchDueTime GUARDED_BY(mLock);
 
-    bool isAppSwitchKeyEvent(KeyEntry* keyEntry);
+    bool isAppSwitchKeyEvent(const KeyEntry& keyEntry);
     bool isAppSwitchPendingLocked() REQUIRES(mLock);
     void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
 
-    // Stale event latency optimization.
-    static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry);
-
     // Blocked event latency optimization.  Drops old events when the user intends
     // to transfer focus to a new application.
     EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
 
     sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
+                                                    TouchState* touchState,
                                                     bool addOutsideTargets = false,
                                                     bool addPortalWindows = false) REQUIRES(mLock);
 
     // All registered connections mapped by channel file descriptor.
-    KeyedVector<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
+    std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
+
+    sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
+            REQUIRES(mLock);
+
+    void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
 
     struct IBinderHash {
         std::size_t operator()(const sp<IBinder>& b) const {
@@ -186,8 +216,6 @@
     std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
             REQUIRES(mLock);
 
-    ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
-
     // Input channels that will receive a copy of all input events sent to the provided display.
     std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
 
@@ -197,6 +225,12 @@
     // the pointer stream in order to claim it for a system gesture.
     std::unordered_map<int32_t, std::vector<Monitor>> mGestureMonitorsByDisplay GUARDED_BY(mLock);
 
+    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;
     bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
@@ -222,12 +256,14 @@
         bool operator==(const KeyReplacement& rhs) const {
             return keyCode == rhs.keyCode && deviceId == rhs.deviceId;
         }
-        bool operator<(const KeyReplacement& rhs) const {
-            return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId;
+    };
+    struct KeyReplacementHash {
+        size_t operator()(const KeyReplacement& key) const {
+            return std::hash<int32_t>()(key.keyCode) ^ (std::hash<int32_t>()(key.deviceId) << 1);
         }
     };
     // Maps the key code replaced, device id tuple to the key code it was replaced with
-    KeyedVector<KeyReplacement, int32_t> mReplacedKeys GUARDED_BY(mLock);
+    std::unordered_map<KeyReplacement, int32_t, KeyReplacementHash> mReplacedKeys GUARDED_BY(mLock);
     // Process certain Meta + Key combinations
     void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, int32_t& keyCode,
                                  int32_t& metaState);
@@ -235,7 +271,10 @@
     // Deferred command processing.
     bool haveCommandsLocked() const REQUIRES(mLock);
     bool runCommandsLockedInterruptible() REQUIRES(mLock);
-    CommandEntry* postCommandLocked(Command command) REQUIRES(mLock);
+    void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
+
+    nsecs_t processAnrsLocked() REQUIRES(mLock);
+    nsecs_t getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock);
 
     // Input filter processing.
     bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
@@ -250,9 +289,12 @@
     bool mDispatchEnabled GUARDED_BY(mLock);
     bool mDispatchFrozen GUARDED_BY(mLock);
     bool mInputFilterEnabled GUARDED_BY(mLock);
+    bool mInTouchMode GUARDED_BY(mLock);
 
     std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
             GUARDED_BY(mLock);
+    void setInputWindowsLocked(const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
+                               int32_t displayId) REQUIRES(mLock);
     // Get window handles by display, return an empty vector if not found.
     std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const
             REQUIRES(mLock);
@@ -261,12 +303,18 @@
     sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
     bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
 
+    /*
+     * Validate and update InputWindowHandles for a given display.
+     */
+    void updateWindowHandlesForDisplayLocked(
+            const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
+            REQUIRES(mLock);
+
     // Focus tracking for keys, trackball, etc.
     std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
             GUARDED_BY(mLock);
 
-    KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
-    TouchState mTempTouchState GUARDED_BY(mLock);
+    std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
 
     // Focused applications.
     std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay
@@ -276,7 +324,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)
@@ -286,57 +334,74 @@
                            nsecs_t* nextWakeupTime) REQUIRES(mLock);
     bool dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason,
                               nsecs_t* nextWakeupTime) REQUIRES(mLock);
+    void dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) REQUIRES(mLock);
     void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
                              const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
 
-    void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry);
-    void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry);
+    void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
+    void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
 
-    // Keeping track of ANR timeouts.
-    enum InputTargetWaitCause {
-        INPUT_TARGET_WAIT_CAUSE_NONE,
-        INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY,
-        INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY,
-    };
+    /**
+     * This field is set if there is no focused window, and we have an event that requires
+     * a focused window to be dispatched (for example, a KeyEvent).
+     * When this happens, we will wait until *mNoFocusedWindowTimeoutTime before
+     * dropping the event and raising an ANR for that application.
+     * This is useful if an application is slow to add a focused window.
+     */
+    std::optional<nsecs_t> mNoFocusedWindowTimeoutTime GUARDED_BY(mLock);
 
-    InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock);
-    nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock);
-    nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock);
-    bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock);
-    sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock);
+    bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock);
+
+    /**
+     * Time to stop waiting for the events to be processed while trying to dispatch a key.
+     * When this time expires, we just send the pending key event to the currently focused window,
+     * without waiting on other events to be processed first.
+     */
+    std::optional<nsecs_t> mKeyIsWaitingForEventsTimeout GUARDED_BY(mLock);
+    bool shouldWaitToSendKeyLocked(nsecs_t currentTime, const char* focusedWindowName)
+            REQUIRES(mLock);
+
+    /**
+     * The focused application at the time when no focused window was present.
+     * Used to raise an ANR when we have no focused window.
+     */
+    sp<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock);
+
+    // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
+    // AnrTracker must be kept in-sync with all responsive connection.waitQueues.
+    // If a connection is not responsive, then the entries should not be added to the AnrTracker.
+    // Once a connection becomes unresponsive, its entries are removed from AnrTracker to
+    // prevent unneeded wakeups.
+    AnrTracker mAnrTracker GUARDED_BY(mLock);
+    void extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application,
+                                 const sp<IBinder>& connectionToken, nsecs_t timeoutExtension)
+            REQUIRES(mLock);
 
     // Contains the last window which received a hover event.
     sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
 
-    // Finding targets for input events.
-    int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
-                                        const sp<InputApplicationHandle>& applicationHandle,
-                                        const sp<InputWindowHandle>& windowHandle,
-                                        nsecs_t* nextWakeupTime, const char* reason)
-            REQUIRES(mLock);
-
-    void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
-
-    void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
-                                                 const sp<InputChannel>& inputChannel)
-            REQUIRES(mLock);
+    void cancelEventsForAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
     nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
-    void resetANRTimeoutsLocked() REQUIRES(mLock);
+    // If a focused application changes, we should stop counting down the "no focused window" time,
+    // because we will have no way of knowing when the previous application actually added a window.
+    // This also means that we will miss cases like pulling down notification shade when the
+    // focused application does not have a focused window (no ANR will be raised if notification
+    // shade is pulled down while we are counting down the timeout).
+    void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
 
-    int32_t getTargetDisplayId(const EventEntry* entry);
-    int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
+    int32_t getTargetDisplayId(const EventEntry& entry);
+    int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry,
                                            std::vector<InputTarget>& inputTargets,
                                            nsecs_t* nextWakeupTime) REQUIRES(mLock);
-    int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
+    int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry,
                                            std::vector<InputTarget>& inputTargets,
                                            nsecs_t* nextWakeupTime,
                                            bool* outConflictingPointerActions) REQUIRES(mLock);
     std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(
-            int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows)
+            int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const
             REQUIRES(mLock);
-    void addGestureMonitors(const std::vector<Monitor>& monitors,
-                            std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0,
-                            float yOffset = 0);
+    std::vector<TouchedMonitor> selectResponsiveMonitorsLocked(
+            const std::vector<TouchedMonitor>& gestureMonitors) const REQUIRES(mLock);
 
     void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
                                BitSet32 pointerIds, std::vector<InputTarget>& inputTargets)
@@ -346,7 +411,7 @@
     void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
                                           float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
 
-    void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock);
+    void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
     bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
                                   const InjectionState* injectionState);
     bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x,
@@ -355,23 +420,18 @@
     std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle,
                                           const sp<InputWindowHandle>& windowHandle);
 
-    std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
-                                                   const sp<InputWindowHandle>& windowHandle,
-                                                   const EventEntry* eventEntry,
-                                                   const char* targetType) REQUIRES(mLock);
-
     // Manage the dispatch cycle for a single connection.
     // These methods are deliberately not Interruptible because doing all of the work
     // with the mutex held makes it easier to ensure that connection invariants are maintained.
     // If needed, the methods post commands to run later once the critical bits are done.
     void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                    EventEntry* eventEntry, const InputTarget* inputTarget)
+                                    EventEntry* eventEntry, const InputTarget& inputTarget)
             REQUIRES(mLock);
     void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                      EventEntry* eventEntry, const InputTarget* inputTarget)
+                                      EventEntry* eventEntry, const InputTarget& inputTarget)
             REQUIRES(mLock);
     void enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry,
-                                    const InputTarget* inputTarget, int32_t dispatchMode)
+                                    const InputTarget& inputTarget, int32_t dispatchMode)
             REQUIRES(mLock);
     void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
             REQUIRES(mLock);
@@ -379,7 +439,7 @@
                                    uint32_t seq, bool handled) REQUIRES(mLock);
     void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
                                         bool notify) REQUIRES(mLock);
-    void drainDispatchQueue(Queue<DispatchEntry>* queue);
+    void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
     void releaseDispatchEntry(DispatchEntry* dispatchEntry);
     static int handleReceiveCallback(int fd, int events, void* data);
     // The action sent should only be of type AMOTION_EVENT_*
@@ -400,8 +460,11 @@
                                                         const CancelationOptions& options)
             REQUIRES(mLock);
 
+    void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection)
+            REQUIRES(mLock);
+
     // Splitting motion events across windows.
-    MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
+    MotionEntry* splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds);
 
     // Reset and drop everything the dispatcher is doing.
     void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
@@ -426,16 +489,21 @@
             REQUIRES(mLock);
     void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
                               const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
-    void onANRLocked(nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
-                     const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime,
-                     nsecs_t waitStartTime, const char* reason) REQUIRES(mLock);
+    void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
+    void onAnrLocked(const sp<InputApplicationHandle>& application) REQUIRES(mLock);
+    void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
+            REQUIRES(mLock);
+    void updateLastAnrStateLocked(const sp<InputApplicationHandle>& application,
+                                  const std::string& reason) REQUIRES(mLock);
+    void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
+            REQUIRES(mLock);
 
     // Outbound policy interactions.
     void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry)
             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);
@@ -446,12 +514,16 @@
                                              DispatchEntry* dispatchEntry, MotionEntry* motionEntry,
                                              bool handled) REQUIRES(mLock);
     void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
+    KeyEvent createKeyEvent(const KeyEntry& entry);
     void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
 
     // Statistics gathering.
-    void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
-                                  int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
+    static constexpr std::chrono::duration TOUCH_STATS_REPORT_PERIOD = 5min;
+    LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD};
+
+    void reportTouchEventForStatistics(const MotionEntry& entry);
+    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/InputDispatcherThread.cpp b/services/inputflinger/dispatcher/InputDispatcherThread.cpp
deleted file mode 100644
index 18b1b8c..0000000
--- a/services/inputflinger/dispatcher/InputDispatcherThread.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "InputDispatcherThread.h"
-
-#include "InputDispatcherInterface.h"
-
-namespace android {
-
-InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher)
-      : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {}
-
-InputDispatcherThread::~InputDispatcherThread() {}
-
-bool InputDispatcherThread::threadLoop() {
-    mDispatcher->dispatchOnce();
-    return true;
-}
-
-} // namespace android
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 7d9b03a..386056d 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -16,9 +16,11 @@
 
 #include "InputState.h"
 
+#include "InputDispatcher.h"
+
 namespace android::inputdispatcher {
 
-InputState::InputState() {}
+InputState::InputState(const IdGenerator& idGenerator) : mIdGenerator(idGenerator) {}
 
 InputState::~InputState() {}
 
@@ -36,12 +38,12 @@
     return false;
 }
 
-bool InputState::trackKey(const KeyEntry* entry, int32_t action, int32_t flags) {
+bool InputState::trackKey(const KeyEntry& entry, int32_t action, int32_t flags) {
     switch (action) {
         case AKEY_EVENT_ACTION_UP: {
-            if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) {
+            if (entry.flags & AKEY_EVENT_FLAG_FALLBACK) {
                 for (size_t i = 0; i < mFallbackKeys.size();) {
-                    if (mFallbackKeys.valueAt(i) == entry->keyCode) {
+                    if (mFallbackKeys.valueAt(i) == entry.keyCode) {
                         mFallbackKeys.removeItemsAt(i);
                     } else {
                         i += 1;
@@ -65,7 +67,7 @@
     #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, "
                     "keyCode=%d, scanCode=%d",
-                    entry->deviceId, entry->source, entry->keyCode, entry->scanCode);
+                    entry.deviceId, entry.source, entry.keyCode, entry.scanCode);
     #endif
             return false;
             */
@@ -86,7 +88,7 @@
     }
 }
 
-bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t flags) {
+bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t flags) {
     int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
     switch (actionMasked) {
         case AMOTION_EVENT_ACTION_UP:
@@ -99,7 +101,7 @@
 #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
                   "displayId=%" PRId32 ", actionMasked=%d",
-                  entry->deviceId, entry->source, entry->displayId, actionMasked);
+                  entry.deviceId, entry.source, entry.displayId, actionMasked);
 #endif
             return false;
         }
@@ -116,7 +118,7 @@
         case AMOTION_EVENT_ACTION_POINTER_UP:
         case AMOTION_EVENT_ACTION_POINTER_DOWN:
         case AMOTION_EVENT_ACTION_MOVE: {
-            if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) {
+            if (entry.source & AINPUT_SOURCE_CLASS_NAVIGATION) {
                 // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need
                 // to generate cancellation events for these since they're based in relative rather
                 // than absolute units.
@@ -125,35 +127,38 @@
 
             ssize_t index = findMotionMemento(entry, false /*hovering*/);
 
-            if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) {
+            if (entry.source & AINPUT_SOURCE_CLASS_JOYSTICK) {
                 // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all
                 // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral.
                 // Any other value and we need to track the motion so we can send cancellation
                 // events for anything generating fallback events (e.g. DPad keys for joystick
                 // movements).
                 if (index >= 0) {
-                    if (entry->pointerCoords[0].isEmpty()) {
+                    if (entry.pointerCoords[0].isEmpty()) {
                         mMotionMementos.erase(mMotionMementos.begin() + index);
                     } else {
                         MotionMemento& memento = mMotionMementos[index];
                         memento.setPointers(entry);
                     }
-                } else if (!entry->pointerCoords[0].isEmpty()) {
+                } else if (!entry.pointerCoords[0].isEmpty()) {
                     addMotionMemento(entry, flags, false /*hovering*/);
                 }
 
                 // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
                 return true;
             }
+
             if (index >= 0) {
                 MotionMemento& memento = mMotionMementos[index];
-                memento.setPointers(entry);
-                return true;
+                if (memento.firstNewPointerIdx < 0) {
+                    memento.setPointers(entry);
+                    return true;
+                }
             }
 #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Dropping inconsistent motion pointer up/down or move event: "
                   "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d",
-                  entry->deviceId, entry->source, entry->displayId, actionMasked);
+                  entry.deviceId, entry.source, entry.displayId, actionMasked);
 #endif
             return false;
         }
@@ -167,7 +172,7 @@
 #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, "
                   "displayId=%" PRId32,
-                  entry->deviceId, entry->source, entry->displayId);
+                  entry.deviceId, entry.source, entry.displayId);
 #endif
             return false;
         }
@@ -187,77 +192,90 @@
     }
 }
 
-ssize_t InputState::findKeyMemento(const KeyEntry* entry) const {
+ssize_t InputState::findKeyMemento(const KeyEntry& entry) const {
     for (size_t i = 0; i < mKeyMementos.size(); i++) {
         const KeyMemento& memento = mKeyMementos[i];
-        if (memento.deviceId == entry->deviceId && memento.source == entry->source &&
-            memento.displayId == entry->displayId && memento.keyCode == entry->keyCode &&
-            memento.scanCode == entry->scanCode) {
+        if (memento.deviceId == entry.deviceId && memento.source == entry.source &&
+            memento.displayId == entry.displayId && memento.keyCode == entry.keyCode &&
+            memento.scanCode == entry.scanCode) {
             return i;
         }
     }
     return -1;
 }
 
-ssize_t InputState::findMotionMemento(const MotionEntry* entry, bool hovering) const {
+ssize_t InputState::findMotionMemento(const MotionEntry& entry, bool hovering) const {
     for (size_t i = 0; i < mMotionMementos.size(); i++) {
         const MotionMemento& memento = mMotionMementos[i];
-        if (memento.deviceId == entry->deviceId && memento.source == entry->source &&
-            memento.displayId == entry->displayId && memento.hovering == hovering) {
+        if (memento.deviceId == entry.deviceId && memento.source == entry.source &&
+            memento.displayId == entry.displayId && memento.hovering == hovering) {
             return i;
         }
     }
     return -1;
 }
 
-void InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) {
+void InputState::addKeyMemento(const KeyEntry& entry, int32_t flags) {
     KeyMemento memento;
-    memento.deviceId = entry->deviceId;
-    memento.source = entry->source;
-    memento.displayId = entry->displayId;
-    memento.keyCode = entry->keyCode;
-    memento.scanCode = entry->scanCode;
-    memento.metaState = entry->metaState;
+    memento.deviceId = entry.deviceId;
+    memento.source = entry.source;
+    memento.displayId = entry.displayId;
+    memento.keyCode = entry.keyCode;
+    memento.scanCode = entry.scanCode;
+    memento.metaState = entry.metaState;
     memento.flags = flags;
-    memento.downTime = entry->downTime;
-    memento.policyFlags = entry->policyFlags;
+    memento.downTime = entry.downTime;
+    memento.policyFlags = entry.policyFlags;
     mKeyMementos.push_back(memento);
 }
 
-void InputState::addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering) {
+void InputState::addMotionMemento(const MotionEntry& entry, int32_t flags, bool hovering) {
     MotionMemento memento;
-    memento.deviceId = entry->deviceId;
-    memento.source = entry->source;
-    memento.displayId = entry->displayId;
+    memento.deviceId = entry.deviceId;
+    memento.source = entry.source;
+    memento.displayId = entry.displayId;
     memento.flags = flags;
-    memento.xPrecision = entry->xPrecision;
-    memento.yPrecision = entry->yPrecision;
-    memento.downTime = entry->downTime;
+    memento.xPrecision = entry.xPrecision;
+    memento.yPrecision = entry.yPrecision;
+    memento.xCursorPosition = entry.xCursorPosition;
+    memento.yCursorPosition = entry.yCursorPosition;
+    memento.downTime = entry.downTime;
     memento.setPointers(entry);
     memento.hovering = hovering;
-    memento.policyFlags = entry->policyFlags;
+    memento.policyFlags = entry.policyFlags;
     mMotionMementos.push_back(memento);
 }
 
-void InputState::MotionMemento::setPointers(const MotionEntry* entry) {
-    pointerCount = entry->pointerCount;
-    for (uint32_t i = 0; i < entry->pointerCount; i++) {
-        pointerProperties[i].copyFrom(entry->pointerProperties[i]);
-        pointerCoords[i].copyFrom(entry->pointerCoords[i]);
+void InputState::MotionMemento::setPointers(const MotionEntry& entry) {
+    pointerCount = entry.pointerCount;
+    for (uint32_t i = 0; i < entry.pointerCount; i++) {
+        pointerProperties[i].copyFrom(entry.pointerProperties[i]);
+        pointerCoords[i].copyFrom(entry.pointerCoords[i]);
     }
 }
 
-void InputState::synthesizeCancelationEvents(nsecs_t currentTime,
-                                             std::vector<EventEntry*>& outEvents,
-                                             const CancelationOptions& options) {
+void InputState::MotionMemento::mergePointerStateTo(MotionMemento& other) const {
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        if (other.firstNewPointerIdx < 0) {
+            other.firstNewPointerIdx = other.pointerCount;
+        }
+        other.pointerProperties[other.pointerCount].copyFrom(pointerProperties[i]);
+        other.pointerCoords[other.pointerCount].copyFrom(pointerCoords[i]);
+        other.pointerCount++;
+    }
+}
+
+std::vector<EventEntry*> InputState::synthesizeCancelationEvents(
+        nsecs_t currentTime, const CancelationOptions& options) {
+    std::vector<EventEntry*> events;
     for (KeyMemento& memento : mKeyMementos) {
         if (shouldCancelKey(memento, options)) {
-            outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime,
-                                             memento.deviceId, memento.source, memento.displayId,
-                                             memento.policyFlags, AKEY_EVENT_ACTION_UP,
-                                             memento.flags | AKEY_EVENT_FLAG_CANCELED,
-                                             memento.keyCode, memento.scanCode, memento.metaState,
-                                             0, memento.downTime));
+            events.push_back(new KeyEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
+                                          memento.source, memento.displayId, memento.policyFlags,
+                                          AKEY_EVENT_ACTION_UP,
+                                          memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode,
+                                          memento.scanCode, memento.metaState, 0 /*repeatCount*/,
+                                          memento.downTime));
         }
     }
 
@@ -265,17 +283,71 @@
         if (shouldCancelMotion(memento, options)) {
             const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT
                                                     : AMOTION_EVENT_ACTION_CANCEL;
-            outEvents.push_back(
-                    new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId,
-                                    memento.source, memento.displayId, memento.policyFlags, action,
-                                    0 /*actionButton*/, memento.flags, AMETA_NONE,
-                                    0 /*buttonState*/, MotionClassification::NONE,
-                                    AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
-                                    memento.yPrecision, memento.downTime, memento.pointerCount,
-                                    memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/,
-                                    0 /*yOffset*/));
+            events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
+                                             memento.source, memento.displayId, memento.policyFlags,
+                                             action, 0 /*actionButton*/, memento.flags, AMETA_NONE,
+                                             0 /*buttonState*/, MotionClassification::NONE,
+                                             AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+                                             memento.yPrecision, memento.xCursorPosition,
+                                             memento.yCursorPosition, memento.downTime,
+                                             memento.pointerCount, memento.pointerProperties,
+                                             memento.pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/));
         }
     }
+    return events;
+}
+
+std::vector<EventEntry*> InputState::synthesizePointerDownEvents(nsecs_t currentTime) {
+    std::vector<EventEntry*> events;
+    for (MotionMemento& memento : mMotionMementos) {
+        if (!(memento.source & AINPUT_SOURCE_CLASS_POINTER)) {
+            continue;
+        }
+
+        if (memento.firstNewPointerIdx < 0) {
+            continue;
+        }
+
+        uint32_t pointerCount = 0;
+        PointerProperties pointerProperties[MAX_POINTERS];
+        PointerCoords pointerCoords[MAX_POINTERS];
+
+        // We will deliver all pointers the target already knows about
+        for (uint32_t i = 0; i < static_cast<uint32_t>(memento.firstNewPointerIdx); i++) {
+            pointerProperties[i].copyFrom(memento.pointerProperties[i]);
+            pointerCoords[i].copyFrom(memento.pointerCoords[i]);
+            pointerCount++;
+        }
+
+        // We will send explicit events for all pointers the target doesn't know about
+        for (uint32_t i = static_cast<uint32_t>(memento.firstNewPointerIdx);
+                i < memento.pointerCount; i++) {
+
+            pointerProperties[i].copyFrom(memento.pointerProperties[i]);
+            pointerCoords[i].copyFrom(memento.pointerCoords[i]);
+            pointerCount++;
+
+            // Down only if the first pointer, pointer down otherwise
+            const int32_t action = (pointerCount <= 1)
+                    ? AMOTION_EVENT_ACTION_DOWN
+                    : AMOTION_EVENT_ACTION_POINTER_DOWN
+                            | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+            events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
+                                             memento.source, memento.displayId, memento.policyFlags,
+                                             action, 0 /*actionButton*/, memento.flags, AMETA_NONE,
+                                             0 /*buttonState*/, MotionClassification::NONE,
+                                             AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
+                                             memento.yPrecision, memento.xCursorPosition,
+                                             memento.yCursorPosition, memento.downTime,
+                                             pointerCount, pointerProperties, pointerCoords,
+                                             0 /*xOffset*/, 0 /*yOffset*/));
+        }
+
+        memento.firstNewPointerIdx = INVALID_POINTER_INDEX;
+    }
+
+    return events;
 }
 
 void InputState::clear() {
@@ -284,21 +356,27 @@
     mFallbackKeys.clear();
 }
 
-void InputState::copyPointerStateTo(InputState& other) const {
+void InputState::mergePointerStateTo(InputState& other) {
     for (size_t i = 0; i < mMotionMementos.size(); i++) {
-        const MotionMemento& memento = mMotionMementos[i];
+        MotionMemento& memento = mMotionMementos[i];
+        // Since we support split pointers we need to merge touch events
+        // from the same source + device + screen.
         if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
-            for (size_t j = 0; j < other.mMotionMementos.size();) {
-                const MotionMemento& otherMemento = other.mMotionMementos[j];
+            bool merged = false;
+            for (size_t j = 0; j < other.mMotionMementos.size(); j++) {
+                MotionMemento& otherMemento = other.mMotionMementos[j];
                 if (memento.deviceId == otherMemento.deviceId &&
                     memento.source == otherMemento.source &&
                     memento.displayId == otherMemento.displayId) {
-                    other.mMotionMementos.erase(other.mMotionMementos.begin() + j);
-                } else {
-                    j += 1;
+                    memento.mergePointerStateTo(otherMemento);
+                    merged = true;
+                    break;
                 }
             }
-            other.mMotionMementos.push_back(memento);
+            if (!merged) {
+                memento.firstNewPointerIdx = 0;
+                other.mMotionMementos.push_back(memento);
+            }
         }
     }
 }
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index 205b647..d97a664 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -24,14 +24,13 @@
 
 namespace android::inputdispatcher {
 
-// Sequence number for synthesized or injected events.
-constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0;
+static constexpr int32_t INVALID_POINTER_INDEX = -1;
 
 /* Tracks dispatched key and motion event state so that cancellation events can be
  * synthesized when events are dropped. */
 class InputState {
 public:
-    InputState();
+    explicit InputState(const IdGenerator& idGenerator);
     ~InputState();
 
     // Returns true if there is no state to be canceled.
@@ -44,22 +43,25 @@
     // Records tracking information for a key event that has just been published.
     // Returns true if the event should be delivered, false if it is inconsistent
     // and should be skipped.
-    bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags);
+    bool trackKey(const KeyEntry& entry, int32_t action, int32_t flags);
 
     // Records tracking information for a motion event that has just been published.
     // Returns true if the event should be delivered, false if it is inconsistent
     // and should be skipped.
-    bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags);
+    bool trackMotion(const MotionEntry& entry, int32_t action, int32_t flags);
 
     // Synthesizes cancelation events for the current state and resets the tracked state.
-    void synthesizeCancelationEvents(nsecs_t currentTime, std::vector<EventEntry*>& outEvents,
-                                     const CancelationOptions& options);
+    std::vector<EventEntry*> synthesizeCancelationEvents(nsecs_t currentTime,
+                                                         const CancelationOptions& options);
+
+    // Synthesizes down events for the current state.
+    std::vector<EventEntry*> synthesizePointerDownEvents(nsecs_t currentTime);
 
     // Clears the current state.
     void clear();
 
-    // Copies pointer-related parts of the input state to another instance.
-    void copyPointerStateTo(InputState& other) const;
+    // Merges pointer-related parts of the input state into another instance.
+    void mergePointerStateTo(InputState& other);
 
     // Gets the fallback key associated with a keycode.
     // Returns -1 if none.
@@ -94,25 +96,32 @@
         int32_t flags;
         float xPrecision;
         float yPrecision;
+        float xCursorPosition;
+        float yCursorPosition;
         nsecs_t downTime;
         uint32_t pointerCount;
         PointerProperties pointerProperties[MAX_POINTERS];
         PointerCoords pointerCoords[MAX_POINTERS];
+        // Track for which pointers the target doesn't know about.
+        int32_t firstNewPointerIdx = INVALID_POINTER_INDEX;
         bool hovering;
         uint32_t policyFlags;
 
-        void setPointers(const MotionEntry* entry);
+        void setPointers(const MotionEntry& entry);
+        void mergePointerStateTo(MotionMemento& other) const;
     };
 
+    const IdGenerator& mIdGenerator; // InputDispatcher owns it so we won't have dangling reference.
+
     std::vector<KeyMemento> mKeyMementos;
     std::vector<MotionMemento> mMotionMementos;
     KeyedVector<int32_t, int32_t> mFallbackKeys;
 
-    ssize_t findKeyMemento(const KeyEntry* entry) const;
-    ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const;
+    ssize_t findKeyMemento(const KeyEntry& entry) const;
+    ssize_t findMotionMemento(const MotionEntry& entry, bool hovering) const;
 
-    void addKeyMemento(const KeyEntry* entry, int32_t flags);
-    void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering);
+    void addKeyMemento(const KeyEntry& entry, int32_t flags);
+    void addMotionMemento(const MotionEntry& entry, int32_t flags, bool hovering);
 
     static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options);
     static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options);
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 80fa2cb..0588374 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -42,4 +42,63 @@
     return StringPrintf("%" PRId32, dispatchMode);
 }
 
+void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset,
+                              float windowXScale, float windowYScale) {
+    // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
+    // and non splittable windows since we will just use all the pointers from the input event.
+    if (newPointerIds.isEmpty()) {
+        setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale);
+        return;
+    }
+
+    // Ensure that the new set of pointers doesn't overlap with the current set of pointers.
+    ALOG_ASSERT((pointerIds & newPointerIds) == 0);
+
+    pointerIds |= newPointerIds;
+    while (!newPointerIds.isEmpty()) {
+        int32_t pointerId = newPointerIds.clearFirstMarkedBit();
+        pointerInfos[pointerId].xOffset = xOffset;
+        pointerInfos[pointerId].yOffset = yOffset;
+        pointerInfos[pointerId].windowXScale = windowXScale;
+        pointerInfos[pointerId].windowYScale = windowYScale;
+    }
+}
+
+void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
+                                        float windowYScale) {
+    pointerIds.clear();
+    pointerInfos[0].xOffset = xOffset;
+    pointerInfos[0].yOffset = yOffset;
+    pointerInfos[0].windowXScale = windowXScale;
+    pointerInfos[0].windowYScale = windowYScale;
+}
+
+bool InputTarget::useDefaultPointerInfo() const {
+    return pointerIds.isEmpty();
+}
+
+const PointerInfo& InputTarget::getDefaultPointerInfo() const {
+    return pointerInfos[0];
+}
+
+std::string InputTarget::getPointerInfoString() const {
+    if (useDefaultPointerInfo()) {
+        const PointerInfo& pointerInfo = getDefaultPointerInfo();
+        return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)",
+                            pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale,
+                            pointerInfo.windowYScale);
+    }
+
+    std::string out;
+    for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) {
+        if (!pointerIds.hasBit(i)) {
+            continue;
+        }
+        out += StringPrintf("\n  pointerId %d: xOffset=%.1f, yOffset=%.1f "
+                            "windowScaleFactor=(%.1f, %.1f)",
+                            i, pointerInfos[i].xOffset, pointerInfos[i].yOffset,
+                            pointerInfos[i].windowXScale, pointerInfos[i].windowYScale);
+    }
+    return out;
+}
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 5acf92b..499a75f 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -24,6 +24,22 @@
 namespace android::inputdispatcher {
 
 /*
+ * Information about each pointer for an InputTarget. This includes offset and scale so
+ * all pointers can be normalized to a single offset and scale.
+ *
+ * These values are ignored for KeyEvents
+ */
+struct PointerInfo {
+    // The x and y offset to add to a MotionEvent as it is delivered.
+    float xOffset = 0.0f;
+    float yOffset = 0.0f;
+
+    // Scaling factor to apply to MotionEvent as it is delivered.
+    float windowXScale = 1.0f;
+    float windowYScale = 1.0f;
+};
+
+/*
  * An input target specifies how an input event is to be dispatched to a particular window
  * including the window's input channel, control flags, a timeout, and an X / Y offset to
  * be added to input event coordinates to compensate for the absolute position of the
@@ -93,21 +109,39 @@
     sp<InputChannel> inputChannel;
 
     // Flags for the input target.
-    int32_t flags;
-
-    // The x and y offset to add to a MotionEvent as it is delivered.
-    // (ignored for KeyEvents)
-    float xOffset, yOffset;
+    int32_t flags = 0;
 
     // Scaling factor to apply to MotionEvent as it is delivered.
     // (ignored for KeyEvents)
-    float globalScaleFactor;
-    float windowXScale = 1.0f;
-    float windowYScale = 1.0f;
+    float globalScaleFactor = 1.0f;
 
     // The subset of pointer ids to include in motion events dispatched to this input target
     // if FLAG_SPLIT is set.
     BitSet32 pointerIds;
+    // The data is stored by the pointerId. Use the bit position of pointerIds to look up
+    // PointerInfo per pointerId.
+    PointerInfo pointerInfos[MAX_POINTERS];
+
+    void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale,
+                     float windowYScale);
+    void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
+                               float windowYScale);
+
+    /**
+     * Returns whether the default pointer information should be used. This will be true when the
+     * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors
+     * and non splittable windows since we want all pointers for the EventEntry to go to this
+     * target.
+     */
+    bool useDefaultPointerInfo() const;
+
+    /**
+     * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is
+     * true.
+     */
+    const PointerInfo& getDefaultPointerInfo() const;
+
+    std::string getPointerInfoString() const;
 };
 
 std::string dispatchModeToString(int32_t dispatchMode);
diff --git a/services/inputflinger/dispatcher/Queue.h b/services/inputflinger/dispatcher/Queue.h
deleted file mode 100644
index 0e75821..0000000
--- a/services/inputflinger/dispatcher/Queue.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _UI_INPUT_INPUTDISPATCHER_QUEUE_H
-#define _UI_INPUT_INPUTDISPATCHER_QUEUE_H
-
-namespace android::inputdispatcher {
-
-// Generic queue implementation.
-template <typename T>
-struct Queue {
-    T* head;
-    T* tail;
-    uint32_t entryCount;
-
-    inline Queue() : head(nullptr), tail(nullptr), entryCount(0) {}
-
-    inline bool isEmpty() const { return !head; }
-
-    inline void enqueueAtTail(T* entry) {
-        entryCount++;
-        entry->prev = tail;
-        if (tail) {
-            tail->next = entry;
-        } else {
-            head = entry;
-        }
-        entry->next = nullptr;
-        tail = entry;
-    }
-
-    inline void enqueueAtHead(T* entry) {
-        entryCount++;
-        entry->next = head;
-        if (head) {
-            head->prev = entry;
-        } else {
-            tail = entry;
-        }
-        entry->prev = nullptr;
-        head = entry;
-    }
-
-    inline void dequeue(T* entry) {
-        entryCount--;
-        if (entry->prev) {
-            entry->prev->next = entry->next;
-        } else {
-            head = entry->next;
-        }
-        if (entry->next) {
-            entry->next->prev = entry->prev;
-        } else {
-            tail = entry->prev;
-        }
-    }
-
-    inline T* dequeueAtHead() {
-        entryCount--;
-        T* entry = head;
-        head = entry->next;
-        if (head) {
-            head->prev = nullptr;
-        } else {
-            tail = nullptr;
-        }
-        return entry;
-    }
-
-    uint32_t count() const { return entryCount; }
-};
-
-} // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_QUEUE_H
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 18848a0..2baceba 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -93,15 +93,6 @@
                            std::end(newMonitors));
 }
 
-void TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) {
-    for (size_t i = 0; i < windows.size(); i++) {
-        if (windows[i].windowHandle == windowHandle) {
-            windows.erase(windows.begin() + i);
-            return;
-        }
-    }
-}
-
 void TouchState::removeWindowByToken(const sp<IBinder>& token) {
     for (size_t i = 0; i < windows.size(); i++) {
         if (windows[i].windowHandle->getToken() == token) {
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 3e0e0eb..623c6a8 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -49,7 +49,6 @@
                            BitSet32 pointerIds);
     void addPortalWindow(const sp<android::InputWindowHandle>& windowHandle);
     void addGestureMonitors(const std::vector<TouchedMonitor>& monitors);
-    void removeWindow(const sp<android::InputWindowHandle>& windowHandle);
     void removeWindowByToken(const sp<IBinder>& token);
     void filterNonAsIsTouchWindows();
     void filterNonMonitors();
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 9329ca6..9b002f4 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -19,6 +19,7 @@
 
 #include <InputListener.h>
 #include <input/ISetInputWindowsListener.h>
+#include <unordered_map>
 
 namespace android {
 
@@ -63,12 +64,23 @@
     /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
     virtual void monitor() = 0;
 
-    /* Runs a single iteration of the dispatch loop.
-     * Nominally processes one queued event, a timeout, or a response from an input consumer.
-     *
-     * This method should only be called on the input dispatcher thread.
+    /**
+     * Wait until dispatcher is idle. That means, there are no further events to be processed,
+     * and all of the policy callbacks have been completed.
+     * Return true if the dispatcher is idle.
+     * Return false if the timeout waiting for the dispatcher to become idle has expired.
      */
-    virtual void dispatchOnce() = 0;
+    virtual bool waitForIdle() = 0;
+
+    /* Make the dispatcher start processing events.
+     *
+     * The dispatcher will start consuming events from the InputListenerInterface
+     * in the order that they were received.
+     */
+    virtual status_t start() = 0;
+
+    /* Makes the dispatcher stop processing events. */
+    virtual status_t stop() = 0;
 
     /* Injects an input event and optionally waits for sync.
      * The synchronization mode determines whether the method blocks while waiting for
@@ -78,16 +90,23 @@
      * 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;
 
-    /* Sets the list of input windows.
+    /*
+     * Check whether InputEvent actually happened by checking the signature of the event.
+     *
+     * Return nullptr if the event cannot be verified.
+     */
+    virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) = 0;
+
+    /* Sets the list of input windows per display.
      *
      * This method may be called on any thread (usually by the input manager).
      */
     virtual void setInputWindows(
-            const std::vector<sp<InputWindowHandle> >& inputWindowHandles, int32_t displayId,
-            const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) = 0;
+            const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>&
+                    handlesPerDisplay) = 0;
 
     /* Sets the focused application on the given display.
      *
@@ -116,6 +135,14 @@
      */
     virtual void setInputFilterEnabled(bool enabled) = 0;
 
+    /**
+     * Set the touch mode state.
+     * Touch mode is a global state that apps may enter / exit based on specific
+     * user interactions with input devices.
+     * If true, the device is in touch mode.
+     */
+    virtual void setInTouchMode(bool inTouchMode) = 0;
+
     /* Transfers touch focus from one window to another window.
      *
      * Returns true on success.  False if the window did not actually have touch focus.
@@ -126,8 +153,7 @@
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
-                                          int32_t displayId) = 0;
+    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
 
     /* Registers input channels to be used to monitor input events.
      *
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/dispatcher/include/InputDispatcherThread.h b/services/inputflinger/dispatcher/include/InputDispatcherThread.h
deleted file mode 100644
index 2604959..0000000
--- a/services/inputflinger/dispatcher/include/InputDispatcherThread.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
-
-#include <utils/RefBase.h>
-#include <utils/threads.h>
-
-namespace android {
-
-class InputDispatcherInterface;
-
-/* Enqueues and dispatches input events, endlessly. */
-class InputDispatcherThread : public Thread {
-public:
-    explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);
-    ~InputDispatcherThread();
-
-private:
-    virtual bool threadLoop();
-
-    sp<InputDispatcherInterface> mDispatcher;
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index b51dcb6..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 {
 
@@ -31,13 +30,12 @@
 
 /* Superclass of all input event argument objects */
 struct NotifyArgs {
-    uint32_t sequenceNum;
+    int32_t id;
     nsecs_t eventTime;
 
-    inline NotifyArgs() : sequenceNum(0), eventTime(0) { }
+    inline NotifyArgs() : id(0), eventTime(0) {}
 
-    inline explicit NotifyArgs(uint32_t sequenceNum, nsecs_t eventTime) :
-            sequenceNum(sequenceNum), eventTime(eventTime) { }
+    inline explicit NotifyArgs(int32_t id, nsecs_t eventTime) : id(id), eventTime(eventTime) {}
 
     virtual ~NotifyArgs() { }
 
@@ -52,7 +50,7 @@
 
     bool operator==(const NotifyConfigurationChangedArgs& rhs) const;
 
-    NotifyConfigurationChangedArgs(uint32_t sequenceNum, nsecs_t eventTime);
+    NotifyConfigurationChangedArgs(int32_t id, nsecs_t eventTime);
 
     NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other);
 
@@ -77,9 +75,9 @@
 
     inline NotifyKeyArgs() { }
 
-    NotifyKeyArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-            int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
-            int32_t scanCode, int32_t metaState, nsecs_t downTime);
+    NotifyKeyArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                  int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
+                  int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime);
 
     bool operator==(const NotifyKeyArgs& rhs) const;
 
@@ -107,31 +105,32 @@
      */
     MotionClassification classification;
     int32_t edgeFlags;
-    /**
-     * A timestamp in the input device's time base, not the platform's.
-     * The units are microseconds since the last reset.
-     * This can only be compared to other device timestamps from the same device.
-     * This value will overflow after a little over an hour.
-     */
-    uint32_t deviceTimestamp;
+
     uint32_t pointerCount;
     PointerProperties pointerProperties[MAX_POINTERS];
     PointerCoords pointerCoords[MAX_POINTERS];
     float xPrecision;
     float yPrecision;
+    /**
+     * Mouse cursor position when this event is reported relative to the origin of the specified
+     * display. Only valid if this is a mouse event (originates from a mouse or from a trackpad in
+     * gestures enabled mode.
+     */
+    float xCursorPosition;
+    float yCursorPosition;
     nsecs_t downTime;
     std::vector<TouchVideoFrame> videoFrames;
 
     inline NotifyMotionArgs() { }
 
-    NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-            int32_t displayId, uint32_t policyFlags,
-            int32_t action, int32_t actionButton, int32_t flags,
-            int32_t metaState, int32_t buttonState, MotionClassification classification,
-            int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount,
-            const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-            float xPrecision, float yPrecision, nsecs_t downTime,
-            const std::vector<TouchVideoFrame>& videoFrames);
+    NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
+                     int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
+                     int32_t flags, int32_t metaState, int32_t buttonState,
+                     MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount,
+                     const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
+                     float xPrecision, float yPrecision, float xCursorPosition,
+                     float yCursorPosition, nsecs_t downTime,
+                     const std::vector<TouchVideoFrame>& videoFrames);
 
     NotifyMotionArgs(const NotifyMotionArgs& other);
 
@@ -151,8 +150,8 @@
 
     inline NotifySwitchArgs() { }
 
-    NotifySwitchArgs(uint32_t sequenceNum, nsecs_t eventTime, uint32_t policyFlags,
-            uint32_t switchValues, uint32_t switchMask);
+    NotifySwitchArgs(int32_t id, nsecs_t eventTime, uint32_t policyFlags, uint32_t switchValues,
+                     uint32_t switchMask);
 
     NotifySwitchArgs(const NotifySwitchArgs& other);
 
@@ -171,7 +170,7 @@
 
     inline NotifyDeviceResetArgs() { }
 
-    NotifyDeviceResetArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId);
+    NotifyDeviceResetArgs(int32_t id, nsecs_t eventTime, int32_t deviceId);
 
     NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other);
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index ef567c7..0fa8787 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -19,19 +19,18 @@
 
 #include "PointerControllerInterface.h"
 
+#include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <input/InputDevice.h>
-#include <input/DisplayViewport.h>
 #include <input/VelocityControl.h>
 #include <input/VelocityTracker.h>
-#include <utils/KeyedVector.h>
-#include <utils/Thread.h>
+#include <utils/Errors.h>
 #include <utils/RefBase.h>
-#include <utils/SortedVector.h>
 
-#include <optional>
 #include <stddef.h>
 #include <unistd.h>
+#include <optional>
+#include <set>
 #include <unordered_map>
 #include <vector>
 
@@ -45,7 +44,16 @@
 
 namespace android {
 
-/* Processes raw input events and sends cooked event data to an input listener. */
+// --- InputReaderInterface ---
+
+/* The interface for the InputReader shared library.
+ *
+ * Manages one or more threads that process raw input events and sends cooked event data to an
+ * input listener.
+ *
+ * The implementation must guarantee thread safety for this interface. However, since the input
+ * listener is NOT thread safe, all calls to the listener must happen from the same thread.
+ */
 class InputReaderInterface : public virtual RefBase {
 protected:
     InputReaderInterface() { }
@@ -57,18 +65,17 @@
      * This method may be called on any thread (usually by the input manager). */
     virtual void dump(std::string& dump) = 0;
 
-    /* Called by the heatbeat to ensures that the reader has not deadlocked. */
+    /* Called by the heartbeat to ensures that the reader has not deadlocked. */
     virtual void monitor() = 0;
 
     /* Returns true if the input device is enabled. */
     virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
 
-    /* Runs a single iteration of the processing loop.
-     * Nominally reads and processes one incoming message from the EventHub.
-     *
-     * This method should be called on the input reader thread.
-     */
-    virtual void loopOnce() = 0;
+    /* Makes the reader start processing events from the kernel. */
+    virtual status_t start() = 0;
+
+    /* Makes the reader stop processing any more events. */
+    virtual status_t stop() = 0;
 
     /* Gets information about all input devices.
      *
@@ -105,17 +112,7 @@
     virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
 };
 
-/* Reads raw events from the event hub and processes them, endlessly. */
-class InputReaderThread : public Thread {
-public:
-    explicit InputReaderThread(const sp<InputReaderInterface>& reader);
-    virtual ~InputReaderThread();
-
-private:
-    sp<InputReaderInterface> mReader;
-
-    virtual bool threadLoop();
-};
+// --- InputReaderConfiguration ---
 
 /*
  * Input reader configuration.
@@ -253,7 +250,7 @@
     bool pointerCapture;
 
     // The set of currently disabled input devices.
-    SortedVector<int32_t> disabledDevices;
+    std::set<int32_t> disabledDevices;
 
     InputReaderConfiguration() :
             virtualKeyQuietTime(0),
@@ -273,6 +270,8 @@
             pointerGestureZoomSpeedRatio(0.3f),
             showTouches(false), pointerCapture(false) { }
 
+    static std::string changesToString(uint32_t changes);
+
     std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
     std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
             const;
@@ -288,6 +287,8 @@
     std::vector<DisplayViewport> mDisplays;
 };
 
+// --- TouchAffineTransformation ---
+
 struct TouchAffineTransformation {
     float x_scale;
     float x_ymix;
@@ -310,6 +311,8 @@
     void applyTo(float& x, float& y) const;
 };
 
+// --- InputReaderPolicyInterface ---
+
 /*
  * Input reader policy interface.
  *
@@ -319,8 +322,8 @@
  * The actual implementation is partially supported by callbacks into the DVM
  * via JNI.  This interface is also mocked in the unit tests.
  *
- * These methods must NOT re-enter the input reader since they may be called while
- * holding the input reader lock.
+ * These methods will NOT re-enter the input reader interface, so they may be called from
+ * any method in the input reader interface.
  */
 class InputReaderPolicyInterface : public virtual RefBase {
 protected:
diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h
new file mode 100644
index 0000000..407365a
--- /dev/null
+++ b/services/inputflinger/include/InputThread.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_INPUT_THREAD_H
+#define _UI_INPUT_THREAD_H
+
+#include <utils/Thread.h>
+
+namespace android {
+
+/* A thread that loops continuously until destructed to process input events.
+ *
+ * Creating the InputThread starts it immediately. The thread begins looping the loop
+ * function until the InputThread is destroyed. The wake function is used to wake anything
+ * that sleeps in the loop when it is time for the thread to be destroyed.
+ */
+class InputThread {
+public:
+    explicit InputThread(std::string name, std::function<void()> loop,
+                         std::function<void()> wake = nullptr);
+    virtual ~InputThread();
+
+    bool isCallingThread();
+
+private:
+    std::string mName;
+    std::function<void()> mThreadWake;
+    sp<Thread> mThread;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_THREAD_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 4e97397..83a610f 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -21,10 +21,8 @@
     ],
 }
 
-cc_library_shared {
-    name: "libinputreader",
-    defaults: ["inputflinger_defaults"],
-
+filegroup {
+    name: "libinputreader_sources",
     srcs: [
         "EventHub.cpp",
         "InputDevice.cpp",
@@ -44,29 +42,43 @@
         "mapper/TouchInputMapper.cpp",
         "mapper/VibratorInputMapper.cpp",
         "InputReader.cpp",
-        "InputReaderFactory.cpp",
         "TouchVideoDevice.cpp",
     ],
+}
 
+cc_defaults {
+    name: "libinputreader_defaults",
+    srcs: [":libinputreader_sources"],
     shared_libs: [
         "libbase",
-        "libinputflinger_base",
+        "libcap",
         "libcrypto",
         "libcutils",
         "libinput",
         "liblog",
         "libui",
         "libutils",
-        "libhardware_legacy",
-        "libstatslog",
     ],
-
     header_libs: [
-        "libinputflinger_headers",
         "libinputreader_headers",
     ],
+}
 
+cc_library_shared {
+    name: "libinputreader",
+    defaults: [
+        "inputflinger_defaults",
+        "libinputreader_defaults"
+    ],
+    srcs: [
+        "InputReaderFactory.cpp",
+    ],
+    shared_libs: [
+        // This should consist only of dependencies from inputflinger. Other dependencies should be
+        // in cc_defaults so that they are included in the tests.
+        "libinputflinger_base",
+    ],
     export_header_lib_headers: [
-        "libinputflinger_headers",
+        "libinputreader_headers",
     ],
 }
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index a5e5415..a1514af 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -24,11 +24,11 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/capability.h>
 #include <sys/epoll.h>
 #include <sys/inotify.h>
 #include <sys/ioctl.h>
 #include <sys/limits.h>
-#include <sys/utsname.h>
 #include <unistd.h>
 
 #define LOG_TAG "EventHub"
@@ -37,8 +37,6 @@
 
 #include "EventHub.h"
 
-#include <hardware_legacy/power.h>
-
 #include <android-base/stringprintf.h>
 #include <cutils/properties.h>
 #include <openssl/sha.h>
@@ -71,7 +69,6 @@
 
 static constexpr bool DEBUG = false;
 
-static const char* WAKE_LOCK_ID = "KeyEvents";
 static const char* DEVICE_PATH = "/dev/input";
 // v4l2 devices go directly into /dev
 static const char* VIDEO_DEVICE_PATH = "/dev";
@@ -94,14 +91,6 @@
     return out;
 }
 
-static void getLinuxRelease(int* major, int* minor) {
-    struct utsname info;
-    if (uname(&info) || sscanf(info.release, "%d.%d", major, minor) <= 0) {
-        *major = 0, *minor = 0;
-        ALOGE("Could not get linux version: %s", strerror(errno));
-    }
-}
-
 /**
  * Return true if name matches "v4l-touch*"
  */
@@ -246,6 +235,47 @@
     return !isVirtual && enabled;
 }
 
+/**
+ * Get the capabilities for the current process.
+ * Crashes the system if unable to create / check / destroy the capabilities object.
+ */
+class Capabilities final {
+public:
+    explicit Capabilities() {
+        mCaps = cap_get_proc();
+        LOG_ALWAYS_FATAL_IF(mCaps == nullptr, "Could not get capabilities of the current process");
+    }
+
+    /**
+     * Check whether the current process has a specific capability
+     * in the set of effective capabilities.
+     * Return CAP_SET if the process has the requested capability
+     * Return CAP_CLEAR otherwise.
+     */
+    cap_flag_value_t checkEffectiveCapability(cap_value_t capability) {
+        cap_flag_value_t value;
+        const int result = cap_get_flag(mCaps, capability, CAP_EFFECTIVE, &value);
+        LOG_ALWAYS_FATAL_IF(result == -1, "Could not obtain the requested capability");
+        return value;
+    }
+
+    ~Capabilities() {
+        const int result = cap_free(mCaps);
+        LOG_ALWAYS_FATAL_IF(result == -1, "Could not release the capabilities structure");
+    }
+
+private:
+    cap_t mCaps;
+};
+
+static void ensureProcessCanBlockSuspend() {
+    Capabilities capabilities;
+    const bool canBlockSuspend =
+            capabilities.checkEffectiveCapability(CAP_BLOCK_SUSPEND) == CAP_SET;
+    LOG_ALWAYS_FATAL_IF(!canBlockSuspend,
+                        "Input must be able to block suspend to properly process events");
+}
+
 // --- EventHub ---
 
 const int EventHub::EPOLL_MAX_EVENTS;
@@ -262,7 +292,7 @@
         mPendingEventCount(0),
         mPendingEventIndex(0),
         mPendingINotify(false) {
-    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
+    ensureProcessCanBlockSuspend();
 
     mEpollFd = epoll_create1(EPOLL_CLOEXEC);
     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
@@ -280,9 +310,8 @@
         ALOGI("Video device scanning disabled");
     }
 
-    struct epoll_event eventItem;
-    memset(&eventItem, 0, sizeof(eventItem));
-    eventItem.events = EPOLLIN;
+    struct epoll_event eventItem = {};
+    eventItem.events = EPOLLIN | EPOLLWAKEUP;
     eventItem.data.fd = mINotifyFd;
     int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);
@@ -306,11 +335,6 @@
     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
                         errno);
-
-    int major, minor;
-    getLinuxRelease(&major, &minor);
-    // EPOLLWAKEUP was introduced in kernel 3.5
-    mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
 }
 
 EventHub::~EventHub(void) {
@@ -326,8 +350,6 @@
     ::close(mINotifyFd);
     ::close(mWakeReadPipeFd);
     ::close(mWakeWritePipeFd);
-
-    release_wake_lock(WAKE_LOCK_ID);
 }
 
 InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
@@ -1018,26 +1040,24 @@
             break;
         }
 
-        // Poll for events.  Mind the wake lock dance!
-        // We hold a wake lock at all times except during epoll_wait().  This works due to some
-        // subtle choreography.  When a device driver has pending (unread) events, it acquires
-        // a kernel wake lock.  However, once the last pending event has been read, the device
-        // driver will release the kernel wake lock.  To prevent the system from going to sleep
-        // when this happens, the EventHub holds onto its own user wake lock while the client
-        // is processing events.  Thus the system can only sleep if there are no events
-        // pending or currently being processed.
+        // Poll for events.
+        // When a device driver has pending (unread) events, it acquires
+        // a kernel wake lock.  Once the last pending event has been read, the device
+        // driver will release the kernel wake lock, but the epoll will hold the wakelock,
+        // since we are using EPOLLWAKEUP. The wakelock is released by the epoll when epoll_wait
+        // is called again for the same fd that produced the event.
+        // Thus the system can only sleep if there are no events pending or
+        // currently being processed.
         //
         // The timeout is advisory only.  If the device is asleep, it will not wake just to
         // service the timeout.
         mPendingEventIndex = 0;
 
-        mLock.unlock(); // release lock before poll, must be before release_wake_lock
-        release_wake_lock(WAKE_LOCK_ID);
+        mLock.unlock(); // release lock before poll
 
         int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
 
-        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
-        mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock
+        mLock.lock(); // reacquire lock after poll
 
         if (pollResult == 0) {
             // Timed out.
@@ -1298,7 +1318,7 @@
     // joystick and gamepad buttons which are handled like keyboards for the most part.
     bool haveKeyboardKeys =
             containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) ||
-            containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK),
+            containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_WHEEL),
                                 sizeof_bit_array(KEY_MAX + 1));
     bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC),
                                                   sizeof_bit_array(BTN_MOUSE)) ||
@@ -1491,27 +1511,13 @@
         }
     }
 
-    std::string wakeMechanism = "EPOLLWAKEUP";
-    if (!mUsingEpollWakeup) {
-#ifndef EVIOCSSUSPENDBLOCK
-        // uapi headers don't include EVIOCSSUSPENDBLOCK, and future kernels
-        // will use an epoll flag instead, so as long as we want to support
-        // this feature, we need to be prepared to define the ioctl ourselves.
-#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int)
-#endif
-        if (ioctl(device->fd, EVIOCSSUSPENDBLOCK, 1)) {
-            wakeMechanism = "<none>";
-        } else {
-            wakeMechanism = "EVIOCSSUSPENDBLOCK";
-        }
-    }
     // Tell the kernel that we want to use the monotonic clock for reporting timestamps
     // associated with input events.  This is important because the input system
     // uses the timestamps extensively and assumes they were recorded using the monotonic
     // clock.
     int clockId = CLOCK_MONOTONIC;
     bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId);
-    ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.c_str(), toString(usingClockIoctl));
+    ALOGI("usingClockIoctl=%s", toString(usingClockIoctl));
 }
 
 void EventHub::openVideoDeviceLocked(const std::string& devicePath) {
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index d0613b0..4b19e5e 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -18,47 +18,67 @@
 
 #include "InputDevice.h"
 
-#include "InputMapper.h"
+#include <algorithm>
+
+#include "CursorInputMapper.h"
+#include "ExternalStylusInputMapper.h"
+#include "InputReaderContext.h"
+#include "JoystickInputMapper.h"
+#include "KeyboardInputMapper.h"
+#include "MultiTouchInputMapper.h"
+#include "RotaryEncoderInputMapper.h"
+#include "SingleTouchInputMapper.h"
+#include "SwitchInputMapper.h"
+#include "VibratorInputMapper.h"
 
 namespace android {
 
 InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
-                         int32_t controllerNumber, const InputDeviceIdentifier& identifier,
-                         uint32_t classes)
+                         const InputDeviceIdentifier& identifier)
       : mContext(context),
         mId(id),
         mGeneration(generation),
-        mControllerNumber(controllerNumber),
+        mControllerNumber(0),
         mIdentifier(identifier),
-        mClasses(classes),
+        mClasses(0),
         mSources(0),
         mIsExternal(false),
         mHasMic(false),
         mDropUntilNextSync(false) {}
 
-InputDevice::~InputDevice() {
-    size_t numMappers = mMappers.size();
-    for (size_t i = 0; i < numMappers; i++) {
-        delete mMappers[i];
-    }
-    mMappers.clear();
-}
+InputDevice::~InputDevice() {}
 
 bool InputDevice::isEnabled() {
-    return getEventHub()->isDeviceEnabled(mId);
+    if (!hasEventHubDevices()) {
+        return false;
+    }
+    // devices are either all enabled or all disabled, so we only need to check the first
+    auto& devicePair = mDevices.begin()->second;
+    auto& contextPtr = devicePair.first;
+    return contextPtr->isDeviceEnabled();
 }
 
 void InputDevice::setEnabled(bool enabled, nsecs_t when) {
+    if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) {
+        ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", "
+              "but the corresponding viewport is not found",
+              getName().c_str(), *mAssociatedDisplayPort);
+        enabled = false;
+    }
+
     if (isEnabled() == enabled) {
         return;
     }
 
+    // When resetting some devices, the driver needs to be queried to ensure that a proper reset is
+    // performed. The querying must happen when the device is enabled, so we reset after enabling
+    // but before disabling the device. See MultiTouchMotionAccumulator::reset for more information.
     if (enabled) {
-        getEventHub()->enableDevice(mId);
+        for_each_subdevice([](auto& context) { context.enableDevice(); });
         reset(when);
     } else {
         reset(when);
-        getEventHub()->disableDevice(mId);
+        for_each_subdevice([](auto& context) { context.disableDevice(); });
     }
     // Must change generation to flag this device as changed
     bumpGeneration();
@@ -103,31 +123,132 @@
         }
     }
 
-    size_t numMappers = mMappers.size();
-    for (size_t i = 0; i < numMappers; i++) {
-        InputMapper* mapper = mMappers[i];
-        mapper->dump(dump);
-    }
+    for_each_mapper([&dump](InputMapper& mapper) { mapper.dump(dump); });
 }
 
-void InputDevice::addMapper(InputMapper* mapper) {
-    mMappers.push_back(mapper);
+void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {
+    if (mDevices.find(eventHubId) != mDevices.end()) {
+        return;
+    }
+    std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
+    uint32_t classes = contextPtr->getDeviceClasses();
+    std::vector<std::unique_ptr<InputMapper>> mappers;
+
+    // Check if we should skip population
+    if (!populateMappers) {
+        mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
+        return;
+    }
+
+    // Switch-like devices.
+    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
+        mappers.push_back(std::make_unique<SwitchInputMapper>(*contextPtr));
+    }
+
+    // Scroll wheel-like devices.
+    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
+        mappers.push_back(std::make_unique<RotaryEncoderInputMapper>(*contextPtr));
+    }
+
+    // Vibrator-like devices.
+    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
+        mappers.push_back(std::make_unique<VibratorInputMapper>(*contextPtr));
+    }
+
+    // Keyboard-like devices.
+    uint32_t keyboardSource = 0;
+    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
+    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
+    }
+    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
+        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
+    }
+    if (classes & INPUT_DEVICE_CLASS_DPAD) {
+        keyboardSource |= AINPUT_SOURCE_DPAD;
+    }
+    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
+    }
+
+    if (keyboardSource != 0) {
+        mappers.push_back(
+                std::make_unique<KeyboardInputMapper>(*contextPtr, keyboardSource, keyboardType));
+    }
+
+    // Cursor-like devices.
+    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
+        mappers.push_back(std::make_unique<CursorInputMapper>(*contextPtr));
+    }
+
+    // Touchscreens and touchpad devices.
+    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
+        mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
+    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
+        mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));
+    }
+
+    // Joystick-like devices.
+    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
+        mappers.push_back(std::make_unique<JoystickInputMapper>(*contextPtr));
+    }
+
+    // External stylus-like devices.
+    if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+        mappers.push_back(std::make_unique<ExternalStylusInputMapper>(*contextPtr));
+    }
+
+    // insert the context into the devices set
+    mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
+}
+
+void InputDevice::removeEventHubDevice(int32_t eventHubId) {
+    mDevices.erase(eventHubId);
 }
 
 void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
                             uint32_t changes) {
     mSources = 0;
+    mClasses = 0;
+    mControllerNumber = 0;
+
+    for_each_subdevice([this](InputDeviceContext& context) {
+        mClasses |= context.getDeviceClasses();
+        int32_t controllerNumber = context.getDeviceControllerNumber();
+        if (controllerNumber > 0) {
+            if (mControllerNumber && mControllerNumber != controllerNumber) {
+                ALOGW("InputDevice::configure(): composite device contains multiple unique "
+                      "controller numbers");
+            }
+            mControllerNumber = controllerNumber;
+        }
+    });
+
+    mIsExternal = !!(mClasses & INPUT_DEVICE_CLASS_EXTERNAL);
+    mHasMic = !!(mClasses & INPUT_DEVICE_CLASS_MIC);
 
     if (!isIgnored()) {
         if (!changes) { // first time only
-            mContext->getEventHub()->getConfiguration(mId, &mConfiguration);
+            mConfiguration.clear();
+            for_each_subdevice([this](InputDeviceContext& context) {
+                PropertyMap configuration;
+                context.getConfiguration(&configuration);
+                mConfiguration.addAll(&configuration);
+            });
         }
 
         if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
             if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) {
                 sp<KeyCharacterMap> keyboardLayout =
                         mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
-                if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) {
+                bool shouldBumpGeneration = false;
+                for_each_subdevice(
+                        [&keyboardLayout, &shouldBumpGeneration](InputDeviceContext& context) {
+                            if (context.setKeyboardLayoutOverlay(keyboardLayout)) {
+                                shouldBumpGeneration = true;
+                            }
+                        });
+                if (shouldBumpGeneration) {
                     bumpGeneration();
                 }
             }
@@ -144,14 +265,15 @@
         }
 
         if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) {
-            ssize_t index = config->disabledDevices.indexOf(mId);
-            bool enabled = index < 0;
+            auto it = config->disabledDevices.find(mId);
+            bool enabled = it == config->disabledDevices.end();
             setEnabled(enabled, when);
         }
 
         if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
             // In most situations, no port will be specified.
             mAssociatedDisplayPort = std::nullopt;
+            mAssociatedViewport = std::nullopt;
             // Find the display port that corresponds to the current input port.
             const std::string& inputPort = mIdentifier.location;
             if (!inputPort.empty()) {
@@ -161,19 +283,45 @@
                     mAssociatedDisplayPort = std::make_optional(displayPort->second);
                 }
             }
+
+            // If the device was explicitly disabled by the user, it would be present in the
+            // "disabledDevices" list. If it is associated with a specific display, and it was not
+            // explicitly disabled, then enable/disable the device based on whether we can find the
+            // corresponding viewport.
+            bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end());
+            if (mAssociatedDisplayPort) {
+                mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort);
+                if (!mAssociatedViewport) {
+                    ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
+                          "but the corresponding viewport is not found.",
+                          getName().c_str(), *mAssociatedDisplayPort);
+                    enabled = false;
+                }
+            }
+
+            if (changes) {
+                // For first-time configuration, only allow device to be disabled after mappers have
+                // finished configuring. This is because we need to read some of the properties from
+                // the device's open fd.
+                setEnabled(enabled, when);
+            }
         }
 
-        for (InputMapper* mapper : mMappers) {
-            mapper->configure(when, config, changes);
-            mSources |= mapper->getSources();
+        for_each_mapper([this, when, config, changes](InputMapper& mapper) {
+            mapper.configure(when, config, changes);
+            mSources |= mapper.getSources();
+        });
+
+        // If a device is just plugged but it might be disabled, we need to update some info like
+        // axis range of touch from each InputMapper first, then disable it.
+        if (!changes) {
+            setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when);
         }
     }
 }
 
 void InputDevice::reset(nsecs_t when) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->reset(when);
-    }
+    for_each_mapper([when](InputMapper& mapper) { mapper.reset(when); });
 
     mContext->updateGlobalMetaState();
 
@@ -208,32 +356,27 @@
             mDropUntilNextSync = true;
             reset(rawEvent->when);
         } else {
-            for (InputMapper* mapper : mMappers) {
-                mapper->process(rawEvent);
-            }
+            for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
+                mapper.process(rawEvent);
+            });
         }
         --count;
     }
 }
 
 void InputDevice::timeoutExpired(nsecs_t when) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->timeoutExpired(when);
-    }
+    for_each_mapper([when](InputMapper& mapper) { mapper.timeoutExpired(when); });
 }
 
 void InputDevice::updateExternalStylusState(const StylusState& state) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->updateExternalStylusState(state);
-    }
+    for_each_mapper([state](InputMapper& mapper) { mapper.updateExternalStylusState(state); });
 }
 
 void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
     outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal,
                               mHasMic);
-    for (InputMapper* mapper : mMappers) {
-        mapper->populateDeviceInfo(outDeviceInfo);
-    }
+    for_each_mapper(
+            [outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); });
 }
 
 int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
@@ -250,15 +393,20 @@
 
 int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
     int32_t result = AKEY_STATE_UNKNOWN;
-    for (InputMapper* mapper : mMappers) {
-        if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
-            // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
-            // value.  Otherwise, return AKEY_STATE_UP as long as one mapper reports it.
-            int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code);
-            if (currentResult >= AKEY_STATE_DOWN) {
-                return currentResult;
-            } else if (currentResult == AKEY_STATE_UP) {
-                result = currentResult;
+    for (auto& deviceEntry : mDevices) {
+        auto& devicePair = deviceEntry.second;
+        auto& mappers = devicePair.second;
+        for (auto& mapperPtr : mappers) {
+            InputMapper& mapper = *mapperPtr;
+            if (sourcesMatchMask(mapper.getSources(), sourceMask)) {
+                // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
+                // value.  Otherwise, return AKEY_STATE_UP as long as one mapper reports it.
+                int32_t currentResult = (mapper.*getStateFunc)(sourceMask, code);
+                if (currentResult >= AKEY_STATE_DOWN) {
+                    return currentResult;
+                } else if (currentResult == AKEY_STATE_UP) {
+                    result = currentResult;
+                }
             }
         }
     }
@@ -268,51 +416,37 @@
 bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
                                         const int32_t* keyCodes, uint8_t* outFlags) {
     bool result = false;
-    for (InputMapper* mapper : mMappers) {
-        if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
-            result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
+    for_each_mapper([&result, sourceMask, numCodes, keyCodes, outFlags](InputMapper& mapper) {
+        if (sourcesMatchMask(mapper.getSources(), sourceMask)) {
+            result |= mapper.markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
         }
-    }
+    });
     return result;
 }
 
 void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
                           int32_t token) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->vibrate(pattern, patternSize, repeat, token);
-    }
+    for_each_mapper([pattern, patternSize, repeat, token](InputMapper& mapper) {
+        mapper.vibrate(pattern, patternSize, repeat, token);
+    });
 }
 
 void InputDevice::cancelVibrate(int32_t token) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->cancelVibrate(token);
-    }
+    for_each_mapper([token](InputMapper& mapper) { mapper.cancelVibrate(token); });
 }
 
 void InputDevice::cancelTouch(nsecs_t when) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->cancelTouch(when);
-    }
+    for_each_mapper([when](InputMapper& mapper) { mapper.cancelTouch(when); });
 }
 
 int32_t InputDevice::getMetaState() {
     int32_t result = 0;
-    for (InputMapper* mapper : mMappers) {
-        result |= mapper->getMetaState();
-    }
+    for_each_mapper([&result](InputMapper& mapper) { result |= mapper.getMetaState(); });
     return result;
 }
 
 void InputDevice::updateMetaState(int32_t keyCode) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->updateMetaState(keyCode);
-    }
-}
-
-void InputDevice::fadePointer() {
-    for (InputMapper* mapper : mMappers) {
-        mapper->fadePointer();
-    }
+    for_each_mapper([keyCode](InputMapper& mapper) { mapper.updateMetaState(keyCode); });
 }
 
 void InputDevice::bumpGeneration() {
@@ -320,19 +454,39 @@
 }
 
 void InputDevice::notifyReset(nsecs_t when) {
-    NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId);
+    NotifyDeviceResetArgs args(mContext->getNextId(), when, mId);
     mContext->getListener()->notifyDeviceReset(&args);
 }
 
-std::optional<int32_t> InputDevice::getAssociatedDisplay() {
-    for (InputMapper* mapper : mMappers) {
-        std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplay();
-        if (associatedDisplayId) {
-            return associatedDisplayId;
-        }
+std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
+    // Check if we had associated to the specific display.
+    if (mAssociatedViewport) {
+        return mAssociatedViewport->displayId;
     }
 
-    return std::nullopt;
+    // No associated display port, check if some InputMapper is associated.
+    return first_in_mappers<int32_t>(
+            [](InputMapper& mapper) { return mapper.getAssociatedDisplayId(); });
 }
 
+// returns the number of mappers associated with the device
+size_t InputDevice::getMapperCount() {
+    size_t count = 0;
+    for (auto& deviceEntry : mDevices) {
+        auto& devicePair = deviceEntry.second;
+        auto& mappers = devicePair.second;
+        count += mappers.size();
+    }
+    return count;
+}
+
+InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId)
+      : mDevice(device),
+        mContext(device.getContext()),
+        mEventHub(device.getContext()->getEventHub()),
+        mId(eventHubId),
+        mDeviceId(device.getId()) {}
+
+InputDeviceContext::~InputDeviceContext() {}
+
 } // namespace android
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 27cbf19..06e3743 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -18,45 +18,37 @@
 
 #include "InputReader.h"
 
-#include "CursorInputMapper.h"
-#include "ExternalStylusInputMapper.h"
-#include "InputReaderContext.h"
-#include "JoystickInputMapper.h"
-#include "KeyboardInputMapper.h"
-#include "MultiTouchInputMapper.h"
-#include "RotaryEncoderInputMapper.h"
-#include "SingleTouchInputMapper.h"
-#include "SwitchInputMapper.h"
-#include "VibratorInputMapper.h"
-
+#include <android-base/stringprintf.h>
 #include <errno.h>
+#include <input/Keyboard.h>
+#include <input/VirtualKeyMap.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <log/log.h>
 #include <math.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <utils/Errors.h>
+#include <utils/Thread.h>
 
-#include <log/log.h>
-
-#include <android-base/stringprintf.h>
-#include <input/Keyboard.h>
-#include <input/VirtualKeyMap.h>
-
+#include "InputDevice.h"
 
 using android::base::StringPrintf;
 
 namespace android {
 
-InputReader::InputReader(const sp<EventHubInterface>& eventHub,
+// --- InputReader ---
+
+InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
                          const sp<InputReaderPolicyInterface>& policy,
                          const sp<InputListenerInterface>& listener)
       : mContext(this),
         mEventHub(eventHub),
         mPolicy(policy),
-        mNextSequenceNum(1),
         mGlobalMetaState(0),
         mGeneration(1),
+        mNextInputDeviceId(END_RESERVED_ID),
         mDisableVirtualKeysTimeout(LLONG_MIN),
         mNextTimeout(LLONG_MAX),
         mConfigurationChangesToRefresh(0) {
@@ -70,10 +62,24 @@
     } // release lock
 }
 
-InputReader::~InputReader() {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        delete mDevices.valueAt(i);
+InputReader::~InputReader() {}
+
+status_t InputReader::start() {
+    if (mThread) {
+        return ALREADY_EXISTS;
     }
+    mThread = std::make_unique<InputThread>(
+            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
+    return OK;
+}
+
+status_t InputReader::stop() {
+    if (mThread && mThread->isCallingThread()) {
+        ALOGE("InputReader cannot be stopped from its own thread!");
+        return INVALID_OPERATION;
+    }
+    mThread.reset();
+    return OK;
 }
 
 void InputReader::loopOnce() {
@@ -178,30 +184,28 @@
     }
 }
 
-void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
+void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) {
+    if (mDevices.find(eventHubId) != mDevices.end()) {
+        ALOGW("Ignoring spurious device added event for eventHubId %d.", eventHubId);
         return;
     }
 
-    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
-    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
-    int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
-
-    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
+    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(eventHubId);
+    std::shared_ptr<InputDevice> device = createDeviceLocked(eventHubId, identifier);
     device->configure(when, &mConfig, 0);
     device->reset(when);
 
     if (device->isIgnored()) {
-        ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
-              identifier.name.c_str());
+        ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s' "
+              "(ignored non-input device)",
+              device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str());
     } else {
-        ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, identifier.name.c_str(),
+        ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=0x%08x",
+              device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str(),
               device->getSources());
     }
 
-    mDevices.add(deviceId, device);
+    mDevices.emplace(eventHubId, device);
     bumpGenerationLocked();
 
     if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
@@ -209,119 +213,68 @@
     }
 }
 
-void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
-    InputDevice* device = nullptr;
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
-        ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
+void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) {
+    auto deviceIt = mDevices.find(eventHubId);
+    if (deviceIt == mDevices.end()) {
+        ALOGW("Ignoring spurious device removed event for eventHubId %d.", eventHubId);
         return;
     }
 
-    device = mDevices.valueAt(deviceIndex);
-    mDevices.removeItemsAt(deviceIndex, 1);
+    std::shared_ptr<InputDevice> device = std::move(deviceIt->second);
+    mDevices.erase(deviceIt);
     bumpGenerationLocked();
 
     if (device->isIgnored()) {
-        ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)", device->getId(),
-              device->getName().c_str());
+        ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s' "
+              "(ignored non-input device)",
+              device->getId(), eventHubId, device->getName().c_str(),
+              device->getDescriptor().c_str());
     } else {
-        ALOGI("Device removed: id=%d, name='%s', sources=0x%08x", device->getId(),
-              device->getName().c_str(), device->getSources());
+        ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s', sources=0x%08x",
+              device->getId(), eventHubId, device->getName().c_str(),
+              device->getDescriptor().c_str(), device->getSources());
     }
 
+    device->removeEventHubDevice(eventHubId);
+
     if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
         notifyExternalStylusPresenceChanged();
     }
 
+    if (device->hasEventHubDevices()) {
+        device->configure(when, &mConfig, 0);
+    }
     device->reset(when);
-    delete device;
 }
 
-InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
-                                             const InputDeviceIdentifier& identifier,
-                                             uint32_t classes) {
-    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
-                                          controllerNumber, identifier, classes);
+std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
+        int32_t eventHubId, const InputDeviceIdentifier& identifier) {
+    auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {
+        return devicePair.second->getDescriptor().size() && identifier.descriptor.size() &&
+                devicePair.second->getDescriptor() == identifier.descriptor;
+    });
 
-    // External devices.
-    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
-        device->setExternal(true);
+    std::shared_ptr<InputDevice> device;
+    if (deviceIt != mDevices.end()) {
+        device = deviceIt->second;
+    } else {
+        int32_t deviceId = (eventHubId < END_RESERVED_ID) ? eventHubId : nextInputDeviceIdLocked();
+        device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),
+                                               identifier);
     }
-
-    // Devices with mics.
-    if (classes & INPUT_DEVICE_CLASS_MIC) {
-        device->setMic(true);
-    }
-
-    // Switch-like devices.
-    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
-        device->addMapper(new SwitchInputMapper(device));
-    }
-
-    // Scroll wheel-like devices.
-    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
-        device->addMapper(new RotaryEncoderInputMapper(device));
-    }
-
-    // Vibrator-like devices.
-    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
-        device->addMapper(new VibratorInputMapper(device));
-    }
-
-    // Keyboard-like devices.
-    uint32_t keyboardSource = 0;
-    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
-    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
-        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
-    }
-    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
-        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
-    }
-    if (classes & INPUT_DEVICE_CLASS_DPAD) {
-        keyboardSource |= AINPUT_SOURCE_DPAD;
-    }
-    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
-        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
-    }
-
-    if (keyboardSource != 0) {
-        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
-    }
-
-    // Cursor-like devices.
-    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
-        device->addMapper(new CursorInputMapper(device));
-    }
-
-    // Touchscreens and touchpad devices.
-    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
-        device->addMapper(new MultiTouchInputMapper(device));
-    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
-        device->addMapper(new SingleTouchInputMapper(device));
-    }
-
-    // Joystick-like devices.
-    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
-        device->addMapper(new JoystickInputMapper(device));
-    }
-
-    // External stylus-like devices.
-    if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
-        device->addMapper(new ExternalStylusInputMapper(device));
-    }
-
+    device->addEventHubDevice(eventHubId);
     return device;
 }
 
-void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents,
+void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
                                                size_t count) {
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
-        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
+    auto deviceIt = mDevices.find(eventHubId);
+    if (deviceIt == mDevices.end()) {
+        ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
         return;
     }
 
-    InputDevice* device = mDevices.valueAt(deviceIndex);
+    std::shared_ptr<InputDevice>& device = deviceIt->second;
     if (device->isIgnored()) {
         // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
         return;
@@ -330,21 +283,36 @@
     device->process(rawEvents, count);
 }
 
+InputDevice* InputReader::findInputDevice(int32_t deviceId) {
+    auto deviceIt =
+            std::find_if(mDevices.begin(), mDevices.end(), [deviceId](const auto& devicePair) {
+                return devicePair.second->getId() == deviceId;
+            });
+    if (deviceIt != mDevices.end()) {
+        return deviceIt->second.get();
+    }
+    return nullptr;
+}
+
 void InputReader::timeoutExpiredLocked(nsecs_t when) {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        std::shared_ptr<InputDevice>& device = devicePair.second;
         if (!device->isIgnored()) {
             device->timeoutExpired(when);
         }
     }
 }
 
+int32_t InputReader::nextInputDeviceIdLocked() {
+    return ++mNextInputDeviceId;
+}
+
 void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
     // Reset global meta state because it depends on the list of all configured devices.
     updateGlobalMetaStateLocked();
 
     // Enqueue configuration changed.
-    NotifyConfigurationChangedArgs args(mContext.getNextSequenceNum(), when);
+    NotifyConfigurationChangedArgs args(mContext.getNextId(), when);
     mQueuedListener->notifyConfigurationChanged(&args);
 }
 
@@ -353,14 +321,19 @@
     mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
 
     if (changes) {
-        ALOGI("Reconfiguring input devices.  changes=0x%08x", changes);
+        ALOGI("Reconfiguring input devices, changes=%s",
+              InputReaderConfiguration::changesToString(changes).c_str());
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
 
+        if (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) {
+            updatePointerDisplayLocked();
+        }
+
         if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
             mEventHub->requestReopenDevices();
         } else {
-            for (size_t i = 0; i < mDevices.size(); i++) {
-                InputDevice* device = mDevices.valueAt(i);
+            for (auto& devicePair : mDevices) {
+                std::shared_ptr<InputDevice>& device = devicePair.second;
                 device->configure(now, &mConfig, changes);
             }
         }
@@ -370,8 +343,8 @@
 void InputReader::updateGlobalMetaStateLocked() {
     mGlobalMetaState = 0;
 
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        std::shared_ptr<InputDevice>& device = devicePair.second;
         mGlobalMetaState |= device->getMetaState();
     }
 }
@@ -385,8 +358,8 @@
 }
 
 void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        std::shared_ptr<InputDevice>& device = devicePair.second;
         if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
             InputDeviceInfo info;
             device->getDeviceInfo(&info);
@@ -396,8 +369,8 @@
 }
 
 void InputReader::dispatchExternalStylusState(const StylusState& state) {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        std::shared_ptr<InputDevice>& device = devicePair.second;
         device->updateExternalStylusState(state);
     }
 }
@@ -406,23 +379,55 @@
     mDisableVirtualKeysTimeout = time;
 }
 
-bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, int32_t keyCode,
-                                             int32_t scanCode) {
+bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, int32_t keyCode, int32_t scanCode) {
     if (now < mDisableVirtualKeysTimeout) {
-        ALOGI("Dropping virtual key from device %s because virtual keys are "
+        ALOGI("Dropping virtual key from device because virtual keys are "
               "temporarily disabled for the next %0.3fms.  keyCode=%d, scanCode=%d",
-              device->getName().c_str(), (mDisableVirtualKeysTimeout - now) * 0.000001, keyCode,
-              scanCode);
+              (mDisableVirtualKeysTimeout - now) * 0.000001, keyCode, scanCode);
         return true;
     } else {
         return false;
     }
 }
 
+std::shared_ptr<PointerControllerInterface> InputReader::getPointerControllerLocked(
+        int32_t deviceId) {
+    std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
+    if (controller == nullptr) {
+        controller = mPolicy->obtainPointerController(deviceId);
+        mPointerController = controller;
+        updatePointerDisplayLocked();
+    }
+    return controller;
+}
+
+void InputReader::updatePointerDisplayLocked() {
+    std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
+    if (controller == nullptr) {
+        return;
+    }
+
+    std::optional<DisplayViewport> viewport =
+            mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
+    if (!viewport) {
+        ALOGW("Can't find the designated viewport with ID %" PRId32 " to update cursor input "
+              "mapper. Fall back to default display",
+              mConfig.defaultPointerDisplayId);
+        viewport = mConfig.getDisplayViewportById(ADISPLAY_ID_DEFAULT);
+    }
+    if (!viewport) {
+        ALOGE("Still can't find a viable viewport to update cursor input mapper. Skip setting it to"
+              " PointerController.");
+        return;
+    }
+
+    controller->setDisplayViewport(*viewport);
+}
+
 void InputReader::fadePointerLocked() {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
-        device->fadePointer();
+    std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
+    if (controller != nullptr) {
+        controller->fade(PointerControllerInterface::Transition::GRADUAL);
     }
 }
 
@@ -445,9 +450,8 @@
 void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) {
     outInputDevices.clear();
 
-    size_t numDevices = mDevices.size();
-    for (size_t i = 0; i < numDevices; i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        std::shared_ptr<InputDevice>& device = devicePair.second;
         if (!device->isIgnored()) {
             InputDeviceInfo info;
             device->getDeviceInfo(&info);
@@ -478,21 +482,17 @@
                                     GetStateFunc getStateFunc) {
     int32_t result = AKEY_STATE_UNKNOWN;
     if (deviceId >= 0) {
-        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-        if (deviceIndex >= 0) {
-            InputDevice* device = mDevices.valueAt(deviceIndex);
-            if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
-                result = (device->*getStateFunc)(sourceMask, code);
-            }
+        InputDevice* device = findInputDevice(deviceId);
+        if (device && !device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+            result = (device->*getStateFunc)(sourceMask, code);
         }
     } else {
-        size_t numDevices = mDevices.size();
-        for (size_t i = 0; i < numDevices; i++) {
-            InputDevice* device = mDevices.valueAt(i);
+        for (auto& devicePair : mDevices) {
+            std::shared_ptr<InputDevice>& device = devicePair.second;
             if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
                 // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
                 // value.  Otherwise, return AKEY_STATE_UP as long as one device reports it.
-                int32_t currentResult = (device->*getStateFunc)(sourceMask, code);
+                int32_t currentResult = (device.get()->*getStateFunc)(sourceMask, code);
                 if (currentResult >= AKEY_STATE_DOWN) {
                     return currentResult;
                 } else if (currentResult == AKEY_STATE_UP) {
@@ -505,13 +505,12 @@
 }
 
 void InputReader::toggleCapsLockState(int32_t deviceId) {
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
+    InputDevice* device = findInputDevice(deviceId);
+    if (!device) {
         ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
         return;
     }
 
-    InputDevice* device = mDevices.valueAt(deviceIndex);
     if (device->isIgnored()) {
         return;
     }
@@ -532,17 +531,13 @@
                                               uint8_t* outFlags) {
     bool result = false;
     if (deviceId >= 0) {
-        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-        if (deviceIndex >= 0) {
-            InputDevice* device = mDevices.valueAt(deviceIndex);
-            if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
-                result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
-            }
+        InputDevice* device = findInputDevice(deviceId);
+        if (device && !device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
+            result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
         }
     } else {
-        size_t numDevices = mDevices.size();
-        for (size_t i = 0; i < numDevices; i++) {
-            InputDevice* device = mDevices.valueAt(i);
+        for (auto& devicePair : mDevices) {
+            std::shared_ptr<InputDevice>& device = devicePair.second;
             if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
                 result |= device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
             }
@@ -567,10 +562,8 @@
 void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
                           ssize_t repeat, int32_t token) {
     AutoMutex _l(mLock);
-
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        InputDevice* device = mDevices.valueAt(deviceIndex);
+    InputDevice* device = findInputDevice(deviceId);
+    if (device) {
         device->vibrate(pattern, patternSize, repeat, token);
     }
 }
@@ -578,9 +571,8 @@
 void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
     AutoMutex _l(mLock);
 
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        InputDevice* device = mDevices.valueAt(deviceIndex);
+    InputDevice* device = findInputDevice(deviceId);
+    if (device) {
         device->cancelVibrate(token);
     }
 }
@@ -588,9 +580,8 @@
 bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
     AutoMutex _l(mLock);
 
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        InputDevice* device = mDevices.valueAt(deviceIndex);
+    InputDevice* device = findInputDevice(deviceId);
+    if (device) {
         return device->isEnabled();
     }
     ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
@@ -600,21 +591,25 @@
 bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
     AutoMutex _l(mLock);
 
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
+    InputDevice* device = findInputDevice(deviceId);
+    if (!device) {
         ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
         return false;
     }
 
-    InputDevice* device = mDevices.valueAt(deviceIndex);
-    std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplay();
+    if (!device->isEnabled()) {
+        ALOGW("Ignoring disabled device %s", device->getName().c_str());
+        return false;
+    }
+
+    std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplayId();
     // No associated display. By default, can dispatch to all displays.
     if (!associatedDisplayId) {
         return true;
     }
 
     if (*associatedDisplayId == ADISPLAY_ID_NONE) {
-        ALOGW("Device has associated, but no associated display id.");
+        ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str());
         return true;
     }
 
@@ -629,8 +624,9 @@
 
     dump += "Input Reader State:\n";
 
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        mDevices.valueAt(i)->dump(dump);
+    for (const auto& devicePair : mDevices) {
+        const std::shared_ptr<InputDevice>& device = devicePair.second;
+        device->dump(dump);
     }
 
     dump += INDENT "Configuration:\n";
@@ -701,7 +697,8 @@
 
 // --- InputReader::ContextImpl ---
 
-InputReader::ContextImpl::ContextImpl(InputReader* reader) : mReader(reader) {}
+InputReader::ContextImpl::ContextImpl(InputReader* reader)
+      : mReader(reader), mIdGenerator(IdGenerator::Source::INPUT_READER) {}
 
 void InputReader::ContextImpl::updateGlobalMetaState() {
     // lock is already held by the input loop
@@ -718,10 +715,10 @@
     mReader->disableVirtualKeysUntilLocked(time);
 }
 
-bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now, InputDevice* device,
-                                                    int32_t keyCode, int32_t scanCode) {
+bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now, int32_t keyCode,
+                                                    int32_t scanCode) {
     // lock is already held by the input loop
-    return mReader->shouldDropVirtualKeyLocked(now, device, keyCode, scanCode);
+    return mReader->shouldDropVirtualKeyLocked(now, keyCode, scanCode);
 }
 
 void InputReader::ContextImpl::fadePointer() {
@@ -729,6 +726,12 @@
     mReader->fadePointerLocked();
 }
 
+std::shared_ptr<PointerControllerInterface> InputReader::ContextImpl::getPointerController(
+        int32_t deviceId) {
+    // lock is already held by the input loop
+    return mReader->getPointerControllerLocked(deviceId);
+}
+
 void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) {
     // lock is already held by the input loop
     mReader->requestTimeoutAtTimeLocked(when);
@@ -760,8 +763,8 @@
     return mReader->mEventHub.get();
 }
 
-uint32_t InputReader::ContextImpl::getNextSequenceNum() {
-    return (mReader->mNextSequenceNum)++;
+int32_t InputReader::ContextImpl::getNextId() {
+    return mIdGenerator.nextId();
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/InputReaderFactory.cpp b/services/inputflinger/reader/InputReaderFactory.cpp
index 9f73680..a897141 100644
--- a/services/inputflinger/reader/InputReaderFactory.cpp
+++ b/services/inputflinger/reader/InputReaderFactory.cpp
@@ -22,7 +22,7 @@
 
 sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
                                            const sp<InputListenerInterface>& listener) {
-    return new InputReader(new EventHub(), policy, listener);
+    return new InputReader(std::make_unique<EventHub>(), policy, listener);
 }
 
 } // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 794396a..c17f3a1 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -145,12 +145,11 @@
  * which keys are currently down.  Finally, the event hub keeps track of the capabilities of
  * individual input devices, such as their class and the set of key codes that they support.
  */
-class EventHubInterface : public virtual RefBase {
-protected:
+class EventHubInterface {
+public:
     EventHubInterface() {}
     virtual ~EventHubInterface() {}
 
-public:
     // Synthetic raw event type codes produced when devices are added or removed.
     enum {
         // Sent when a device is added.
@@ -261,62 +260,64 @@
 public:
     EventHub();
 
-    virtual uint32_t getDeviceClasses(int32_t deviceId) const;
+    virtual uint32_t getDeviceClasses(int32_t deviceId) const override;
 
-    virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const;
+    virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override;
 
-    virtual int32_t getDeviceControllerNumber(int32_t deviceId) const;
+    virtual int32_t getDeviceControllerNumber(int32_t deviceId) const override;
 
-    virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const;
+    virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override;
 
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
-                                         RawAbsoluteAxisInfo* outAxisInfo) const;
+                                         RawAbsoluteAxisInfo* outAxisInfo) const override;
 
-    virtual bool hasRelativeAxis(int32_t deviceId, int axis) const;
+    virtual bool hasRelativeAxis(int32_t deviceId, int axis) const override;
 
-    virtual bool hasInputProperty(int32_t deviceId, int property) const;
+    virtual bool hasInputProperty(int32_t deviceId, int property) const override;
 
     virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
                             int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
-                            uint32_t* outFlags) const;
+                            uint32_t* outFlags) const override;
 
-    virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const;
+    virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
+                             AxisInfo* outAxisInfo) const override;
 
-    virtual void setExcludedDevices(const std::vector<std::string>& devices);
+    virtual void setExcludedDevices(const std::vector<std::string>& devices) override;
 
-    virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
-    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
-    virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const;
-    virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const;
+    virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override;
+    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override;
+    virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const override;
+    virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+                                          int32_t* outValue) const override;
 
     virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
-                                       uint8_t* outFlags) const;
+                                       uint8_t* outFlags) const override;
 
-    virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize);
-    virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId);
+    virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override;
+    virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override;
 
-    virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const;
-    virtual bool hasLed(int32_t deviceId, int32_t led) const;
-    virtual void setLedState(int32_t deviceId, int32_t led, bool on);
+    virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const override;
+    virtual bool hasLed(int32_t deviceId, int32_t led) const override;
+    virtual void setLedState(int32_t deviceId, int32_t led, bool on) override;
 
-    virtual void getVirtualKeyDefinitions(int32_t deviceId,
-                                          std::vector<VirtualKeyDefinition>& outVirtualKeys) const;
+    virtual void getVirtualKeyDefinitions(
+            int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override;
 
-    virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
-    virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map);
+    virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override;
+    virtual bool setKeyboardLayoutOverlay(int32_t deviceId,
+                                          const sp<KeyCharacterMap>& map) override;
 
-    virtual void vibrate(int32_t deviceId, nsecs_t duration);
-    virtual void cancelVibrate(int32_t deviceId);
+    virtual void vibrate(int32_t deviceId, nsecs_t duration) override;
+    virtual void cancelVibrate(int32_t deviceId) override;
 
-    virtual void requestReopenDevices();
+    virtual void requestReopenDevices() override;
 
-    virtual void wake();
+    virtual void wake() override;
 
-    virtual void dump(std::string& dump);
-    virtual void monitor();
+    virtual void dump(std::string& dump) override;
+    virtual void monitor() override;
 
-protected:
-    virtual ~EventHub();
+    virtual ~EventHub() override;
 
 private:
     struct Device {
@@ -385,9 +386,9 @@
 
     void configureFd(Device* device);
 
-    bool isDeviceEnabled(int32_t deviceId);
-    status_t enableDevice(int32_t deviceId);
-    status_t disableDevice(int32_t deviceId);
+    bool isDeviceEnabled(int32_t deviceId) override;
+    status_t enableDevice(int32_t deviceId) override;
+    status_t disableDevice(int32_t deviceId) override;
     status_t registerFdForEpoll(int fd);
     status_t unregisterFdFromEpoll(int fd);
     status_t registerDeviceForEpollLocked(Device* device);
@@ -475,8 +476,6 @@
     size_t mPendingEventCount;
     size_t mPendingEventIndex;
     bool mPendingINotify;
-
-    bool mUsingEpollWakeup;
 };
 
 }; // namespace android
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 57f0b31..71313fc 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -17,28 +17,29 @@
 #ifndef _UI_INPUTREADER_INPUT_DEVICE_H
 #define _UI_INPUTREADER_INPUT_DEVICE_H
 
+#include <input/DisplayViewport.h>
+#include <input/InputDevice.h>
+#include <stdint.h>
+#include <utils/PropertyMap.h>
+
+#include <optional>
+#include <unordered_map>
+#include <vector>
+
 #include "EventHub.h"
 #include "InputReaderBase.h"
 #include "InputReaderContext.h"
 
-#include <input/DisplayViewport.h>
-#include <input/InputDevice.h>
-#include <utils/PropertyMap.h>
-
-#include <stdint.h>
-#include <optional>
-#include <vector>
-
 namespace android {
 
+class InputDeviceContext;
 class InputMapper;
 
 /* Represents the state of a single input device. */
 class InputDevice {
 public:
     InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
-                int32_t controllerNumber, const InputDeviceIdentifier& identifier,
-                uint32_t classes);
+                const InputDeviceIdentifier& identifier);
     ~InputDevice();
 
     inline InputReaderContext* getContext() { return mContext; }
@@ -49,23 +50,25 @@
     inline const std::string getDescriptor() { return mIdentifier.descriptor; }
     inline uint32_t getClasses() const { return mClasses; }
     inline uint32_t getSources() const { return mSources; }
+    inline bool hasEventHubDevices() const { return !mDevices.empty(); }
 
     inline bool isExternal() { return mIsExternal; }
-    inline void setExternal(bool external) { mIsExternal = external; }
     inline std::optional<uint8_t> getAssociatedDisplayPort() const {
         return mAssociatedDisplayPort;
     }
-
-    inline void setMic(bool hasMic) { mHasMic = hasMic; }
+    inline std::optional<DisplayViewport> getAssociatedViewport() const {
+        return mAssociatedViewport;
+    }
     inline bool hasMic() const { return mHasMic; }
 
-    inline bool isIgnored() { return mMappers.empty(); }
+    inline bool isIgnored() { return !getMapperCount(); }
 
     bool isEnabled();
     void setEnabled(bool enabled, nsecs_t when);
 
     void dump(std::string& dump);
-    void addMapper(InputMapper* mapper);
+    void addEventHubDevice(int32_t eventHubId, bool populateMappers = true);
+    void removeEventHubDevice(int32_t eventHubId);
     void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
     void reset(nsecs_t when);
     void process(const RawEvent* rawEvents, size_t count);
@@ -85,8 +88,6 @@
     int32_t getMetaState();
     void updateMetaState(int32_t keyCode);
 
-    void fadePointer();
-
     void bumpGeneration();
 
     void notifyReset(nsecs_t when);
@@ -94,26 +95,25 @@
     inline const PropertyMap& getConfiguration() { return mConfiguration; }
     inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
 
-    bool hasKey(int32_t code) { return getEventHub()->hasScanCode(mId, code); }
+    std::optional<int32_t> getAssociatedDisplayId();
 
-    bool hasAbsoluteAxis(int32_t code) {
-        RawAbsoluteAxisInfo info;
-        getEventHub()->getAbsoluteAxisInfo(mId, code, &info);
-        return info.valid;
+    size_t getMapperCount();
+
+    // construct and add a mapper to the input device
+    template <class T, typename... Args>
+    T& addMapper(int32_t eventHubId, Args... args) {
+        // ensure a device entry exists for this eventHubId
+        addEventHubDevice(eventHubId, false);
+
+        // create mapper
+        auto& devicePair = mDevices[eventHubId];
+        auto& deviceContext = devicePair.first;
+        auto& mappers = devicePair.second;
+        T* mapper = new T(*deviceContext, args...);
+        mappers.emplace_back(mapper);
+        return *mapper;
     }
 
-    bool isKeyPressed(int32_t code) {
-        return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN;
-    }
-
-    int32_t getAbsoluteAxisValue(int32_t code) {
-        int32_t value;
-        getEventHub()->getAbsoluteAxisValue(mId, code, &value);
-        return value;
-    }
-
-    std::optional<int32_t> getAssociatedDisplay();
-
 private:
     InputReaderContext* mContext;
     int32_t mId;
@@ -123,11 +123,15 @@
     std::string mAlias;
     uint32_t mClasses;
 
-    std::vector<InputMapper*> mMappers;
+    // map from eventHubId to device context and mappers
+    using MapperVector = std::vector<std::unique_ptr<InputMapper>>;
+    using DevicePair = std::pair<std::unique_ptr<InputDeviceContext>, MapperVector>;
+    std::unordered_map<int32_t, DevicePair> mDevices;
 
     uint32_t mSources;
     bool mIsExternal;
     std::optional<uint8_t> mAssociatedDisplayPort;
+    std::optional<DisplayViewport> mAssociatedViewport;
     bool mHasMic;
     bool mDropUntilNextSync;
 
@@ -135,6 +139,168 @@
     int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
 
     PropertyMap mConfiguration;
+
+    // helpers to interate over the devices collection
+    // run a function against every mapper on every subdevice
+    inline void for_each_mapper(std::function<void(InputMapper&)> f) {
+        for (auto& deviceEntry : mDevices) {
+            auto& devicePair = deviceEntry.second;
+            auto& mappers = devicePair.second;
+            for (auto& mapperPtr : mappers) {
+                f(*mapperPtr);
+            }
+        }
+    }
+
+    // run a function against every mapper on a specific subdevice
+    inline void for_each_mapper_in_subdevice(int32_t eventHubDevice,
+                                             std::function<void(InputMapper&)> f) {
+        auto deviceIt = mDevices.find(eventHubDevice);
+        if (deviceIt != mDevices.end()) {
+            auto& devicePair = deviceIt->second;
+            auto& mappers = devicePair.second;
+            for (auto& mapperPtr : mappers) {
+                f(*mapperPtr);
+            }
+        }
+    }
+
+    // run a function against every subdevice
+    inline void for_each_subdevice(std::function<void(InputDeviceContext&)> f) {
+        for (auto& deviceEntry : mDevices) {
+            auto& devicePair = deviceEntry.second;
+            auto& contextPtr = devicePair.first;
+            f(*contextPtr);
+        }
+    }
+
+    // return the first value returned by a function over every mapper.
+    // if all mappers return nullopt, return nullopt.
+    template <typename T>
+    inline std::optional<T> first_in_mappers(std::function<std::optional<T>(InputMapper&)> f) {
+        for (auto& deviceEntry : mDevices) {
+            auto& devicePair = deviceEntry.second;
+            auto& mappers = devicePair.second;
+            for (auto& mapperPtr : mappers) {
+                std::optional<T> ret = f(*mapperPtr);
+                if (ret) {
+                    return ret;
+                }
+            }
+        }
+        return std::nullopt;
+    }
+};
+
+/* Provides access to EventHub methods, but limits access to the current InputDevice.
+ * Essentially an implementation of EventHubInterface, but for a specific device id.
+ * Helps hide implementation details of InputDevice and EventHub. Used by mappers to
+ * check the status of the associated hardware device
+ */
+class InputDeviceContext {
+public:
+    InputDeviceContext(InputDevice& device, int32_t eventHubId);
+    ~InputDeviceContext();
+
+    inline InputReaderContext* getContext() { return mContext; }
+    inline int32_t getId() { return mDeviceId; }
+    inline int32_t getEventHubId() { return mId; }
+
+    inline uint32_t getDeviceClasses() const { return mEventHub->getDeviceClasses(mId); }
+    inline InputDeviceIdentifier getDeviceIdentifier() const {
+        return mEventHub->getDeviceIdentifier(mId);
+    }
+    inline int32_t getDeviceControllerNumber() const {
+        return mEventHub->getDeviceControllerNumber(mId);
+    }
+    inline void getConfiguration(PropertyMap* outConfiguration) const {
+        return mEventHub->getConfiguration(mId, outConfiguration);
+    }
+    inline status_t getAbsoluteAxisInfo(int32_t code, RawAbsoluteAxisInfo* axisInfo) const {
+        return mEventHub->getAbsoluteAxisInfo(mId, code, axisInfo);
+    }
+    inline bool hasRelativeAxis(int32_t code) const {
+        return mEventHub->hasRelativeAxis(mId, code);
+    }
+    inline bool hasInputProperty(int property) const {
+        return mEventHub->hasInputProperty(mId, property);
+    }
+    inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState,
+                           int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const {
+        return mEventHub->mapKey(mId, scanCode, usageCode, metaState, outKeycode, outMetaState,
+                                 outFlags);
+    }
+    inline status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const {
+        return mEventHub->mapAxis(mId, scanCode, outAxisInfo);
+    }
+    inline std::vector<TouchVideoFrame> getVideoFrames() { return mEventHub->getVideoFrames(mId); }
+    inline int32_t getScanCodeState(int32_t scanCode) const {
+        return mEventHub->getScanCodeState(mId, scanCode);
+    }
+    inline int32_t getKeyCodeState(int32_t keyCode) const {
+        return mEventHub->getKeyCodeState(mId, keyCode);
+    }
+    inline int32_t getSwitchState(int32_t sw) const { return mEventHub->getSwitchState(mId, sw); }
+    inline status_t getAbsoluteAxisValue(int32_t code, int32_t* outValue) const {
+        return mEventHub->getAbsoluteAxisValue(mId, code, outValue);
+    }
+    inline bool markSupportedKeyCodes(size_t numCodes, const int32_t* keyCodes,
+                                      uint8_t* outFlags) const {
+        return mEventHub->markSupportedKeyCodes(mId, numCodes, keyCodes, outFlags);
+    }
+    inline bool hasScanCode(int32_t scanCode) const {
+        return mEventHub->hasScanCode(mId, scanCode);
+    }
+    inline bool hasLed(int32_t led) const { return mEventHub->hasLed(mId, led); }
+    inline void setLedState(int32_t led, bool on) { return mEventHub->setLedState(mId, led, on); }
+    inline void getVirtualKeyDefinitions(std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
+        return mEventHub->getVirtualKeyDefinitions(mId, outVirtualKeys);
+    }
+    inline sp<KeyCharacterMap> getKeyCharacterMap() const {
+        return mEventHub->getKeyCharacterMap(mId);
+    }
+    inline bool setKeyboardLayoutOverlay(const sp<KeyCharacterMap>& map) {
+        return mEventHub->setKeyboardLayoutOverlay(mId, map);
+    }
+    inline void vibrate(nsecs_t duration) { return mEventHub->vibrate(mId, duration); }
+    inline void cancelVibrate() { return mEventHub->cancelVibrate(mId); }
+
+    inline bool hasAbsoluteAxis(int32_t code) const {
+        RawAbsoluteAxisInfo info;
+        mEventHub->getAbsoluteAxisInfo(mId, code, &info);
+        return info.valid;
+    }
+    inline bool isKeyPressed(int32_t code) const {
+        return mEventHub->getScanCodeState(mId, code) == AKEY_STATE_DOWN;
+    }
+    inline int32_t getAbsoluteAxisValue(int32_t code) const {
+        int32_t value;
+        mEventHub->getAbsoluteAxisValue(mId, code, &value);
+        return value;
+    }
+    inline bool isDeviceEnabled() { return mEventHub->isDeviceEnabled(mId); }
+    inline status_t enableDevice() { return mEventHub->enableDevice(mId); }
+    inline status_t disableDevice() { return mEventHub->disableDevice(mId); }
+
+    inline const std::string getName() { return mDevice.getName(); }
+    inline const std::string getDescriptor() { return mDevice.getDescriptor(); }
+    inline bool isExternal() { return mDevice.isExternal(); }
+    inline std::optional<uint8_t> getAssociatedDisplayPort() const {
+        return mDevice.getAssociatedDisplayPort();
+    }
+    inline std::optional<DisplayViewport> getAssociatedViewport() const {
+        return mDevice.getAssociatedViewport();
+    }
+    inline void cancelTouch(nsecs_t when) { mDevice.cancelTouch(when); }
+    inline void bumpGeneration() { mDevice.bumpGeneration(); }
+    inline const PropertyMap& getConfiguration() { return mDevice.getConfiguration(); }
+
+private:
+    InputDevice& mDevice;
+    InputReaderContext* mContext;
+    EventHubInterface* mEventHub;
+    int32_t mId;
+    int32_t mDeviceId;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index e29c8f2..108b9c2 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -17,18 +17,19 @@
 #ifndef _UI_INPUTREADER_INPUT_READER_H
 #define _UI_INPUTREADER_INPUT_READER_H
 
+#include <PointerControllerInterface.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
 #include "EventHub.h"
 #include "InputListener.h"
 #include "InputReaderBase.h"
 #include "InputReaderContext.h"
-
-#include <utils/BitSet.h>
-#include <utils/Condition.h>
-#include <utils/KeyedVector.h>
-#include <utils/Mutex.h>
-#include <utils/Timers.h>
-
-#include <vector>
+#include "InputThread.h"
 
 namespace android {
 
@@ -40,100 +41,114 @@
  * that it sends to the input listener.  Some functions of the input reader, such as early
  * event filtering in low power states, are controlled by a separate policy object.
  *
- * The InputReader owns a collection of InputMappers.  Most of the work it does happens
- * on the input reader thread but the InputReader can receive queries from other system
+ * The InputReader owns a collection of InputMappers. InputReader starts its own thread, where
+ * most of the work happens, but the InputReader can receive queries from other system
  * components running on arbitrary threads.  To keep things manageable, the InputReader
  * uses a single Mutex to guard its state.  The Mutex may be held while calling into the
  * EventHub or the InputReaderPolicy but it is never held while calling into the
- * InputListener.
+ * InputListener. All calls to InputListener must happen from InputReader's thread.
  */
 class InputReader : public InputReaderInterface {
 public:
-    InputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy,
+    InputReader(std::shared_ptr<EventHubInterface> eventHub,
+                const sp<InputReaderPolicyInterface>& policy,
                 const sp<InputListenerInterface>& listener);
     virtual ~InputReader();
 
-    virtual void dump(std::string& dump);
-    virtual void monitor();
+    virtual void dump(std::string& dump) override;
+    virtual void monitor() override;
 
-    virtual void loopOnce();
+    virtual status_t start() override;
+    virtual status_t stop() override;
 
-    virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices);
+    virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
 
-    virtual bool isInputDeviceEnabled(int32_t deviceId);
+    virtual bool isInputDeviceEnabled(int32_t deviceId) override;
 
-    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode);
-    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode);
-    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw);
+    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+                                     int32_t scanCode) override;
+    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+                                    int32_t keyCode) override;
+    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
 
-    virtual void toggleCapsLockState(int32_t deviceId);
+    virtual void toggleCapsLockState(int32_t deviceId) override;
 
     virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
-                         const int32_t* keyCodes, uint8_t* outFlags);
+                         const int32_t* keyCodes, uint8_t* outFlags) override;
 
-    virtual void requestRefreshConfiguration(uint32_t changes);
+    virtual void requestRefreshConfiguration(uint32_t changes) override;
 
     virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
-                         ssize_t repeat, int32_t token);
-    virtual void cancelVibrate(int32_t deviceId, int32_t token);
+                         ssize_t repeat, int32_t token) override;
+    virtual void cancelVibrate(int32_t deviceId, int32_t token) override;
 
-    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId);
+    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
+
 protected:
     // These members are protected so they can be instrumented by test cases.
-    virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
-                                            const InputDeviceIdentifier& identifier,
-                                            uint32_t classes);
+    virtual std::shared_ptr<InputDevice> createDeviceLocked(
+            int32_t deviceId, const InputDeviceIdentifier& identifier);
+
+    // With each iteration of the loop, InputReader reads and processes one incoming message from
+    // the EventHub.
+    void loopOnce();
 
     class ContextImpl : public InputReaderContext {
         InputReader* mReader;
+        IdGenerator mIdGenerator;
 
     public:
         explicit ContextImpl(InputReader* reader);
 
-        virtual void updateGlobalMetaState();
-        virtual int32_t getGlobalMetaState();
-        virtual void disableVirtualKeysUntil(nsecs_t time);
-        virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode,
-                                          int32_t scanCode);
-        virtual void fadePointer();
-        virtual void requestTimeoutAtTime(nsecs_t when);
-        virtual int32_t bumpGeneration();
-        virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices);
-        virtual void dispatchExternalStylusState(const StylusState& outState);
-        virtual InputReaderPolicyInterface* getPolicy();
-        virtual InputListenerInterface* getListener();
-        virtual EventHubInterface* getEventHub();
-        virtual uint32_t getNextSequenceNum();
+        virtual void updateGlobalMetaState() override;
+        virtual int32_t getGlobalMetaState() override;
+        virtual void disableVirtualKeysUntil(nsecs_t time) override;
+        virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override;
+        virtual void fadePointer() override;
+        virtual std::shared_ptr<PointerControllerInterface> getPointerController(
+                int32_t deviceId) override;
+        virtual void requestTimeoutAtTime(nsecs_t when) override;
+        virtual int32_t bumpGeneration() override;
+        virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override;
+        virtual void dispatchExternalStylusState(const StylusState& outState) override;
+        virtual InputReaderPolicyInterface* getPolicy() override;
+        virtual InputListenerInterface* getListener() override;
+        virtual EventHubInterface* getEventHub() override;
+        virtual int32_t getNextId() override;
     } mContext;
 
     friend class ContextImpl;
 
 private:
+    std::unique_ptr<InputThread> mThread;
+
     Mutex mLock;
 
     Condition mReaderIsAliveCondition;
 
-    sp<EventHubInterface> mEventHub;
+    // This could be unique_ptr, but due to the way InputReader tests are written,
+    // it is made shared_ptr here. In the tests, an EventHub reference is retained by the test
+    // in parallel to passing it to the InputReader.
+    std::shared_ptr<EventHubInterface> mEventHub;
     sp<InputReaderPolicyInterface> mPolicy;
     sp<QueuedInputListener> mQueuedListener;
 
     InputReaderConfiguration mConfig;
 
-    // used by InputReaderContext::getNextSequenceNum() as a counter for event sequence numbers
-    uint32_t mNextSequenceNum;
-
     // The event queue.
     static const int EVENT_BUFFER_SIZE = 256;
     RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
 
-    KeyedVector<int32_t, InputDevice*> mDevices;
+    // An input device can represent a collection of EventHub devices. This map provides a way
+    // to lookup the input device instance from the EventHub device id.
+    std::unordered_map<int32_t /*eventHubId*/, std::shared_ptr<InputDevice>> mDevices;
 
     // low-level input event decoding and device management
     void processEventsLocked(const RawEvent* rawEvents, size_t count);
 
-    void addDeviceLocked(nsecs_t when, int32_t deviceId);
-    void removeDeviceLocked(nsecs_t when, int32_t deviceId);
-    void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count);
+    void addDeviceLocked(nsecs_t when, int32_t eventHubId);
+    void removeDeviceLocked(nsecs_t when, int32_t eventHubId);
+    void processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents, size_t count);
     void timeoutExpiredLocked(nsecs_t when);
 
     void handleConfigurationChangedLocked(nsecs_t when);
@@ -146,17 +161,23 @@
     void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices);
     void dispatchExternalStylusState(const StylusState& state);
 
+    // The PointerController that is shared among all the input devices that need it.
+    std::weak_ptr<PointerControllerInterface> mPointerController;
+    std::shared_ptr<PointerControllerInterface> getPointerControllerLocked(int32_t deviceId);
+    void updatePointerDisplayLocked();
     void fadePointerLocked();
 
     int32_t mGeneration;
     int32_t bumpGenerationLocked();
 
+    int32_t mNextInputDeviceId;
+    int32_t nextInputDeviceIdLocked();
+
     void getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices);
 
     nsecs_t mDisableVirtualKeysTimeout;
     void disableVirtualKeysUntilLocked(nsecs_t time);
-    bool shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, int32_t keyCode,
-                                    int32_t scanCode);
+    bool shouldDropVirtualKeyLocked(nsecs_t now, int32_t keyCode, int32_t scanCode);
 
     nsecs_t mNextTimeout;
     void requestTimeoutAtTimeLocked(nsecs_t when);
@@ -170,6 +191,9 @@
                            GetStateFunc getStateFunc);
     bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
                                      const int32_t* keyCodes, uint8_t* outFlags);
+
+    // find an InputDevice from an InputDevice id
+    InputDevice* findInputDevice(int32_t deviceId);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index 3472346..ffb8d8c 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -28,6 +28,7 @@
 class InputListenerInterface;
 class InputMapper;
 class InputReaderPolicyInterface;
+class PointerControllerInterface;
 struct StylusState;
 
 /* Internal interface used by individual input devices to access global input device state
@@ -42,10 +43,10 @@
     virtual int32_t getGlobalMetaState() = 0;
 
     virtual void disableVirtualKeysUntil(nsecs_t time) = 0;
-    virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode,
-                                      int32_t scanCode) = 0;
+    virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) = 0;
 
     virtual void fadePointer() = 0;
+    virtual std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) = 0;
 
     virtual void requestTimeoutAtTime(nsecs_t when) = 0;
     virtual int32_t bumpGeneration() = 0;
@@ -57,7 +58,7 @@
     virtual InputListenerInterface* getListener() = 0;
     virtual EventHubInterface* getEventHub() = 0;
 
-    virtual uint32_t getNextSequenceNum() = 0;
+    virtual int32_t getNextId() = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index da837a5..1a4d551 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "CursorInputMapper.h"
 
@@ -31,7 +31,7 @@
     clearRelativeAxes();
 }
 
-void CursorMotionAccumulator::reset(InputDevice* device) {
+void CursorMotionAccumulator::reset(InputDeviceContext& deviceContext) {
     clearRelativeAxes();
 }
 
@@ -59,7 +59,8 @@
 
 // --- CursorInputMapper ---
 
-CursorInputMapper::CursorInputMapper(InputDevice* device) : InputMapper(device) {}
+CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext)
+      : InputMapper(deviceContext) {}
 
 CursorInputMapper::~CursorInputMapper() {}
 
@@ -114,7 +115,7 @@
     InputMapper::configure(when, config, changes);
 
     if (!changes) { // first time only
-        mCursorScrollAccumulator.configure(getDevice());
+        mCursorScrollAccumulator.configure(getDeviceContext());
 
         // Configure basic parameters.
         configureParameters();
@@ -132,7 +133,7 @@
                 mYPrecision = 1.0f;
                 mXScale = 1.0f;
                 mYScale = 1.0f;
-                mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+                mPointerController = getContext()->getPointerController(getDeviceId());
                 break;
             case Parameters::MODE_NAVIGATION:
                 mSource = AINPUT_SOURCE_TRACKBALL;
@@ -168,7 +169,8 @@
         }
         bumpGeneration();
         if (changes) {
-            getDevice()->notifyReset(when);
+            NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
+            getListener()->notifyDeviceReset(&args);
         }
     }
 
@@ -188,38 +190,15 @@
             }
         }
 
-        // Update the PointerController if viewports changed.
-        if (mParameters.mode == Parameters::MODE_POINTER) {
-            updatePointerControllerDisplayViewport(*config);
-        }
         bumpGeneration();
     }
 }
 
-void CursorInputMapper::updatePointerControllerDisplayViewport(
-        const InputReaderConfiguration& config) {
-    std::optional<DisplayViewport> viewport =
-            config.getDisplayViewportById(config.defaultPointerDisplayId);
-    if (!viewport) {
-        ALOGW("Can't find the designated viewport with ID %" PRId32 " to update cursor input "
-              "mapper. Fall back to default display",
-              config.defaultPointerDisplayId);
-        viewport = config.getDisplayViewportById(ADISPLAY_ID_DEFAULT);
-    }
-
-    if (!viewport) {
-        ALOGE("Still can't find a viable viewport to update cursor input mapper. Skip setting it to"
-              " PointerController.");
-        return;
-    }
-
-    mPointerController->setDisplayViewport(*viewport);
-}
-
 void CursorInputMapper::configureParameters() {
     mParameters.mode = Parameters::MODE_POINTER;
     String8 cursorModeString;
-    if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) {
+    if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"),
+                                                             cursorModeString)) {
         if (cursorModeString == "navigation") {
             mParameters.mode = Parameters::MODE_NAVIGATION;
         } else if (cursorModeString != "pointer" && cursorModeString != "default") {
@@ -228,8 +207,8 @@
     }
 
     mParameters.orientationAware = false;
-    getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
-                                                   mParameters.orientationAware);
+    getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
+                                                         mParameters.orientationAware);
 
     mParameters.hasAssociatedDisplay = false;
     if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) {
@@ -267,9 +246,9 @@
     mWheelXVelocityControl.reset();
     mWheelYVelocityControl.reset();
 
-    mCursorButtonAccumulator.reset(getDevice());
-    mCursorMotionAccumulator.reset(getDevice());
-    mCursorScrollAccumulator.reset(getDevice());
+    mCursorButtonAccumulator.reset(getDeviceContext());
+    mCursorMotionAccumulator.reset(getDeviceContext());
+    mCursorScrollAccumulator.reset(getDeviceContext());
 
     InputMapper::reset(when);
 }
@@ -334,6 +313,8 @@
     mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
     int32_t displayId;
+    float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+    float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     if (mSource == AINPUT_SOURCE_MOUSE) {
         if (moved || scrolled || buttonsChanged) {
             mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
@@ -349,10 +330,9 @@
             mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
         }
 
-        float x, y;
-        mPointerController->getPosition(&x, &y);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+        mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
         displayId = mPointerController->getDisplayId();
@@ -369,7 +349,7 @@
     // the device in your pocket.
     // TODO: Use the input device configuration to control this behavior more finely.
     uint32_t policyFlags = 0;
-    if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) {
+    if ((buttonsPressed || moved || scrolled) && getDeviceContext().isExternal()) {
         policyFlags |= POLICY_FLAG_WAKE;
     }
 
@@ -379,7 +359,7 @@
 
     // Send motion event.
     if (downChanged || moved || scrolled || buttonsChanged) {
-        int32_t metaState = mContext->getGlobalMetaState();
+        int32_t metaState = getContext()->getGlobalMetaState();
         int32_t buttonState = lastButtonState;
         int32_t motionEventAction;
         if (downChanged) {
@@ -395,24 +375,24 @@
             while (!released.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
                 buttonState &= ~actionButton;
-                NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
+                NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, getDeviceId(),
                                              mSource, displayId, policyFlags,
                                              AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
                                              metaState, buttonState, MotionClassification::NONE,
-                                             AMOTION_EVENT_EDGE_FLAG_NONE,
-                                             /* deviceTimestamp */ 0, 1, &pointerProperties,
-                                             &pointerCoords, mXPrecision, mYPrecision, downTime,
+                                             AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                             &pointerCoords, mXPrecision, mYPrecision,
+                                             xCursorPosition, yCursorPosition, downTime,
                                              /* videoFrames */ {});
                 getListener()->notifyMotion(&releaseArgs);
             }
         }
 
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
-                              displayId, policyFlags, motionEventAction, 0, 0, metaState,
-                              currentButtonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE,
-                              /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                              mXPrecision, mYPrecision, downTime, /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, motionEventAction, 0, 0, metaState, currentButtonState,
+                              MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                              &pointerProperties, &pointerCoords, mXPrecision, mYPrecision,
+                              xCursorPosition, yCursorPosition, downTime,
+                              /* videoFrames */ {});
         getListener()->notifyMotion(&args);
 
         if (buttonsPressed) {
@@ -420,13 +400,13 @@
             while (!pressed.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
                 buttonState |= actionButton;
-                NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
-                                           mSource, displayId, policyFlags,
+                NotifyMotionArgs pressArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                           displayId, policyFlags,
                                            AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
                                            metaState, buttonState, MotionClassification::NONE,
-                                           AMOTION_EVENT_EDGE_FLAG_NONE,
-                                           /* deviceTimestamp */ 0, 1, &pointerProperties,
-                                           &pointerCoords, mXPrecision, mYPrecision, downTime,
+                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                           &pointerCoords, mXPrecision, mYPrecision,
+                                           xCursorPosition, yCursorPosition, downTime,
                                            /* videoFrames */ {});
                 getListener()->notifyMotion(&pressArgs);
             }
@@ -436,13 +416,12 @@
 
         // Send hover move after UP to tell the application that the mouse is hovering now.
         if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
-            NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+            NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
                                        displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
                                        0, metaState, currentButtonState, MotionClassification::NONE,
-                                       AMOTION_EVENT_EDGE_FLAG_NONE,
-                                       /* deviceTimestamp */ 0, 1, &pointerProperties,
-                                       &pointerCoords, mXPrecision, mYPrecision, downTime,
-                                       /* videoFrames */ {});
+                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                       &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
+                                       yCursorPosition, downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&hoverArgs);
         }
 
@@ -451,14 +430,12 @@
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-            NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(),
-                                        mSource, displayId, policyFlags,
-                                        AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
-                                        currentButtonState, MotionClassification::NONE,
-                                        AMOTION_EVENT_EDGE_FLAG_NONE,
-                                        /* deviceTimestamp */ 0, 1, &pointerProperties,
-                                        &pointerCoords, mXPrecision, mYPrecision, downTime,
-                                        /* videoFrames */ {});
+            NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
+                                        displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
+                                        metaState, currentButtonState, MotionClassification::NONE,
+                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
+                                        yCursorPosition, downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&scrollArgs);
         }
     }
@@ -473,19 +450,13 @@
 
 int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
     if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
-        return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+        return getDeviceContext().getScanCodeState(scanCode);
     } else {
         return AKEY_STATE_UNKNOWN;
     }
 }
 
-void CursorInputMapper::fadePointer() {
-    if (mPointerController != nullptr) {
-        mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
-    }
-}
-
-std::optional<int32_t> CursorInputMapper::getAssociatedDisplay() {
+std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
     if (mParameters.hasAssociatedDisplay) {
         if (mParameters.mode == Parameters::MODE_POINTER) {
             return std::make_optional(mPointerController->getDisplayId());
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index a1e9f10..05bbb26 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -36,7 +36,7 @@
 class CursorMotionAccumulator {
 public:
     CursorMotionAccumulator();
-    void reset(InputDevice* device);
+    void reset(InputDeviceContext& deviceContext);
 
     void process(const RawEvent* rawEvent);
     void finishSync();
@@ -53,21 +53,20 @@
 
 class CursorInputMapper : public InputMapper {
 public:
-    explicit CursorInputMapper(InputDevice* device);
+    explicit CursorInputMapper(InputDeviceContext& deviceContext);
     virtual ~CursorInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
-    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
 
-    virtual void fadePointer();
-
-    virtual std::optional<int32_t> getAssociatedDisplay();
+    virtual std::optional<int32_t> getAssociatedDisplayId() override;
 
 private:
     // Amount that trackball needs to move in order to generate a key event.
@@ -116,7 +115,6 @@
     void dumpParameters(std::string& dump);
 
     void sync(nsecs_t when);
-    void updatePointerControllerDisplayViewport(const InputReaderConfiguration& config);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 9aa0770..37d1d74 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "ExternalStylusInputMapper.h"
 
@@ -23,7 +23,8 @@
 
 namespace android {
 
-ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) : InputMapper(device) {}
+ExternalStylusInputMapper::ExternalStylusInputMapper(InputDeviceContext& deviceContext)
+      : InputMapper(deviceContext) {}
 
 uint32_t ExternalStylusInputMapper::getSources() {
     return AINPUT_SOURCE_STYLUS;
@@ -46,13 +47,12 @@
 void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
                                           uint32_t changes) {
     getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis);
-    mTouchButtonAccumulator.configure(getDevice());
+    mTouchButtonAccumulator.configure(getDeviceContext());
 }
 
 void ExternalStylusInputMapper::reset(nsecs_t when) {
-    InputDevice* device = getDevice();
-    mSingleTouchMotionAccumulator.reset(device);
-    mTouchButtonAccumulator.reset(device);
+    mSingleTouchMotionAccumulator.reset(getDeviceContext());
+    mTouchButtonAccumulator.reset(getDeviceContext());
     InputMapper::reset(when);
 }
 
@@ -86,7 +86,7 @@
 
     mStylusState.buttons = mTouchButtonAccumulator.getButtonState();
 
-    mContext->dispatchExternalStylusState(mStylusState);
+    getContext()->dispatchExternalStylusState(mStylusState);
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index 9764fbb..1d42b30 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -27,16 +27,16 @@
 
 class ExternalStylusInputMapper : public InputMapper {
 public:
-    explicit ExternalStylusInputMapper(InputDevice* device);
+    explicit ExternalStylusInputMapper(InputDeviceContext& deviceContext);
     virtual ~ExternalStylusInputMapper() = default;
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
-    virtual void sync(nsecs_t when);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
 private:
     SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
@@ -44,6 +44,8 @@
     TouchButtonAccumulator mTouchButtonAccumulator;
 
     StylusState mStylusState;
+
+    void sync(nsecs_t when);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index d941528..a8fe39a 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "InputMapper.h"
 
@@ -22,7 +22,7 @@
 
 namespace android {
 
-InputMapper::InputMapper(InputDevice* device) : mDevice(device), mContext(device->getContext()) {}
+InputMapper::InputMapper(InputDeviceContext& deviceContext) : mDeviceContext(deviceContext) {}
 
 InputMapper::~InputMapper() {}
 
@@ -71,14 +71,12 @@
 
 void InputMapper::updateExternalStylusState(const StylusState& state) {}
 
-void InputMapper::fadePointer() {}
-
 status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) {
-    return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo);
+    return getDeviceContext().getAbsoluteAxisInfo(axis, axisInfo);
 }
 
 void InputMapper::bumpGeneration() {
-    mDevice->bumpGeneration();
+    getDeviceContext().bumpGeneration();
 }
 
 void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis,
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index cfd207c..949c7ea 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -39,16 +39,15 @@
  */
 class InputMapper {
 public:
-    explicit InputMapper(InputDevice* device);
+    explicit InputMapper(InputDeviceContext& deviceContext);
     virtual ~InputMapper();
 
-    inline InputDevice* getDevice() { return mDevice; }
-    inline int32_t getDeviceId() { return mDevice->getId(); }
-    inline const std::string getDeviceName() { return mDevice->getName(); }
-    inline InputReaderContext* getContext() { return mContext; }
-    inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); }
-    inline InputListenerInterface* getListener() { return mContext->getListener(); }
-    inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
+    inline int32_t getDeviceId() { return mDeviceContext.getId(); }
+    inline InputDeviceContext& getDeviceContext() { return mDeviceContext; }
+    inline const std::string getDeviceName() { return mDeviceContext.getName(); }
+    inline InputReaderContext* getContext() { return mDeviceContext.getContext(); }
+    inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); }
+    inline InputListenerInterface* getListener() { return getContext()->getListener(); }
 
     virtual uint32_t getSources() = 0;
     virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
@@ -72,12 +71,10 @@
 
     virtual void updateExternalStylusState(const StylusState& state);
 
-    virtual void fadePointer();
-    virtual std::optional<int32_t> getAssociatedDisplay() { return std::nullopt; }
+    virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
 
 protected:
-    InputDevice* mDevice;
-    InputReaderContext* mContext;
+    InputDeviceContext& mDeviceContext;
 
     status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo);
     void bumpGeneration();
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index b493e83..030a846 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "JoystickInputMapper.h"
 
 namespace android {
 
-JoystickInputMapper::JoystickInputMapper(InputDevice* device) : InputMapper(device) {}
+JoystickInputMapper::JoystickInputMapper(InputDeviceContext& deviceContext)
+      : InputMapper(deviceContext) {}
 
 JoystickInputMapper::~JoystickInputMapper() {}
 
@@ -112,7 +113,8 @@
     if (!changes) { // first time only
         // Collect all axes.
         for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
-            if (!(getAbsAxisUsage(abs, getDevice()->getClasses()) & INPUT_DEVICE_CLASS_JOYSTICK)) {
+            if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses()) &
+                  INPUT_DEVICE_CLASS_JOYSTICK)) {
                 continue; // axis must be claimed by a different device
             }
 
@@ -121,7 +123,7 @@
             if (rawAxisInfo.valid) {
                 // Map axis.
                 AxisInfo axisInfo;
-                bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo);
+                bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo);
                 if (!explicitlyMapped) {
                     // Axis is not explicitly mapped, will choose a generic axis later.
                     axisInfo.mode = AxisInfo::MODE_NORMAL;
@@ -304,7 +306,7 @@
         return;
     }
 
-    int32_t metaState = mContext->getGlobalMetaState();
+    int32_t metaState = getContext()->getGlobalMetaState();
     int32_t buttonState = 0;
 
     PointerProperties pointerProperties;
@@ -331,12 +333,12 @@
     // TODO: Use the input device configuration to control this behavior more finely.
     uint32_t policyFlags = 0;
 
-    NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(),
-                          AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags,
-                          AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
-                          MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
-                          /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, 0,
-                          /* videoFrames */ {});
+    NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK,
+                          ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+                          buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                          &pointerProperties, &pointerCoords, 0, 0,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
     getListener()->notifyMotion(&args);
 }
 
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 1b071d0..823a096 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -23,15 +23,16 @@
 
 class JoystickInputMapper : public InputMapper {
 public:
-    explicit JoystickInputMapper(InputDevice* device);
+    explicit JoystickInputMapper(InputDeviceContext& deviceContext);
     virtual ~JoystickInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
 private:
     struct Axis {
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 0e91c0e..e009221 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "KeyboardInputMapper.h"
 
@@ -87,8 +87,9 @@
 
 // --- KeyboardInputMapper ---
 
-KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType)
-      : InputMapper(device), mSource(source), mKeyboardType(keyboardType) {}
+KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source,
+                                         int32_t keyboardType)
+      : InputMapper(deviceContext), mSource(source), mKeyboardType(keyboardType) {}
 
 KeyboardInputMapper::~KeyboardInputMapper() {}
 
@@ -114,7 +115,7 @@
     InputMapper::populateDeviceInfo(info);
 
     info->setKeyboardType(mKeyboardType);
-    info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId()));
+    info->setKeyCharacterMap(getDeviceContext().getKeyCharacterMap());
 }
 
 void KeyboardInputMapper::dump(std::string& dump) {
@@ -127,6 +128,22 @@
     dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
 }
 
+std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
+        nsecs_t when, const InputReaderConfiguration* config) {
+    const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort();
+    if (displayPort) {
+        // Find the viewport that contains the same port
+        return getDeviceContext().getAssociatedViewport();
+    }
+
+    // No associated display defined, try to find default display if orientationAware.
+    if (mParameters.orientationAware) {
+        return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+    }
+
+    return std::nullopt;
+}
+
 void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
                                     uint32_t changes) {
     InputMapper::configure(when, config, changes);
@@ -137,9 +154,7 @@
     }
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-        if (mParameters.orientationAware) {
-            mViewport = config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
-        }
+        mViewport = findViewport(when, config);
     }
 }
 
@@ -157,7 +172,7 @@
 
 void KeyboardInputMapper::configureParameters() {
     mParameters.orientationAware = false;
-    const PropertyMap& config = getDevice()->getConfiguration();
+    const PropertyMap& config = getDeviceContext().getConfiguration();
     config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware);
 
     if (mParameters.orientationAware) {
@@ -169,6 +184,9 @@
 
     mParameters.handlesKeyRepeat = false;
     config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat);
+
+    mParameters.doNotWakeByDefault = false;
+    config.tryGetProperty(String8("keyboard.doNotWakeByDefault"), mParameters.doNotWakeByDefault);
 }
 
 void KeyboardInputMapper::dumpParameters(std::string& dump) {
@@ -215,7 +233,7 @@
 }
 
 bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) {
-    return scanCode < BTN_MOUSE || scanCode >= KEY_OK ||
+    return scanCode < BTN_MOUSE || scanCode >= BTN_WHEEL ||
             (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) ||
             (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI);
 }
@@ -254,8 +272,8 @@
     int32_t keyMetaState;
     uint32_t policyFlags;
 
-    if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState, &keyCode,
-                              &keyMetaState, &policyFlags)) {
+    if (getDeviceContext().mapKey(scanCode, usageCode, mMetaState, &keyCode, &keyMetaState,
+                                  &policyFlags)) {
         keyCode = AKEYCODE_UNKNOWN;
         keyMetaState = mMetaState;
         policyFlags = 0;
@@ -275,11 +293,11 @@
         } else {
             // key down
             if ((policyFlags & POLICY_FLAG_VIRTUAL) &&
-                mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) {
+                getContext()->shouldDropVirtualKey(when, keyCode, scanCode)) {
                 return;
             }
             if (policyFlags & POLICY_FLAG_GESTURE) {
-                mDevice->cancelTouch(when);
+                getDeviceContext().cancelTouch(when);
             }
 
             KeyDown keyDown;
@@ -317,10 +335,12 @@
 
     // Key down on external an keyboard should wake the device.
     // We don't do this for internal keyboards to prevent them from waking up in your pocket.
-    // For internal keyboards, the key layout file should specify the policy flags for
-    // each wake key individually.
+    // For internal keyboards and devices for which the default wake behavior is explicitly
+    // prevented (e.g. TV remotes), the key layout file should specify the policy flags for each
+    // wake key individually.
     // TODO: Use the input device configuration to control this behavior more finely.
-    if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) {
+    if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault &&
+        !isMediaKey(keyCode)) {
         policyFlags |= POLICY_FLAG_WAKE;
     }
 
@@ -328,7 +348,7 @@
         policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
     }
 
-    NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, getDisplayId(),
+    NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(),
                        policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
                        AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
     getListener()->notifyKey(&args);
@@ -345,16 +365,16 @@
 }
 
 int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
-    return getEventHub()->getKeyCodeState(getDeviceId(), keyCode);
+    return getDeviceContext().getKeyCodeState(keyCode);
 }
 
 int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
-    return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
+    return getDeviceContext().getScanCodeState(scanCode);
 }
 
 bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
                                                 const int32_t* keyCodes, uint8_t* outFlags) {
-    return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags);
+    return getDeviceContext().markSupportedKeyCodes(numCodes, keyCodes, outFlags);
 }
 
 int32_t KeyboardInputMapper::getMetaState() {
@@ -388,7 +408,7 @@
 }
 
 void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) {
-    ledState.avail = getEventHub()->hasLed(getDeviceId(), led);
+    ledState.avail = getDeviceContext().hasLed(led);
     ledState.on = false;
 }
 
@@ -403,10 +423,17 @@
     if (ledState.avail) {
         bool desiredState = (mMetaState & modifier) != 0;
         if (reset || ledState.on != desiredState) {
-            getEventHub()->setLedState(getDeviceId(), led, desiredState);
+            getDeviceContext().setLedState(led, desiredState);
             ledState.on = desiredState;
         }
     }
 }
 
+std::optional<int32_t> KeyboardInputMapper::getAssociatedDisplayId() {
+    if (mViewport) {
+        return std::make_optional(mViewport->displayId);
+    }
+    return std::nullopt;
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 7a68fc3..0bdeded 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -23,23 +23,25 @@
 
 class KeyboardInputMapper : public InputMapper {
 public:
-    KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType);
+    KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, int32_t keyboardType);
     virtual ~KeyboardInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
-    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
-    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
     virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
-                                       const int32_t* keyCodes, uint8_t* outFlags);
+                                       const int32_t* keyCodes, uint8_t* outFlags) override;
 
-    virtual int32_t getMetaState();
-    virtual void updateMetaState(int32_t keyCode);
+    virtual int32_t getMetaState() override;
+    virtual void updateMetaState(int32_t keyCode) override;
+    virtual std::optional<int32_t> getAssociatedDisplayId() override;
 
 private:
     // The current viewport.
@@ -71,6 +73,7 @@
     struct Parameters {
         bool orientationAware;
         bool handlesKeyRepeat;
+        bool doNotWakeByDefault;
     } mParameters;
 
     void configureParameters();
@@ -92,6 +95,8 @@
     void initializeLedState(LedState& ledState, int32_t led);
     void updateLedState(bool reset);
     void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
+    std::optional<DisplayViewport> findViewport(nsecs_t when,
+                                                const InputReaderConfiguration* config);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 7460a31..43bd9f1 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "MultiTouchInputMapper.h"
 
@@ -38,17 +38,17 @@
     delete[] mSlots;
 }
 
-void MultiTouchMotionAccumulator::configure(InputDevice* device, size_t slotCount,
+void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
                                             bool usingSlotsProtocol) {
     mSlotCount = slotCount;
     mUsingSlotsProtocol = usingSlotsProtocol;
-    mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
+    mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
 
     delete[] mSlots;
     mSlots = new Slot[slotCount];
 }
 
-void MultiTouchMotionAccumulator::reset(InputDevice* device) {
+void MultiTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
     // Unfortunately there is no way to read the initial contents of the slots.
     // So when we reset the accumulator, we must assume they are all zeroes.
     if (mUsingSlotsProtocol) {
@@ -62,8 +62,7 @@
         // This can cause the touch point to "jump", but at least there will be
         // no stuck touches.
         int32_t initialSlot;
-        status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), ABS_MT_SLOT,
-                                                                      &initialSlot);
+        status_t status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
         if (status) {
             ALOGD("Could not retrieve current multitouch slot index.  status=%d", status);
             initialSlot = -1;
@@ -72,7 +71,6 @@
     } else {
         clearSlots(-1);
     }
-    mDeviceTimestamp = 0;
 }
 
 void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
@@ -166,8 +164,6 @@
     } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
         // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
         mCurrentSlot += 1;
-    } else if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
-        mDeviceTimestamp = rawEvent->value;
     }
 }
 
@@ -212,6 +208,8 @@
                 return AMOTION_EVENT_TOOL_TYPE_FINGER;
             case MT_TOOL_PEN:
                 return AMOTION_EVENT_TOOL_TYPE_STYLUS;
+            case MT_TOOL_PALM:
+                return AMOTION_EVENT_TOOL_TYPE_PALM;
         }
     }
     return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
@@ -219,12 +217,13 @@
 
 // --- MultiTouchInputMapper ---
 
-MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {}
+MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
+      : TouchInputMapper(deviceContext) {}
 
 MultiTouchInputMapper::~MultiTouchInputMapper() {}
 
 void MultiTouchInputMapper::reset(nsecs_t when) {
-    mMultiTouchMotionAccumulator.reset(getDevice());
+    mMultiTouchMotionAccumulator.reset(getDeviceContext());
 
     mPointerIdBits.clear();
 
@@ -250,6 +249,15 @@
             continue;
         }
 
+        if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
+            if (!mCurrentMotionAborted) {
+                ALOGI("Canceling touch gesture from device %s because the palm event was detected",
+                      getDeviceName().c_str());
+                cancelTouch(when);
+            }
+            continue;
+        }
+
         if (outCount >= MAX_POINTERS) {
 #if DEBUG_POINTERS
             ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
@@ -316,7 +324,6 @@
         outCount += 1;
     }
 
-    outState->deviceTimestamp = mMultiTouchMotionAccumulator.getDeviceTimestamp();
     outState->rawPointerData.pointerCount = outCount;
     mPointerIdBits = newPointerIdBits;
 
@@ -347,9 +354,10 @@
                   getDeviceName().c_str(), slotCount, MAX_SLOTS);
             slotCount = MAX_SLOTS;
         }
-        mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/);
+        mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
+                                               true /*usingSlotsProtocol*/);
     } else {
-        mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS,
+        mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
                                                false /*usingSlotsProtocol*/);
     }
 }
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 873dda1..89ef41d 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -70,15 +70,14 @@
     MultiTouchMotionAccumulator();
     ~MultiTouchMotionAccumulator();
 
-    void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol);
-    void reset(InputDevice* device);
+    void configure(InputDeviceContext& deviceContext, size_t slotCount, bool usingSlotsProtocol);
+    void reset(InputDeviceContext& deviceContext);
     void process(const RawEvent* rawEvent);
     void finishSync();
     bool hasStylus() const;
 
     inline size_t getSlotCount() const { return mSlotCount; }
     inline const Slot* getSlot(size_t index) const { return &mSlots[index]; }
-    inline uint32_t getDeviceTimestamp() const { return mDeviceTimestamp; }
 
 private:
     int32_t mCurrentSlot;
@@ -86,18 +85,17 @@
     size_t mSlotCount;
     bool mUsingSlotsProtocol;
     bool mHaveStylus;
-    uint32_t mDeviceTimestamp;
 
     void clearSlots(int32_t initialSlot);
 };
 
 class MultiTouchInputMapper : public TouchInputMapper {
 public:
-    explicit MultiTouchInputMapper(InputDevice* device);
+    explicit MultiTouchInputMapper(InputDeviceContext& deviceContext);
     virtual ~MultiTouchInputMapper();
 
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
 protected:
     virtual void syncTouch(nsecs_t when, RawState* outState);
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 803fdf3..9885889 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "RotaryEncoderInputMapper.h"
 
@@ -22,8 +22,8 @@
 
 namespace android {
 
-RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device)
-      : InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) {
+RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext)
+      : InputMapper(deviceContext), mOrientation(DISPLAY_ORIENTATION_0) {
     mSource = AINPUT_SOURCE_ROTARY_ENCODER;
 }
 
@@ -38,11 +38,11 @@
 
     if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) {
         float res = 0.0f;
-        if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) {
+        if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.res"), res)) {
             ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
         }
-        if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"),
-                                                        mScalingFactor)) {
+        if (!getDeviceContext().getConfiguration().tryGetProperty(String8("device.scalingFactor"),
+                                                                  mScalingFactor)) {
             ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
                   "default to 1.0!\n");
             mScalingFactor = 1.0f;
@@ -62,7 +62,7 @@
                                          uint32_t changes) {
     InputMapper::configure(when, config, changes);
     if (!changes) {
-        mRotaryEncoderScrollAccumulator.configure(getDevice());
+        mRotaryEncoderScrollAccumulator.configure(getDeviceContext());
     }
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
         std::optional<DisplayViewport> internalViewport =
@@ -76,7 +76,7 @@
 }
 
 void RotaryEncoderInputMapper::reset(nsecs_t when) {
-    mRotaryEncoderScrollAccumulator.reset(getDevice());
+    mRotaryEncoderScrollAccumulator.reset(getDeviceContext());
 
     InputMapper::reset(when);
 }
@@ -106,7 +106,7 @@
 
     // Moving the rotary encoder should wake the device (if specified).
     uint32_t policyFlags = 0;
-    if (scrolled && getDevice()->isExternal()) {
+    if (scrolled && getDeviceContext().isExternal()) {
         policyFlags |= POLICY_FLAG_WAKE;
     }
 
@@ -116,15 +116,15 @@
 
     // Send motion event.
     if (scrolled) {
-        int32_t metaState = mContext->getGlobalMetaState();
+        int32_t metaState = getContext()->getGlobalMetaState();
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
 
-        NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+        NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
                                     displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
                                     metaState, /* buttonState */ 0, MotionClassification::NONE,
-                                    AMOTION_EVENT_EDGE_FLAG_NONE,
-                                    /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords,
-                                    0, 0, 0, /* videoFrames */ {});
+                                    AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                    &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                    AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
         getListener()->notifyMotion(&scrollArgs);
     }
 
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 2648837..7a77b12 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -24,15 +24,16 @@
 
 class RotaryEncoderInputMapper : public InputMapper {
 public:
-    explicit RotaryEncoderInputMapper(InputDevice* device);
+    explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext);
     virtual ~RotaryEncoderInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
 private:
     CursorScrollAccumulator mRotaryEncoderScrollAccumulator;
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
index 440d282..4fff9be 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
@@ -18,12 +18,13 @@
 
 namespace android {
 
-SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {}
+SingleTouchInputMapper::SingleTouchInputMapper(InputDeviceContext& deviceContext)
+      : TouchInputMapper(deviceContext) {}
 
 SingleTouchInputMapper::~SingleTouchInputMapper() {}
 
 void SingleTouchInputMapper::reset(nsecs_t when) {
-    mSingleTouchMotionAccumulator.reset(getDevice());
+    mSingleTouchMotionAccumulator.reset(getDeviceContext());
 
     TouchInputMapper::reset(when);
 }
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index d6b1455..f5befb3 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -24,11 +24,11 @@
 
 class SingleTouchInputMapper : public TouchInputMapper {
 public:
-    explicit SingleTouchInputMapper(InputDevice* device);
+    explicit SingleTouchInputMapper(InputDeviceContext& deviceContext);
     virtual ~SingleTouchInputMapper();
 
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
 protected:
     virtual void syncTouch(nsecs_t when, RawState* outState);
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 4ff941f..4f73681 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "SwitchInputMapper.h"
 
 namespace android {
 
-SwitchInputMapper::SwitchInputMapper(InputDevice* device)
-      : InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) {}
+SwitchInputMapper::SwitchInputMapper(InputDeviceContext& deviceContext)
+      : InputMapper(deviceContext), mSwitchValues(0), mUpdatedSwitchMask(0) {}
 
 SwitchInputMapper::~SwitchInputMapper() {}
 
@@ -56,8 +56,8 @@
 void SwitchInputMapper::sync(nsecs_t when) {
     if (mUpdatedSwitchMask) {
         uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
-        NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues,
-                              mUpdatedSwitchMask);
+        NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/,
+                              updatedSwitchValues, mUpdatedSwitchMask);
         getListener()->notifySwitch(&args);
 
         mUpdatedSwitchMask = 0;
@@ -65,7 +65,7 @@
 }
 
 int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) {
-    return getEventHub()->getSwitchState(getDeviceId(), switchCode);
+    return getDeviceContext().getSwitchState(switchCode);
 }
 
 void SwitchInputMapper::dump(std::string& dump) {
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
index dd4bb9e..4d74163 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -23,14 +23,14 @@
 
 class SwitchInputMapper : public InputMapper {
 public:
-    explicit SwitchInputMapper(InputDevice* device);
+    explicit SwitchInputMapper(InputDeviceContext& deviceContext);
     virtual ~SwitchInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void process(const RawEvent* rawEvent) override;
 
-    virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
-    virtual void dump(std::string& dump);
+    virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override;
+    virtual void dump(std::string& dump) override;
 
 private:
     uint32_t mSwitchValues;
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index ca18edc..a86443d 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -17,6 +17,7 @@
 #ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
 #define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
 
+#include <input/DisplayViewport.h>
 #include <stdint.h>
 
 #include "EventHub.h"
@@ -65,8 +66,8 @@
          (currentButtonState & buttonState)) ||
         (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
          !(currentButtonState & buttonState))) {
-        NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId,
-                           policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when);
+        NotifyKeyArgs args(context->getNextId(), when, deviceId, source, displayId, policyFlags,
+                           action, 0, keyCode, 0, context->getGlobalMetaState(), when);
         context->getListener()->notifyKey(&args);
     }
 }
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 97cbad2..efdc84f 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "TouchInputMapper.h"
 
@@ -23,11 +23,6 @@
 #include "TouchButtonAccumulator.h"
 #include "TouchCursorInputMapperCommon.h"
 
-#include <statslog.h>
-
-// How often to report input event statistics
-static constexpr nsecs_t STATISTICS_REPORT_FREQUENCY = seconds_to_nanoseconds(5 * 60);
-
 namespace android {
 
 // --- Constants ---
@@ -161,12 +156,12 @@
 
 // --- TouchInputMapper ---
 
-TouchInputMapper::TouchInputMapper(InputDevice* device)
-      : InputMapper(device),
+TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext)
+      : InputMapper(deviceContext),
         mSource(0),
         mDeviceMode(DEVICE_MODE_DISABLED),
-        mSurfaceWidth(-1),
-        mSurfaceHeight(-1),
+        mRawSurfaceWidth(-1),
+        mRawSurfaceHeight(-1),
         mSurfaceLeft(0),
         mSurfaceTop(0),
         mPhysicalWidth(-1),
@@ -353,8 +348,8 @@
         configureParameters();
 
         // Configure common accumulators.
-        mCursorScrollAccumulator.configure(getDevice());
-        mTouchButtonAccumulator.configure(getDevice());
+        mCursorScrollAccumulator.configure(getDeviceContext());
+        mTouchButtonAccumulator.configure(getDeviceContext());
 
         // Configure absolute axis information.
         configureRawPointerAxes();
@@ -391,13 +386,14 @@
     if (changes && resetNeeded) {
         // Send reset, unless this is the first time the device has been configured,
         // in which case the reader will call reset itself after all mappers are ready.
-        getDevice()->notifyReset(when);
+        NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
+        getListener()->notifyDeviceReset(&args);
     }
 }
 
 void TouchInputMapper::resolveExternalStylusPresence() {
     std::vector<InputDeviceInfo> devices;
-    mContext->getExternalStylusDevices(devices);
+    getContext()->getExternalStylusDevices(devices);
     mExternalStylusConnected = !devices.empty();
 
     if (!mExternalStylusConnected) {
@@ -409,13 +405,13 @@
     // Use the pointer presentation mode for devices that do not support distinct
     // multitouch.  The spot-based presentation relies on being able to accurately
     // locate two or more fingers on the touch pad.
-    mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT)
+    mParameters.gestureMode = getDeviceContext().hasInputProperty(INPUT_PROP_SEMI_MT)
             ? Parameters::GESTURE_MODE_SINGLE_TOUCH
             : Parameters::GESTURE_MODE_MULTI_TOUCH;
 
     String8 gestureModeString;
-    if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"),
-                                                       gestureModeString)) {
+    if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.gestureMode"),
+                                                             gestureModeString)) {
         if (gestureModeString == "single-touch") {
             mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH;
         } else if (gestureModeString == "multi-touch") {
@@ -425,14 +421,14 @@
         }
     }
 
-    if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) {
+    if (getDeviceContext().hasInputProperty(INPUT_PROP_DIRECT)) {
         // The device is a touch screen.
         mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
-    } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) {
+    } else if (getDeviceContext().hasInputProperty(INPUT_PROP_POINTER)) {
         // The device is a pointing device like a track pad.
         mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
-    } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) ||
-               getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) {
+    } else if (getDeviceContext().hasRelativeAxis(REL_X) ||
+               getDeviceContext().hasRelativeAxis(REL_Y)) {
         // The device is a cursor device with a touch pad attached.
         // By default don't use the touch pad to move the pointer.
         mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
@@ -441,12 +437,11 @@
         mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
     }
 
-    mParameters.hasButtonUnderPad =
-            getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD);
+    mParameters.hasButtonUnderPad = getDeviceContext().hasInputProperty(INPUT_PROP_BUTTONPAD);
 
     String8 deviceTypeString;
-    if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
-                                                       deviceTypeString)) {
+    if (getDeviceContext().getConfiguration().tryGetProperty(String8("touch.deviceType"),
+                                                             deviceTypeString)) {
         if (deviceTypeString == "touchScreen") {
             mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
         } else if (deviceTypeString == "touchPad") {
@@ -461,8 +456,8 @@
     }
 
     mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
-    getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"),
-                                                   mParameters.orientationAware);
+    getDeviceContext().getConfiguration().tryGetProperty(String8("touch.orientationAware"),
+                                                         mParameters.orientationAware);
 
     mParameters.hasAssociatedDisplay = false;
     mParameters.associatedDisplayIsExternal = false;
@@ -471,22 +466,22 @@
         mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
         mParameters.hasAssociatedDisplay = true;
         if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) {
-            mParameters.associatedDisplayIsExternal = getDevice()->isExternal();
+            mParameters.associatedDisplayIsExternal = getDeviceContext().isExternal();
             String8 uniqueDisplayId;
-            getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"),
-                                                           uniqueDisplayId);
+            getDeviceContext().getConfiguration().tryGetProperty(String8("touch.displayId"),
+                                                                 uniqueDisplayId);
             mParameters.uniqueDisplayId = uniqueDisplayId.c_str();
         }
     }
-    if (getDevice()->getAssociatedDisplayPort()) {
+    if (getDeviceContext().getAssociatedDisplayPort()) {
         mParameters.hasAssociatedDisplay = true;
     }
 
     // Initial downs on external touch devices should wake the device.
     // Normally we don't do this for internal touch screens to prevent them from waking
     // up in your pocket but you can enable it using the input device configuration.
-    mParameters.wake = getDevice()->isExternal();
-    getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake);
+    mParameters.wake = getDeviceContext().isExternal();
+    getDeviceContext().getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake);
 }
 
 void TouchInputMapper::dumpParameters(std::string& dump) {
@@ -564,16 +559,10 @@
  */
 std::optional<DisplayViewport> TouchInputMapper::findViewport() {
     if (mParameters.hasAssociatedDisplay) {
-        const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+        const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort();
         if (displayPort) {
             // Find the viewport that contains the same port
-            std::optional<DisplayViewport> v = mConfig.getDisplayViewportByPort(*displayPort);
-            if (!v) {
-                ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
-                      "but the corresponding viewport is not found.",
-                      getDeviceName().c_str(), *displayPort);
-            }
-            return v;
+            return getDeviceContext().getAssociatedViewport();
         }
 
         if (mDeviceMode == DEVICE_MODE_POINTER) {
@@ -611,6 +600,7 @@
         return viewport;
     }
 
+    // No associated display, return a non-display viewport.
     DisplayViewport newViewport;
     // Raw width and height in the natural orientation.
     int32_t rawWidth = mRawPointerAxes.getRawWidth();
@@ -739,10 +729,12 @@
             mPhysicalLeft = naturalPhysicalLeft;
             mPhysicalTop = naturalPhysicalTop;
 
-            mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
-            mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
+            mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
+            mRawSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
             mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
             mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
+            mSurfaceRight = mSurfaceLeft + naturalLogicalWidth;
+            mSurfaceBottom = mSurfaceTop + naturalLogicalHeight;
 
             mSurfaceOrientation =
                     mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0;
@@ -752,8 +744,8 @@
             mPhysicalLeft = 0;
             mPhysicalTop = 0;
 
-            mSurfaceWidth = rawWidth;
-            mSurfaceHeight = rawHeight;
+            mRawSurfaceWidth = rawWidth;
+            mRawSurfaceHeight = rawHeight;
             mSurfaceLeft = 0;
             mSurfaceTop = 0;
             mSurfaceOrientation = DISPLAY_ORIENTATION_0;
@@ -766,16 +758,11 @@
         mOrientedRanges.clear();
     }
 
-    // Create or update pointer controller if needed.
+    // Create pointer controller if needed.
     if (mDeviceMode == DEVICE_MODE_POINTER ||
         (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
-        if (mPointerController == nullptr || viewportChanged) {
-            mPointerController = getPolicy()->obtainPointerController(getDeviceId());
-            // Set the DisplayViewport for the PointerController to the default pointer display
-            // that is recommended by the configuration before using it.
-            std::optional<DisplayViewport> defaultViewport =
-                    mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
-            mPointerController->setDisplayViewport(defaultViewport.value_or(mViewport));
+        if (mPointerController == nullptr) {
+            mPointerController = getContext()->getPointerController(getDeviceId());
         }
     } else {
         mPointerController.reset();
@@ -784,12 +771,12 @@
     if (viewportChanged || deviceModeChanged) {
         ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
               "display id %d",
-              getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight,
+              getDeviceId(), getDeviceName().c_str(), mRawSurfaceWidth, mRawSurfaceHeight,
               mSurfaceOrientation, mDeviceMode, mViewport.displayId);
 
         // Configure X and Y factors.
-        mXScale = float(mSurfaceWidth) / rawWidth;
-        mYScale = float(mSurfaceHeight) / rawHeight;
+        mXScale = float(mRawSurfaceWidth) / rawWidth;
+        mYScale = float(mRawSurfaceHeight) / rawHeight;
         mXTranslate = -mSurfaceLeft;
         mYTranslate = -mSurfaceTop;
         mXPrecision = 1.0f / mXScale;
@@ -808,7 +795,7 @@
         mGeometricScale = avg(mXScale, mYScale);
 
         // Size of diagonal axis.
-        float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight);
+        float diagonalSize = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
 
         // Size factors.
         if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) {
@@ -971,13 +958,13 @@
                 mOrientedYPrecision = mXPrecision;
 
                 mOrientedRanges.x.min = mYTranslate;
-                mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1;
+                mOrientedRanges.x.max = mRawSurfaceHeight + mYTranslate - 1;
                 mOrientedRanges.x.flat = 0;
                 mOrientedRanges.x.fuzz = 0;
                 mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
 
                 mOrientedRanges.y.min = mXTranslate;
-                mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1;
+                mOrientedRanges.y.max = mRawSurfaceWidth + mXTranslate - 1;
                 mOrientedRanges.y.flat = 0;
                 mOrientedRanges.y.fuzz = 0;
                 mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
@@ -988,13 +975,13 @@
                 mOrientedYPrecision = mYPrecision;
 
                 mOrientedRanges.x.min = mXTranslate;
-                mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1;
+                mOrientedRanges.x.max = mRawSurfaceWidth + mXTranslate - 1;
                 mOrientedRanges.x.flat = 0;
                 mOrientedRanges.x.fuzz = 0;
                 mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
 
                 mOrientedRanges.y.min = mYTranslate;
-                mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1;
+                mOrientedRanges.y.max = mRawSurfaceHeight + mYTranslate - 1;
                 mOrientedRanges.y.flat = 0;
                 mOrientedRanges.y.fuzz = 0;
                 mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
@@ -1007,7 +994,7 @@
         if (mDeviceMode == DEVICE_MODE_POINTER) {
             // Compute pointer gesture detection parameters.
             float rawDiagonal = hypotf(rawWidth, rawHeight);
-            float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight);
+            float displayDiagonal = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
 
             // Scale movements such that one whole swipe of the touch pad covers a
             // given area relative to the diagonal size of the display when no acceleration
@@ -1042,10 +1029,12 @@
 
 void TouchInputMapper::dumpSurface(std::string& dump) {
     dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
-    dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth);
-    dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight);
+    dump += StringPrintf(INDENT3 "RawSurfaceWidth: %dpx\n", mRawSurfaceWidth);
+    dump += StringPrintf(INDENT3 "RawSurfaceHeight: %dpx\n", mRawSurfaceHeight);
     dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
     dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop);
+    dump += StringPrintf(INDENT3 "SurfaceRight: %d\n", mSurfaceRight);
+    dump += StringPrintf(INDENT3 "SurfaceBottom: %d\n", mSurfaceBottom);
     dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth);
     dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight);
     dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft);
@@ -1055,7 +1044,7 @@
 
 void TouchInputMapper::configureVirtualKeys() {
     std::vector<VirtualKeyDefinition> virtualKeyDefinitions;
-    getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions);
+    getDeviceContext().getVirtualKeyDefinitions(virtualKeyDefinitions);
 
     mVirtualKeys.clear();
 
@@ -1075,8 +1064,8 @@
         int32_t keyCode;
         int32_t dummyKeyMetaState;
         uint32_t flags;
-        if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0, &keyCode,
-                                  &dummyKeyMetaState, &flags)) {
+        if (getDeviceContext().mapKey(virtualKey.scanCode, 0, 0, &keyCode, &dummyKeyMetaState,
+                                      &flags)) {
             ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode);
             continue; // drop the key
         }
@@ -1089,16 +1078,16 @@
         int32_t halfHeight = virtualKeyDefinition.height / 2;
 
         virtualKey.hitLeft =
-                (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mSurfaceWidth +
+                (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mRawSurfaceWidth +
                 touchScreenLeft;
         virtualKey.hitRight =
-                (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mSurfaceWidth +
+                (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mRawSurfaceWidth +
                 touchScreenLeft;
-        virtualKey.hitTop =
-                (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mSurfaceHeight +
+        virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight /
+                        mRawSurfaceHeight +
                 touchScreenTop;
-        virtualKey.hitBottom =
-                (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mSurfaceHeight +
+        virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight /
+                        mRawSurfaceHeight +
                 touchScreenTop;
         mVirtualKeys.push_back(virtualKey);
     }
@@ -1119,7 +1108,7 @@
 }
 
 void TouchInputMapper::parseCalibration() {
-    const PropertyMap& in = getDevice()->getConfiguration();
+    const PropertyMap& in = getDeviceContext().getConfiguration();
     Calibration& out = mCalibration;
 
     // Size
@@ -1363,14 +1352,14 @@
 }
 
 void TouchInputMapper::updateAffineTransformation() {
-    mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(),
+    mAffineTransform = getPolicy()->getTouchAffineTransformation(getDeviceContext().getDescriptor(),
                                                                  mSurfaceOrientation);
 }
 
 void TouchInputMapper::reset(nsecs_t when) {
-    mCursorButtonAccumulator.reset(getDevice());
-    mCursorScrollAccumulator.reset(getDevice());
-    mTouchButtonAccumulator.reset(getDevice());
+    mCursorButtonAccumulator.reset(getDeviceContext());
+    mCursorScrollAccumulator.reset(getDeviceContext());
+    mTouchButtonAccumulator.reset(getDeviceContext());
 
     mPointerVelocityControl.reset();
     mWheelXVelocityControl.reset();
@@ -1413,26 +1402,12 @@
     mExternalStylusFusionTimeout = LLONG_MAX;
 }
 
-void TouchInputMapper::reportEventForStatistics(nsecs_t evdevTime) {
-    nsecs_t now = systemTime(CLOCK_MONOTONIC);
-    nsecs_t latency = now - evdevTime;
-    mStatistics.addValue(nanoseconds_to_microseconds(latency));
-    nsecs_t timeSinceLastReport = now - mStatistics.lastReportTime;
-    if (timeSinceLastReport > STATISTICS_REPORT_FREQUENCY) {
-        android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mStatistics.min,
-                                   mStatistics.max, mStatistics.mean(), mStatistics.stdev(),
-                                   mStatistics.count);
-        mStatistics.reset(now);
-    }
-}
-
 void TouchInputMapper::process(const RawEvent* rawEvent) {
     mCursorButtonAccumulator.process(rawEvent);
     mCursorScrollAccumulator.process(rawEvent);
     mTouchButtonAccumulator.process(rawEvent);
 
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        reportEventForStatistics(rawEvent->when);
         sync(rawEvent->when);
     }
 }
@@ -1806,8 +1781,8 @@
                     mCurrentVirtualKey.keyCode = virtualKey->keyCode;
                     mCurrentVirtualKey.scanCode = virtualKey->scanCode;
                     mCurrentVirtualKey.ignored =
-                            mContext->shouldDropVirtualKey(when, getDevice(), virtualKey->keyCode,
-                                                           virtualKey->scanCode);
+                            getContext()->shouldDropVirtualKey(when, virtualKey->keyCode,
+                                                               virtualKey->scanCode);
 
                     if (!mCurrentVirtualKey.ignored) {
 #if DEBUG_VIRTUAL_KEYS
@@ -1840,7 +1815,7 @@
     //    is displayed.
     if (mConfig.virtualKeyQuietTime > 0 &&
         !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
-        mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime);
+        getContext()->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime);
     }
     return false;
 }
@@ -1850,10 +1825,10 @@
     int32_t keyCode = mCurrentVirtualKey.keyCode;
     int32_t scanCode = mCurrentVirtualKey.scanCode;
     nsecs_t downTime = mCurrentVirtualKey.downTime;
-    int32_t metaState = mContext->getGlobalMetaState();
+    int32_t metaState = getContext()->getGlobalMetaState();
     policyFlags |= POLICY_FLAG_VIRTUAL;
 
-    NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
+    NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
                        mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode,
                        scanCode, metaState, downTime);
     getListener()->notifyKey(&args);
@@ -1866,7 +1841,6 @@
         int32_t buttonState = mCurrentCookedState.buttonState;
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
                        buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                       mCurrentCookedState.deviceTimestamp,
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
@@ -1887,7 +1861,6 @@
             // The listener takes care of batching moves so we don't have to deal with that here.
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
                            buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                           mCurrentCookedState.deviceTimestamp,
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
@@ -1919,7 +1892,7 @@
             uint32_t upId = upIdBits.clearFirstMarkedBit();
 
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
-                           metaState, buttonState, 0, mCurrentCookedState.deviceTimestamp,
+                           metaState, buttonState, 0,
                            mLastCookedState.cookedPointerData.pointerProperties,
                            mLastCookedState.cookedPointerData.pointerCoords,
                            mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,
@@ -1933,8 +1906,7 @@
         if (moveNeeded && !moveIdBits.isEmpty()) {
             ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                           buttonState, 0, mCurrentCookedState.deviceTimestamp,
-                           mCurrentCookedState.cookedPointerData.pointerProperties,
+                           buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1,
                            mOrientedXPrecision, mOrientedYPrecision, mDownTime);
@@ -1951,7 +1923,7 @@
             }
 
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
-                           metaState, buttonState, 0, mCurrentCookedState.deviceTimestamp,
+                           metaState, buttonState, 0,
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,
@@ -1966,7 +1938,7 @@
          !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) {
         int32_t metaState = getContext()->getGlobalMetaState();
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
-                       mLastCookedState.buttonState, 0, mLastCookedState.deviceTimestamp,
+                       mLastCookedState.buttonState, 0,
                        mLastCookedState.cookedPointerData.pointerProperties,
                        mLastCookedState.cookedPointerData.pointerCoords,
                        mLastCookedState.cookedPointerData.idToIndex,
@@ -1983,7 +1955,6 @@
         if (!mSentHoverEnter) {
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
                            metaState, mCurrentRawState.buttonState, 0,
-                           mCurrentCookedState.deviceTimestamp,
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex,
@@ -1993,7 +1964,7 @@
         }
 
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                       mCurrentRawState.buttonState, 0, mCurrentCookedState.deviceTimestamp,
+                       mCurrentRawState.buttonState, 0,
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex,
@@ -2012,7 +1983,6 @@
         buttonState &= ~actionButton;
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
                        actionButton, 0, metaState, buttonState, 0,
-                       mCurrentCookedState.deviceTimestamp,
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
@@ -2029,7 +1999,7 @@
         int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit());
         buttonState |= actionButton;
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton,
-                       0, metaState, buttonState, 0, mCurrentCookedState.deviceTimestamp,
+                       0, metaState, buttonState, 0,
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
@@ -2048,7 +2018,6 @@
     uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;
 
     mCurrentCookedState.cookedPointerData.clear();
-    mCurrentCookedState.deviceTimestamp = mCurrentRawState.deviceTimestamp;
     mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;
     mCurrentCookedState.cookedPointerData.hoveringIdBits =
             mCurrentRawState.rawPointerData.hoveringIdBits;
@@ -2220,15 +2189,13 @@
         // TODO: Adjust coverage coords?
         float xTransformed = in.x, yTransformed = in.y;
         mAffineTransform.applyTo(xTransformed, yTransformed);
+        rotateAndScale(xTransformed, yTransformed);
 
         // Adjust X, Y, and coverage coords for surface orientation.
-        float x, y;
         float left, top, right, bottom;
 
         switch (mSurfaceOrientation) {
             case DISPLAY_ORIENTATION_90:
-                x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
-                y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
                 left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
                 right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
                 bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
@@ -2241,8 +2208,6 @@
                 }
                 break;
             case DISPLAY_ORIENTATION_180:
-                x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale;
-                y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;
                 left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
                 right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
                 bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
@@ -2255,8 +2220,6 @@
                 }
                 break;
             case DISPLAY_ORIENTATION_270:
-                x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale;
-                y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
                 left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
                 right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
                 bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
@@ -2269,8 +2232,6 @@
                 }
                 break;
             default:
-                x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
-                y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
                 left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
                 right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
                 bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
@@ -2281,8 +2242,8 @@
         // Write output coords.
         PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];
         out.clear();
-        out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+        out.setAxisValue(AMOTION_EVENT_AXIS_X, xTransformed);
+        out.setAxisValue(AMOTION_EVENT_AXIS_Y, yTransformed);
         out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
         out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);
         out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);
@@ -2446,7 +2407,7 @@
     if (!dispatchedGestureIdBits.isEmpty()) {
         if (cancelPreviousGesture) {
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
-                           buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
+                           buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                            mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
                            mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
                            mPointerGesture.downTime);
@@ -2465,7 +2426,7 @@
 
                 dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
                                metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                               /* deviceTimestamp */ 0, mPointerGesture.lastGestureProperties,
+                               mPointerGesture.lastGestureProperties,
                                mPointerGesture.lastGestureCoords,
                                mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
                                0, mPointerGesture.downTime);
@@ -2478,7 +2439,7 @@
     // Send motion events for all pointers that moved.
     if (moveNeeded) {
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
+                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.currentGestureProperties,
                        mPointerGesture.currentGestureCoords,
                        mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
@@ -2498,8 +2459,7 @@
             }
 
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
-                           metaState, buttonState, 0,
-                           /* deviceTimestamp */ 0, mPointerGesture.currentGestureProperties,
+                           metaState, buttonState, 0, mPointerGesture.currentGestureProperties,
                            mPointerGesture.currentGestureCoords,
                            mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
                            0, mPointerGesture.downTime);
@@ -2509,7 +2469,7 @@
     // Send motion events for hover.
     if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) {
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
+                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.currentGestureProperties,
                        mPointerGesture.currentGestureCoords,
                        mPointerGesture.currentGestureIdToIndex,
@@ -2533,11 +2493,10 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
 
         const int32_t displayId = mPointerController->getDisplayId();
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
-                              metaState, buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE,
-                              /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0,
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                              buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                              1, &pointerProperties, &pointerCoords, 0, 0, x, y,
                               mPointerGesture.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
@@ -2566,7 +2525,7 @@
         int32_t metaState = getContext()->getGlobalMetaState();
         int32_t buttonState = mCurrentRawState.buttonState;
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
-                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
+                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
                        mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
                        0, 0, mPointerGesture.downTime);
@@ -3436,28 +3395,30 @@
     int32_t metaState = getContext()->getGlobalMetaState();
     int32_t displayId = mViewport.displayId;
 
-    if (mPointerController != nullptr) {
-        if (down || hovering) {
-            mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
-            mPointerController->clearSpots();
-            mPointerController->setButtonState(mCurrentRawState.buttonState);
-            mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
-        } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
-            mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
-        }
-        displayId = mPointerController->getDisplayId();
+    if (down || hovering) {
+        mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
+        mPointerController->clearSpots();
+        mPointerController->setButtonState(mCurrentRawState.buttonState);
+        mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
+    } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
+        mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
     }
+    displayId = mPointerController->getDisplayId();
+
+    float xCursorPosition;
+    float yCursorPosition;
+    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
 
     if (mPointerSimple.down && !down) {
         mPointerSimple.down = false;
 
         // Send up.
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
                               mLastRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1,
-                              &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
-                              mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
                               /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
@@ -3466,12 +3427,12 @@
         mPointerSimple.hovering = false;
 
         // Send hover exit.
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
-                              metaState, mLastRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1,
-                              &mPointerSimple.lastProperties, &mPointerSimple.lastCoords,
-                              mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime,
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
+                              mLastRawState.buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
                               /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
@@ -3482,25 +3443,24 @@
             mPointerSimple.downTime = when;
 
             // Send down.
-            NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+            NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
                                   displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
                                   metaState, mCurrentRawState.buttonState,
-                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
-                                  /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties,
-                                  &mPointerSimple.currentCoords, mOrientedXPrecision,
-                                  mOrientedYPrecision, mPointerSimple.downTime,
-                                  /* videoFrames */ {});
+                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&args);
         }
 
         // Send move.
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
                               mCurrentRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1,
-                              &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                              mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime,
-                              /* videoFrames */ {});
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+                              &mPointerSimple.currentCoords, mOrientedXPrecision,
+                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                              mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -3509,25 +3469,24 @@
             mPointerSimple.hovering = true;
 
             // Send hover enter.
-            NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
+            NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
                                   displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
                                   metaState, mCurrentRawState.buttonState,
-                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
-                                  /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties,
-                                  &mPointerSimple.currentCoords, mOrientedXPrecision,
-                                  mOrientedYPrecision, mPointerSimple.downTime,
-                                  /* videoFrames */ {});
+                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&args);
         }
 
         // Send hover move.
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
-                              metaState, mCurrentRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1,
-                              &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                              mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime,
-                              /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                              mCurrentRawState.buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+                              &mPointerSimple.currentCoords, mOrientedXPrecision,
+                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                              mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -3543,12 +3502,12 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-        NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
-                              displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
+                              policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
                               mCurrentRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1,
-                              &mPointerSimple.currentProperties, &pointerCoords,
-                              mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+                              &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
                               /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
@@ -3572,7 +3531,7 @@
 void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
                                       int32_t action, int32_t actionButton, int32_t flags,
                                       int32_t metaState, int32_t buttonState, int32_t edgeFlags,
-                                      uint32_t deviceTimestamp, const PointerProperties* properties,
+                                      const PointerProperties* properties,
                                       const PointerCoords* coords, const uint32_t* idToIndex,
                                       BitSet32 idBits, int32_t changedId, float xPrecision,
                                       float yPrecision, nsecs_t downTime) {
@@ -3607,16 +3566,21 @@
             ALOG_ASSERT(false);
         }
     }
-    const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE);
+    float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+    float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+    if (mDeviceMode == DEVICE_MODE_POINTER) {
+        mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+    }
+    const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
     const int32_t deviceId = getDeviceId();
-    std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId);
+    std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
     std::for_each(frames.begin(), frames.end(),
                   [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
-    NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId,
-                          policyFlags, action, actionButton, flags, metaState, buttonState,
-                          MotionClassification::NONE, edgeFlags, deviceTimestamp, pointerCount,
-                          pointerProperties, pointerCoords, xPrecision, yPrecision, downTime,
-                          std::move(frames));
+    NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags,
+                          action, actionButton, flags, metaState, buttonState,
+                          MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
+                          pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
+                          downTime, std::move(frames));
     getListener()->notifyMotion(&args);
 }
 
@@ -3650,24 +3614,52 @@
     return changed;
 }
 
-void TouchInputMapper::fadePointer() {
-    if (mPointerController != nullptr) {
-        mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
-    }
-}
-
 void TouchInputMapper::cancelTouch(nsecs_t when) {
     abortPointerUsage(when, 0 /*policyFlags*/);
     abortTouches(when, 0 /* policyFlags*/);
 }
 
+// Transform raw coordinate to surface coordinate
+void TouchInputMapper::rotateAndScale(float& x, float& y) {
+    // Scale to surface coordinate.
+    const float xScaled = float(x - mRawPointerAxes.x.minValue) * mXScale;
+    const float yScaled = float(y - mRawPointerAxes.y.minValue) * mYScale;
+
+    // Rotate to surface coordinate.
+    // 0 - no swap and reverse.
+    // 90 - swap x/y and reverse y.
+    // 180 - reverse x, y.
+    // 270 - swap x/y and reverse x.
+    switch (mSurfaceOrientation) {
+        case DISPLAY_ORIENTATION_0:
+            x = xScaled + mXTranslate;
+            y = yScaled + mYTranslate;
+            break;
+        case DISPLAY_ORIENTATION_90:
+            y = mSurfaceRight - xScaled;
+            x = yScaled + mYTranslate;
+            break;
+        case DISPLAY_ORIENTATION_180:
+            x = mSurfaceRight - xScaled;
+            y = mSurfaceBottom - yScaled;
+            break;
+        case DISPLAY_ORIENTATION_270:
+            y = xScaled + mXTranslate;
+            x = mSurfaceBottom - yScaled;
+            break;
+        default:
+            assert(false);
+    }
+}
+
 bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
-    const float scaledX = x * mXScale;
-    const float scaledY = y * mYScale;
+    const float xScaled = (x - mRawPointerAxes.x.minValue) * mXScale;
+    const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale;
+
     return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
-            scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth &&
+            xScaled >= mSurfaceLeft && xScaled <= mSurfaceRight &&
             y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
-            scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight;
+            yScaled >= mSurfaceTop && yScaled <= mSurfaceBottom;
 }
 
 const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
@@ -3913,7 +3905,7 @@
     return true;
 }
 
-std::optional<int32_t> TouchInputMapper::getAssociatedDisplay() {
+std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() {
     if (mParameters.hasAssociatedDisplay) {
         if (mDeviceMode == DEVICE_MODE_POINTER) {
             return std::make_optional(mPointerController->getDisplayId());
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index b445981..7f811a0 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -28,68 +28,6 @@
 
 namespace android {
 
-/**
- * Basic statistics information.
- * Keep track of min, max, average, and standard deviation of the received samples.
- * Used to report latency information about input events.
- */
-struct LatencyStatistics {
-    float min;
-    float max;
-    // Sum of all samples
-    float sum;
-    // Sum of squares of all samples
-    float sum2;
-    // The number of samples
-    size_t count;
-    // The last time statistics were reported.
-    nsecs_t lastReportTime;
-
-    LatencyStatistics() { reset(systemTime(SYSTEM_TIME_MONOTONIC)); }
-
-    inline void addValue(float x) {
-        if (x < min) {
-            min = x;
-        }
-        if (x > max) {
-            max = x;
-        }
-        sum += x;
-        sum2 += x * x;
-        count++;
-    }
-
-    // Get the average value. Should not be called if no samples have been added.
-    inline float mean() {
-        if (count == 0) {
-            return 0;
-        }
-        return sum / count;
-    }
-
-    // Get the standard deviation. Should not be called if no samples have been added.
-    inline float stdev() {
-        if (count == 0) {
-            return 0;
-        }
-        float average = mean();
-        return sqrt(sum2 / count - average * average);
-    }
-
-    /**
-     * Reset internal state. The variable 'when' is the time when the data collection started.
-     * Call this to start a new data collection window.
-     */
-    inline void reset(nsecs_t when) {
-        max = 0;
-        min = std::numeric_limits<float>::max();
-        sum = 0;
-        sum2 = 0;
-        count = 0;
-        lastReportTime = when;
-    }
-};
-
 /* Raw axis information from the driver. */
 struct RawPointerAxes {
     RawAbsoluteAxisInfo x;
@@ -194,26 +132,26 @@
 
 class TouchInputMapper : public InputMapper {
 public:
-    explicit TouchInputMapper(InputDevice* device);
+    explicit TouchInputMapper(InputDeviceContext& deviceContext);
     virtual ~TouchInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
-    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
-    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
     virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
-                                       const int32_t* keyCodes, uint8_t* outFlags);
+                                       const int32_t* keyCodes, uint8_t* outFlags) override;
 
-    virtual void fadePointer();
-    virtual void cancelTouch(nsecs_t when);
-    virtual void timeoutExpired(nsecs_t when);
-    virtual void updateExternalStylusState(const StylusState& state);
-    virtual std::optional<int32_t> getAssociatedDisplay();
+    virtual void cancelTouch(nsecs_t when) override;
+    virtual void timeoutExpired(nsecs_t when) override;
+    virtual void updateExternalStylusState(const StylusState& state) override;
+    virtual std::optional<int32_t> getAssociatedDisplayId() override;
 
 protected:
     CursorButtonAccumulator mCursorButtonAccumulator;
@@ -358,7 +296,6 @@
 
     struct RawState {
         nsecs_t when;
-        uint32_t deviceTimestamp;
 
         // Raw pointer sample data.
         RawPointerData rawPointerData;
@@ -371,7 +308,6 @@
 
         void copyFrom(const RawState& other) {
             when = other.when;
-            deviceTimestamp = other.deviceTimestamp;
             rawPointerData.copyFrom(other.rawPointerData);
             buttonState = other.buttonState;
             rawVScroll = other.rawVScroll;
@@ -380,7 +316,6 @@
 
         void clear() {
             when = 0;
-            deviceTimestamp = 0;
             rawPointerData.clear();
             buttonState = 0;
             rawVScroll = 0;
@@ -389,7 +324,6 @@
     };
 
     struct CookedState {
-        uint32_t deviceTimestamp;
         // Cooked pointer sample data.
         CookedPointerData cookedPointerData;
 
@@ -401,7 +335,6 @@
         int32_t buttonState;
 
         void copyFrom(const CookedState& other) {
-            deviceTimestamp = other.deviceTimestamp;
             cookedPointerData.copyFrom(other.cookedPointerData);
             fingerIdBits = other.fingerIdBits;
             stylusIdBits = other.stylusIdBits;
@@ -410,7 +343,6 @@
         }
 
         void clear() {
-            deviceTimestamp = 0;
             cookedPointerData.clear();
             fingerIdBits.clear();
             stylusIdBits.clear();
@@ -475,12 +407,16 @@
     // The surface orientation, width and height set by configureSurface().
     // The width and height are derived from the viewport but are specified
     // in the natural orientation.
+    // They could be used for calculating diagonal, scaling factors, and virtual keys.
+    int32_t mRawSurfaceWidth;
+    int32_t mRawSurfaceHeight;
+
     // The surface origin specifies how the surface coordinates should be translated
     // to align with the logical display coordinate space.
-    int32_t mSurfaceWidth;
-    int32_t mSurfaceHeight;
     int32_t mSurfaceLeft;
     int32_t mSurfaceTop;
+    int32_t mSurfaceRight;
+    int32_t mSurfaceBottom;
 
     // Similar to the surface coordinates, but in the raw display coordinate space rather than in
     // the logical coordinate space.
@@ -759,9 +695,6 @@
     VelocityControl mWheelXVelocityControl;
     VelocityControl mWheelYVelocityControl;
 
-    // Latency statistics for touch events
-    struct LatencyStatistics mStatistics;
-
     std::optional<DisplayViewport> findViewport();
 
     void resetExternalStylus();
@@ -811,10 +744,9 @@
     // it is the first / last pointer to go down / up.
     void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action,
                         int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
-                        int32_t edgeFlags, uint32_t deviceTimestamp,
-                        const PointerProperties* properties, const PointerCoords* coords,
-                        const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId,
-                        float xPrecision, float yPrecision, nsecs_t downTime);
+                        int32_t edgeFlags, const PointerProperties* properties,
+                        const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
+                        int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
 
     // Updates pointer coords and properties for pointers with specified ids that have moved.
     // Returns true if any of them changed.
@@ -828,11 +760,10 @@
 
     static void assignPointerIds(const RawState* last, RawState* current);
 
-    void reportEventForStatistics(nsecs_t evdevTime);
-
     const char* modeToString(DeviceMode deviceMode);
+    void rotateAndScale(float& x, float& y);
 };
 
 } // namespace android
 
-#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
+#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index a27fab4..7665680 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#include "Macros.h"
+#include "../Macros.h"
 
 #include "VibratorInputMapper.h"
 
 namespace android {
 
-VibratorInputMapper::VibratorInputMapper(InputDevice* device)
-      : InputMapper(device), mVibrating(false) {}
+VibratorInputMapper::VibratorInputMapper(InputDeviceContext& deviceContext)
+      : InputMapper(deviceContext), mVibrating(false) {}
 
 VibratorInputMapper::~VibratorInputMapper() {}
 
@@ -100,12 +100,12 @@
 #if DEBUG_VIBRATOR
         ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration);
 #endif
-        getEventHub()->vibrate(getDeviceId(), duration);
+        getDeviceContext().vibrate(duration);
     } else {
 #if DEBUG_VIBRATOR
         ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
 #endif
-        getEventHub()->cancelVibrate(getDeviceId());
+        getDeviceContext().cancelVibrate();
     }
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
     mNextStepTime = now + duration;
@@ -120,7 +120,7 @@
 #if DEBUG_VIBRATOR
     ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
 #endif
-    getEventHub()->cancelVibrate(getDeviceId());
+    getDeviceContext().cancelVibrate();
 }
 
 void VibratorInputMapper::dump(std::string& dump) {
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index 6b33f48..f69fdde 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -23,17 +23,18 @@
 
 class VibratorInputMapper : public InputMapper {
 public:
-    explicit VibratorInputMapper(InputDevice* device);
+    explicit VibratorInputMapper(InputDeviceContext& deviceContext);
     virtual ~VibratorInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
-    virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
-    virtual void cancelVibrate(int32_t token);
-    virtual void timeoutExpired(nsecs_t when);
-    virtual void dump(std::string& dump);
+    virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+                         int32_t token) override;
+    virtual void cancelVibrate(int32_t token) override;
+    virtual void timeoutExpired(nsecs_t when) override;
+    virtual void dump(std::string& dump) override;
 
 private:
     bool mVibrating;
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
index 0337d51..2d7d73b 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
@@ -25,15 +25,15 @@
     clearButtons();
 }
 
-void CursorButtonAccumulator::reset(InputDevice* device) {
-    mBtnLeft = device->isKeyPressed(BTN_LEFT);
-    mBtnRight = device->isKeyPressed(BTN_RIGHT);
-    mBtnMiddle = device->isKeyPressed(BTN_MIDDLE);
-    mBtnBack = device->isKeyPressed(BTN_BACK);
-    mBtnSide = device->isKeyPressed(BTN_SIDE);
-    mBtnForward = device->isKeyPressed(BTN_FORWARD);
-    mBtnExtra = device->isKeyPressed(BTN_EXTRA);
-    mBtnTask = device->isKeyPressed(BTN_TASK);
+void CursorButtonAccumulator::reset(InputDeviceContext& deviceContext) {
+    mBtnLeft = deviceContext.isKeyPressed(BTN_LEFT);
+    mBtnRight = deviceContext.isKeyPressed(BTN_RIGHT);
+    mBtnMiddle = deviceContext.isKeyPressed(BTN_MIDDLE);
+    mBtnBack = deviceContext.isKeyPressed(BTN_BACK);
+    mBtnSide = deviceContext.isKeyPressed(BTN_SIDE);
+    mBtnForward = deviceContext.isKeyPressed(BTN_FORWARD);
+    mBtnExtra = deviceContext.isKeyPressed(BTN_EXTRA);
+    mBtnTask = deviceContext.isKeyPressed(BTN_TASK);
 }
 
 void CursorButtonAccumulator::clearButtons() {
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index d912310..9e15906 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -21,14 +21,14 @@
 
 namespace android {
 
-class InputDevice;
+class InputDeviceContext;
 struct RawEvent;
 
 /* Keeps track of the state of mouse or touch pad buttons. */
 class CursorButtonAccumulator {
 public:
     CursorButtonAccumulator();
-    void reset(InputDevice* device);
+    void reset(InputDeviceContext& deviceContext);
 
     void process(const RawEvent* rawEvent);
 
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
index d744096..0714694 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
@@ -25,12 +25,12 @@
     clearRelativeAxes();
 }
 
-void CursorScrollAccumulator::configure(InputDevice* device) {
-    mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL);
-    mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL);
+void CursorScrollAccumulator::configure(InputDeviceContext& deviceContext) {
+    mHaveRelWheel = deviceContext.hasRelativeAxis(REL_WHEEL);
+    mHaveRelHWheel = deviceContext.hasRelativeAxis(REL_HWHEEL);
 }
 
-void CursorScrollAccumulator::reset(InputDevice* device) {
+void CursorScrollAccumulator::reset(InputDeviceContext& deviceContext) {
     clearRelativeAxes();
 }
 
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
index 85f331f..1649559 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
@@ -21,7 +21,7 @@
 
 namespace android {
 
-class InputDevice;
+class InputDeviceContext;
 struct RawEvent;
 
 /* Keeps track of cursor scrolling motions. */
@@ -29,8 +29,8 @@
 class CursorScrollAccumulator {
 public:
     CursorScrollAccumulator();
-    void configure(InputDevice* device);
-    void reset(InputDevice* device);
+    void configure(InputDeviceContext& deviceContext);
+    void reset(InputDeviceContext& deviceContext);
 
     void process(const RawEvent* rawEvent);
     void finishSync();
diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
index e9ba727..27b8e40 100644
--- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
@@ -25,14 +25,14 @@
     clearAbsoluteAxes();
 }
 
-void SingleTouchMotionAccumulator::reset(InputDevice* device) {
-    mAbsX = device->getAbsoluteAxisValue(ABS_X);
-    mAbsY = device->getAbsoluteAxisValue(ABS_Y);
-    mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE);
-    mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH);
-    mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE);
-    mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X);
-    mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y);
+void SingleTouchMotionAccumulator::reset(InputDeviceContext& deviceContext) {
+    mAbsX = deviceContext.getAbsoluteAxisValue(ABS_X);
+    mAbsY = deviceContext.getAbsoluteAxisValue(ABS_Y);
+    mAbsPressure = deviceContext.getAbsoluteAxisValue(ABS_PRESSURE);
+    mAbsToolWidth = deviceContext.getAbsoluteAxisValue(ABS_TOOL_WIDTH);
+    mAbsDistance = deviceContext.getAbsoluteAxisValue(ABS_DISTANCE);
+    mAbsTiltX = deviceContext.getAbsoluteAxisValue(ABS_TILT_X);
+    mAbsTiltY = deviceContext.getAbsoluteAxisValue(ABS_TILT_Y);
 }
 
 void SingleTouchMotionAccumulator::clearAbsoluteAxes() {
diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
index 75f8a96..4c011f1 100644
--- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
@@ -21,7 +21,7 @@
 
 namespace android {
 
-class InputDevice;
+class InputDeviceContext;
 struct RawEvent;
 
 /* Keeps track of the state of single-touch protocol. */
@@ -30,7 +30,7 @@
     SingleTouchMotionAccumulator();
 
     void process(const RawEvent* rawEvent);
-    void reset(InputDevice* device);
+    void reset(InputDeviceContext& deviceContext);
 
     inline int32_t getAbsoluteX() const { return mAbsX; }
     inline int32_t getAbsoluteY() const { return mAbsY; }
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index d2f06c8..86153d3 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -25,29 +25,31 @@
     clearButtons();
 }
 
-void TouchButtonAccumulator::configure(InputDevice* device) {
-    mHaveBtnTouch = device->hasKey(BTN_TOUCH);
-    mHaveStylus = device->hasKey(BTN_TOOL_PEN) || device->hasKey(BTN_TOOL_RUBBER) ||
-            device->hasKey(BTN_TOOL_BRUSH) || device->hasKey(BTN_TOOL_PENCIL) ||
-            device->hasKey(BTN_TOOL_AIRBRUSH);
+void TouchButtonAccumulator::configure(InputDeviceContext& deviceContext) {
+    mHaveBtnTouch = deviceContext.hasScanCode(BTN_TOUCH);
+    mHaveStylus = deviceContext.hasScanCode(BTN_TOOL_PEN) ||
+            deviceContext.hasScanCode(BTN_TOOL_RUBBER) ||
+            deviceContext.hasScanCode(BTN_TOOL_BRUSH) ||
+            deviceContext.hasScanCode(BTN_TOOL_PENCIL) ||
+            deviceContext.hasScanCode(BTN_TOOL_AIRBRUSH);
 }
 
-void TouchButtonAccumulator::reset(InputDevice* device) {
-    mBtnTouch = device->isKeyPressed(BTN_TOUCH);
-    mBtnStylus = device->isKeyPressed(BTN_STYLUS);
+void TouchButtonAccumulator::reset(InputDeviceContext& deviceContext) {
+    mBtnTouch = deviceContext.isKeyPressed(BTN_TOUCH);
+    mBtnStylus = deviceContext.isKeyPressed(BTN_STYLUS);
     // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch
-    mBtnStylus2 = device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0);
-    mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER);
-    mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN);
-    mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER);
-    mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH);
-    mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL);
-    mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH);
-    mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE);
-    mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS);
-    mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP);
-    mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP);
-    mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP);
+    mBtnStylus2 = deviceContext.isKeyPressed(BTN_STYLUS2) || deviceContext.isKeyPressed(BTN_0);
+    mBtnToolFinger = deviceContext.isKeyPressed(BTN_TOOL_FINGER);
+    mBtnToolPen = deviceContext.isKeyPressed(BTN_TOOL_PEN);
+    mBtnToolRubber = deviceContext.isKeyPressed(BTN_TOOL_RUBBER);
+    mBtnToolBrush = deviceContext.isKeyPressed(BTN_TOOL_BRUSH);
+    mBtnToolPencil = deviceContext.isKeyPressed(BTN_TOOL_PENCIL);
+    mBtnToolAirbrush = deviceContext.isKeyPressed(BTN_TOOL_AIRBRUSH);
+    mBtnToolMouse = deviceContext.isKeyPressed(BTN_TOOL_MOUSE);
+    mBtnToolLens = deviceContext.isKeyPressed(BTN_TOOL_LENS);
+    mBtnToolDoubleTap = deviceContext.isKeyPressed(BTN_TOOL_DOUBLETAP);
+    mBtnToolTripleTap = deviceContext.isKeyPressed(BTN_TOOL_TRIPLETAP);
+    mBtnToolQuadTap = deviceContext.isKeyPressed(BTN_TOOL_QUADTAP);
 }
 
 void TouchButtonAccumulator::clearButtons() {
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index 65b6bdc..22ebb72 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -21,15 +21,15 @@
 
 namespace android {
 
-class InputDevice;
+class InputDeviceContext;
 struct RawEvent;
 
 /* Keeps track of the state of touch, stylus and tool buttons. */
 class TouchButtonAccumulator {
 public:
     TouchButtonAccumulator();
-    void configure(InputDevice* device);
-    void reset(InputDevice* device);
+    void configure(InputDeviceContext& deviceContext);
+    void reset(InputDeviceContext& deviceContext);
 
     void process(const RawEvent* rawEvent);
 
diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp
index 5956fb0..fbc51da 100644
--- a/services/inputflinger/reporter/Android.bp
+++ b/services/inputflinger/reporter/Android.bp
@@ -17,25 +17,33 @@
     export_include_dirs: ["."],
 }
 
-cc_library_shared {
-    name: "libinputreporter",
-    defaults: ["inputflinger_defaults"],
-
+filegroup {
+    name: "libinputreporter_sources",
     srcs: [
-        "InputReporter.cpp",
+            "InputReporter.cpp",
     ],
+}
 
+cc_defaults {
+    name: "libinputreporter_defaults",
+    srcs: [":libinputreporter_sources"],
     shared_libs: [
         "liblog",
         "libutils",
     ],
-
     header_libs: [
-        "libinputflinger_headers",
+        "libinputreporter_headers",
     ],
+}
 
+cc_library_shared {
+    name: "libinputreporter",
+    defaults: [
+        "inputflinger_defaults",
+        "libinputreporter_defaults",
+    ],
     export_header_lib_headers: [
-        "libinputflinger_headers",
+        "libinputreporter_headers",
     ],
 }
 
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index c4f8626..a0d2f4f 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -1,38 +1,41 @@
-// Build the unit tests.
+// 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.
 
 cc_test {
     name: "inputflinger_tests",
+    defaults: [
+        "inputflinger_defaults",
+        // For all targets inside inputflinger, these tests build all of their sources using their
+        // defaults rather than including them as shared or static libraries. By doing so, the tests
+        // will always run against the compiled version of the inputflinger code rather than the
+        // version on the device.
+        "libinputflinger_base_defaults",
+        "libinputreader_defaults",
+        "libinputreporter_defaults",
+        "libinputdispatcher_defaults",
+        "libinputflinger_defaults",
+    ],
     srcs: [
+        "AnrTracker_test.cpp",
         "BlockingQueue_test.cpp",
+        "EventHub_test.cpp",
         "TestInputListener.cpp",
         "InputClassifier_test.cpp",
         "InputClassifierConverter_test.cpp",
         "InputDispatcher_test.cpp",
         "InputReader_test.cpp",
+        "UinputDevice.cpp",
     ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wextra",
-        "-Wno-unused-parameter",
-    ],
-    shared_libs: [
-        "android.hardware.input.classifier@1.0",
-        "libbase",
-        "libbinder",
-        "libcutils",
-        "liblog",
-        "libutils",
-        "libhardware",
-        "libhardware_legacy",
-        "libui",
-        "libinput",
-        "libinputflinger",
-        "libinputreader",
-        "libinputflinger_base",
-        "libinputservice",
-    ],
-    header_libs: [
-        "libinputreader_headers",
-    ],
+    require_root: true,
 }
diff --git a/services/inputflinger/tests/AnrTracker_test.cpp b/services/inputflinger/tests/AnrTracker_test.cpp
new file mode 100644
index 0000000..b561da1
--- /dev/null
+++ b/services/inputflinger/tests/AnrTracker_test.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../AnrTracker.h"
+
+#include <binder/Binder.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+namespace inputdispatcher {
+
+// --- AnrTrackerTest ---
+
+/**
+ * Add a single entry and ensure it's returned as first, even if the token isn't valid
+ */
+TEST(AnrTrackerTest, SingleEntry_First) {
+    AnrTracker tracker;
+
+    tracker.insert(1, nullptr);
+
+    ASSERT_EQ(1, tracker.firstTimeout());
+    ASSERT_EQ(tracker.firstToken(), nullptr);
+}
+
+TEST(AnrTrackerTest, MultipleEntries_RemoveToken) {
+    AnrTracker tracker;
+
+    sp<IBinder> token1 = new BBinder();
+    sp<IBinder> token2 = new BBinder();
+
+    tracker.insert(1, token1);
+    tracker.insert(2, token2);
+    tracker.insert(3, token1);
+    tracker.insert(4, token2);
+    tracker.insert(5, token1);
+
+    tracker.eraseToken(token1);
+
+    ASSERT_EQ(2, tracker.firstTimeout());
+}
+
+TEST(AnrTrackerTest, AddAndRemove_Empty) {
+    AnrTracker tracker;
+
+    ASSERT_TRUE(tracker.empty());
+
+    tracker.insert(1, nullptr);
+    ASSERT_FALSE(tracker.empty());
+
+    tracker.erase(1, nullptr);
+    ASSERT_TRUE(tracker.empty());
+}
+
+TEST(AnrTrackerTest, Clear) {
+    AnrTracker tracker;
+
+    tracker.insert(1, nullptr);
+    tracker.clear();
+    ASSERT_TRUE(tracker.empty());
+}
+
+TEST(AnrTrackerTest, SingleToken_MaintainsOrder) {
+    AnrTracker tracker;
+
+    ASSERT_TRUE(tracker.empty());
+
+    tracker.insert(2, nullptr);
+    tracker.insert(5, nullptr);
+    tracker.insert(0, nullptr);
+
+    ASSERT_EQ(0, tracker.firstTimeout());
+    ASSERT_EQ(nullptr, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_MaintainsOrder) {
+    AnrTracker tracker;
+
+    sp<IBinder> token1 = new BBinder();
+    sp<IBinder> token2 = new BBinder();
+
+    tracker.insert(2, token1);
+    tracker.insert(5, token2);
+    tracker.insert(0, token2);
+
+    ASSERT_EQ(0, tracker.firstTimeout());
+    ASSERT_EQ(token2, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_IdenticalTimes) {
+    AnrTracker tracker;
+
+    sp<IBinder> token1 = new BBinder();
+    sp<IBinder> token2 = new BBinder();
+
+    tracker.insert(2, token1);
+    tracker.insert(2, token2);
+    tracker.insert(10, token2);
+
+    ASSERT_EQ(2, tracker.firstTimeout());
+    // Doesn't matter which token is returned - both are valid results
+    ASSERT_TRUE(token1 == tracker.firstToken() || token2 == tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, MultipleTokens_IdenticalTimesRemove) {
+    AnrTracker tracker;
+
+    sp<IBinder> token1 = new BBinder();
+    sp<IBinder> token2 = new BBinder();
+
+    tracker.insert(2, token1);
+    tracker.insert(2, token2);
+    tracker.insert(10, token2);
+
+    tracker.erase(2, token2);
+
+    ASSERT_EQ(2, tracker.firstTimeout());
+    ASSERT_EQ(token1, tracker.firstToken());
+}
+
+TEST(AnrTrackerTest, Empty_DoesntCrash) {
+    AnrTracker tracker;
+
+    ASSERT_TRUE(tracker.empty());
+
+    ASSERT_EQ(LONG_LONG_MAX, tracker.firstTimeout());
+    // Can't call firstToken() if tracker.empty()
+}
+
+TEST(AnrTrackerTest, RemoveInvalidItem_DoesntCrash) {
+    AnrTracker tracker;
+
+    tracker.insert(1, nullptr);
+
+    // Remove with non-matching timestamp
+    tracker.erase(2, nullptr);
+    ASSERT_EQ(1, tracker.firstTimeout());
+    ASSERT_EQ(nullptr, tracker.firstToken());
+
+    // Remove with non-matching token
+    tracker.erase(1, new BBinder());
+    ASSERT_EQ(1, tracker.firstTimeout());
+    ASSERT_EQ(nullptr, tracker.firstToken());
+
+    // Remove with both non-matching
+    tracker.erase(2, new BBinder());
+    ASSERT_EQ(1, tracker.firstTimeout());
+    ASSERT_EQ(nullptr, tracker.firstToken());
+}
+
+} // namespace inputdispatcher
+
+} // namespace android
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
new file mode 100644
index 0000000..71731b0
--- /dev/null
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EventHub.h"
+
+#include "UinputDevice.h"
+
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+#include <chrono>
+
+#define TAG "EventHub_test"
+
+using android::createUinputDevice;
+using android::EventHub;
+using android::EventHubInterface;
+using android::InputDeviceIdentifier;
+using android::RawEvent;
+using android::sp;
+using android::UinputHomeKey;
+using std::chrono_literals::operator""ms;
+using std::chrono_literals::operator""s;
+
+static constexpr bool DEBUG = false;
+
+static void dumpEvents(const std::vector<RawEvent>& events) {
+    for (const RawEvent& event : events) {
+        if (event.type >= EventHubInterface::FIRST_SYNTHETIC_EVENT) {
+            switch (event.type) {
+                case EventHubInterface::DEVICE_ADDED:
+                    ALOGI("Device added: %i", event.deviceId);
+                    break;
+                case EventHubInterface::DEVICE_REMOVED:
+                    ALOGI("Device removed: %i", event.deviceId);
+                    break;
+                case EventHubInterface::FINISHED_DEVICE_SCAN:
+                    ALOGI("Finished device scan.");
+                    break;
+            }
+        } else {
+            ALOGI("Device %" PRId32 " : time = %" PRId64 ", type %i, code %i, value %i",
+                  event.deviceId, event.when, event.type, event.code, event.value);
+        }
+    }
+}
+
+// --- EventHubTest ---
+class EventHubTest : public testing::Test {
+protected:
+    std::unique_ptr<EventHubInterface> mEventHub;
+    // We are only going to emulate a single input device currently.
+    std::unique_ptr<UinputHomeKey> mKeyboard;
+    int32_t mDeviceId;
+
+    virtual void SetUp() override {
+        mEventHub = std::make_unique<EventHub>();
+        consumeInitialDeviceAddedEvents();
+        mKeyboard = createUinputDevice<UinputHomeKey>();
+        ASSERT_NO_FATAL_FAILURE(mDeviceId = waitForDeviceCreation());
+    }
+    virtual void TearDown() override {
+        mKeyboard.reset();
+        waitForDeviceClose(mDeviceId);
+        assertNoMoreEvents();
+    }
+
+    /**
+     * Return the device id of the created device.
+     */
+    int32_t waitForDeviceCreation();
+    void waitForDeviceClose(int32_t deviceId);
+    void consumeInitialDeviceAddedEvents();
+    void assertNoMoreEvents();
+    /**
+     * Read events from the EventHub.
+     *
+     * If expectedEvents is set, wait for a significant period of time to try and ensure that
+     * the expected number of events has been read. The number of returned events
+     * may be smaller (if timeout has been reached) or larger than expectedEvents.
+     *
+     * If expectedEvents is not set, return all of the immediately available events.
+     */
+    std::vector<RawEvent> getEvents(std::optional<size_t> expectedEvents = std::nullopt);
+};
+
+std::vector<RawEvent> EventHubTest::getEvents(std::optional<size_t> expectedEvents) {
+    static constexpr size_t EVENT_BUFFER_SIZE = 256;
+    std::array<RawEvent, EVENT_BUFFER_SIZE> eventBuffer;
+    std::vector<RawEvent> events;
+
+    while (true) {
+        std::chrono::milliseconds timeout = 0s;
+        if (expectedEvents) {
+            timeout = 2s;
+        }
+        const size_t count =
+                mEventHub->getEvents(timeout.count(), eventBuffer.data(), eventBuffer.size());
+        if (count == 0) {
+            break;
+        }
+        events.insert(events.end(), eventBuffer.begin(), eventBuffer.begin() + count);
+        if (expectedEvents && events.size() >= *expectedEvents) {
+            break;
+        }
+    }
+    if (DEBUG) {
+        dumpEvents(events);
+    }
+    return events;
+}
+
+/**
+ * Since the test runs on a real platform, there will be existing devices
+ * in addition to the test devices being added. Therefore, when EventHub is first created,
+ * it will return a lot of "device added" type of events.
+ */
+void EventHubTest::consumeInitialDeviceAddedEvents() {
+    std::vector<RawEvent> events = getEvents();
+    std::set<int32_t /*deviceId*/> existingDevices;
+    // All of the events should be DEVICE_ADDED type, except the last one.
+    for (size_t i = 0; i < events.size() - 1; i++) {
+        const RawEvent& event = events[i];
+        EXPECT_EQ(EventHubInterface::DEVICE_ADDED, event.type);
+        existingDevices.insert(event.deviceId);
+    }
+    // None of the existing system devices should be changing while this test is run.
+    // Check that the returned device ids are unique for all of the existing devices.
+    EXPECT_EQ(existingDevices.size(), events.size() - 1);
+    // The last event should be "finished device scan"
+    EXPECT_EQ(EventHubInterface::FINISHED_DEVICE_SCAN, events[events.size() - 1].type);
+}
+
+int32_t EventHubTest::waitForDeviceCreation() {
+    // Wait a little longer than usual, to ensure input device has time to be created
+    std::vector<RawEvent> events = getEvents(2);
+    if (events.size() != 2) {
+        ADD_FAILURE() << "Instead of 2 events, received " << events.size();
+        return 0; // this value is unused
+    }
+    const RawEvent& deviceAddedEvent = events[0];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_ADDED), deviceAddedEvent.type);
+    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId);
+    const int32_t deviceId = deviceAddedEvent.deviceId;
+    EXPECT_EQ(identifier.name, mKeyboard->getName());
+    const RawEvent& finishedDeviceScanEvent = events[1];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
+              finishedDeviceScanEvent.type);
+    return deviceId;
+}
+
+void EventHubTest::waitForDeviceClose(int32_t deviceId) {
+    std::vector<RawEvent> events = getEvents(2);
+    ASSERT_EQ(2U, events.size());
+    const RawEvent& deviceRemovedEvent = events[0];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_REMOVED), deviceRemovedEvent.type);
+    EXPECT_EQ(deviceId, deviceRemovedEvent.deviceId);
+    const RawEvent& finishedDeviceScanEvent = events[1];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
+              finishedDeviceScanEvent.type);
+}
+
+void EventHubTest::assertNoMoreEvents() {
+    std::vector<RawEvent> events = getEvents();
+    ASSERT_TRUE(events.empty());
+}
+
+/**
+ * Ensure that input_events are generated with monotonic clock.
+ * That means input_event should receive a timestamp that is in the future of the time
+ * before the event was sent.
+ * Input system uses CLOCK_MONOTONIC everywhere in the code base.
+ */
+TEST_F(EventHubTest, InputEvent_TimestampIsMonotonic) {
+    nsecs_t lastEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    ASSERT_NO_FATAL_FAILURE(mKeyboard->pressAndReleaseHomeKey());
+
+    std::vector<RawEvent> events = getEvents(4);
+    ASSERT_EQ(4U, events.size()) << "Expected to receive 2 keys and 2 syncs, total of 4 events";
+    for (const RawEvent& event : events) {
+        // Cannot use strict comparison because the events may happen too quickly
+        ASSERT_LE(lastEventTime, event.when) << "Event must have occurred after the key was sent";
+        ASSERT_LT(std::chrono::nanoseconds(event.when - lastEventTime), 100ms)
+                << "Event times are too far apart";
+        lastEventTime = event.when; // Ensure all returned events are monotonic
+    }
+}
diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp
index 813b69e..f58b628 100644
--- a/services/inputflinger/tests/InputClassifierConverter_test.cpp
+++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp
@@ -38,12 +38,15 @@
     coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 2);
     coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.5);
     static constexpr nsecs_t downTime = 2;
-    NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/,
-            AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN,
-            0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE,
-            AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/,
-            1/*pointerCount*/, &properties, &coords, 0/*xPrecision*/, 0/*yPrecision*/,
-            downTime, {}/*videoFrames*/);
+    NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/,
+                                AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/,
+                                AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/,
+                                AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
+                                AMOTION_EVENT_EDGE_FLAG_NONE, 1 /*pointerCount*/, &properties,
+                                &coords, 0 /*xPrecision*/, 0 /*yPrecision*/,
+                                AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
+                                {} /*videoFrames*/);
     return motionArgs;
 }
 
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index 4cddec5..ab74a04 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -41,12 +41,15 @@
     coords.setAxisValue(AMOTION_EVENT_AXIS_X, 1);
     coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
     static constexpr nsecs_t downTime = 2;
-    NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/,
-            AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN,
-            0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE,
-            AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/,
-            1/*pointerCount*/, &properties, &coords, 0/*xPrecision*/, 0/*yPrecision*/,
-            downTime, {}/*videoFrames*/);
+    NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/,
+                                AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/,
+                                AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/,
+                                AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
+                                AMOTION_EVENT_EDGE_FLAG_NONE, 1 /*pointerCount*/, &properties,
+                                &coords, 0 /*xPrecision*/, 0 /*yPrecision*/,
+                                AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
+                                {} /*videoFrames*/);
     return motionArgs;
 }
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 551bee1..1a133dc 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,12 +16,19 @@
 
 #include "../dispatcher/InputDispatcher.h"
 
-#include <InputDispatcherThread.h>
-
+#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 #include <binder/Binder.h>
+#include <input/Input.h>
 
 #include <gtest/gtest.h>
 #include <linux/input.h>
+#include <cinttypes>
+#include <thread>
+#include <unordered_set>
+#include <vector>
+
+using android::base::StringPrintf;
 
 namespace android::inputdispatcher {
 
@@ -38,6 +45,22 @@
 static const int32_t INJECTOR_PID = 999;
 static const int32_t INJECTOR_UID = 1001;
 
+struct PointF {
+    float x;
+    float y;
+};
+
+/**
+ * Return a DOWN key event with KEYCODE_A.
+ */
+static KeyEvent getTestKeyEvent() {
+    KeyEvent event;
+
+    event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
+                     INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
+                     ARBITRARY_TIME, ARBITRARY_TIME);
+    return event;
+}
 
 // --- FakeInputDispatcherPolicy ---
 
@@ -50,140 +73,287 @@
 
 public:
     FakeInputDispatcherPolicy() {
-        mInputEventFiltered = false;
-        mTime = -1;
-        mAction = -1;
-        mDisplayId = -1;
-        mOnPointerDownToken.clear();
     }
 
-    void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyMotionArgs* args) {
-        ASSERT_TRUE(mInputEventFiltered)
-                << "Expected filterInputEvent() to have been called.";
-
-        ASSERT_EQ(mTime, args->eventTime)
-                << "Expected time of filtered event was not matched";
-        ASSERT_EQ(mAction, args->action)
-                << "Expected action of filtered event was not matched";
-        ASSERT_EQ(mDisplayId, args->displayId)
-                << "Expected displayId of filtered event was not matched";
-
-        reset();
+    void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
+        assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
+                                        args.displayId);
     }
 
-    void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyKeyArgs* args) {
-        ASSERT_TRUE(mInputEventFiltered)
-                << "Expected filterInputEvent() to have been called.";
-
-        ASSERT_EQ(mTime, args->eventTime)
-                << "Expected time of filtered event was not matched";
-        ASSERT_EQ(mAction, args->action)
-                << "Expected action of filtered event was not matched";
-        ASSERT_EQ(mDisplayId, args->displayId)
-                << "Expected displayId of filtered event was not matched";
-
-        reset();
+    void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) {
+        assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_MOTION, args.eventTime, args.action,
+                                        args.displayId);
     }
 
     void assertFilterInputEventWasNotCalled() {
-        ASSERT_FALSE(mInputEventFiltered)
-                << "Expected filterInputEvent() to not have been called.";
+        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);
+        mConfigurationChangedTime = std::nullopt;
+    }
+
+    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);
+        EXPECT_EQ(args.policyFlags, mLastNotifySwitch->policyFlags);
+        EXPECT_EQ(args.switchValues, mLastNotifySwitch->switchValues);
+        EXPECT_EQ(args.switchMask, mLastNotifySwitch->switchMask);
+        mLastNotifySwitch = std::nullopt;
     }
 
     void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
-        ASSERT_EQ(mOnPointerDownToken, touchedToken)
-                << "Expected token from onPointerDownOutsideFocus was not matched";
-        reset();
+        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";
+    }
+
+    // This function must be called soon after the expected ANR timer starts,
+    // because we are also checking how much time has passed.
+    void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout,
+                                  const sp<InputApplicationHandle>& expectedApplication,
+                                  const sp<IBinder>& expectedToken) {
+        std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData;
+        ASSERT_NO_FATAL_FAILURE(anrData = getNotifyAnrData(timeout));
+        ASSERT_EQ(expectedApplication, anrData.first);
+        ASSERT_EQ(expectedToken, anrData.second);
+    }
+
+    std::pair<sp<InputApplicationHandle>, sp<IBinder>> getNotifyAnrData(
+            std::chrono::nanoseconds timeout) {
+        const std::chrono::time_point start = std::chrono::steady_clock::now();
+        std::unique_lock lock(mLock);
+        std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
+        android::base::ScopedLockAssertion assumeLocked(mLock);
+
+        // If there is an ANR, Dispatcher won't be idle because there are still events
+        // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
+        // before checking if ANR was called.
+        // Since dispatcher is not guaranteed to call notifyAnr right away, we need to provide
+        // it some time to act. 100ms seems reasonable.
+        mNotifyAnr.wait_for(lock, timeToWait, [this]() REQUIRES(mLock) {
+            return !mAnrApplications.empty() && !mAnrWindowTokens.empty();
+        });
+        const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
+        if (mAnrApplications.empty() || mAnrWindowTokens.empty()) {
+            ADD_FAILURE() << "Did not receive ANR callback";
+        }
+        // Ensure that the ANR didn't get raised too early. We can't be too strict here because
+        // the dispatcher started counting before this function was called
+        if (std::chrono::abs(timeout - waited) > 100ms) {
+            ADD_FAILURE() << "ANR was raised too early or too late. Expected "
+                          << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()
+                          << "ms, but waited "
+                          << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
+                          << "ms instead";
+        }
+        std::pair<sp<InputApplicationHandle>, sp<IBinder>> result =
+                std::make_pair(mAnrApplications.front(), mAnrWindowTokens.front());
+        mAnrApplications.pop();
+        mAnrWindowTokens.pop();
+        return result;
+    }
+
+    void assertNotifyAnrWasNotCalled() {
+        std::scoped_lock lock(mLock);
+        ASSERT_TRUE(mAnrApplications.empty());
+        ASSERT_TRUE(mAnrWindowTokens.empty());
+    }
+
+    void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) {
+        mConfig.keyRepeatTimeout = timeout;
+        mConfig.keyRepeatDelay = delay;
+    }
+
+    void setAnrTimeout(std::chrono::nanoseconds timeout) { mAnrTimeout = timeout; }
+
 private:
-    bool mInputEventFiltered;
-    nsecs_t mTime;
-    int32_t mAction;
-    int32_t mDisplayId;
-    sp<IBinder> mOnPointerDownToken;
+    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) {
+    // ANR handling
+    std::queue<sp<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
+    std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock);
+    std::condition_variable mNotifyAnr;
+    std::chrono::nanoseconds mAnrTimeout = 0ms;
+
+    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&) {
-        return 0;
+    virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& application,
+                              const sp<IBinder>& windowToken, const std::string&) override {
+        std::scoped_lock lock(mLock);
+        mAnrApplications.push(application);
+        mAnrWindowTokens.push(windowToken);
+        mNotifyAnr.notify_all();
+        return mAnrTimeout.count();
     }
 
-    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) {
+    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);
-                mTime = keyEvent->getEventTime();
-                mAction = keyEvent->getAction();
-                mDisplayId = keyEvent->getDisplayId();
+                mFilteredEvent = std::make_unique<KeyEvent>(*keyEvent);
                 break;
             }
 
             case AINPUT_EVENT_TYPE_MOTION: {
                 const MotionEvent* motionEvent = static_cast<const MotionEvent*>(inputEvent);
-                mTime = motionEvent->getEventTime();
-                mAction = motionEvent->getAction();
-                mDisplayId = motionEvent->getDisplayId();
+                mFilteredEvent = std::make_unique<MotionEvent>(*motionEvent);
                 break;
             }
         }
-
-        mInputEventFiltered = true;
         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, uint32_t, uint32_t, uint32_t) {
+    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 reset() {
-        mInputEventFiltered = false;
-        mTime = -1;
-        mAction = -1;
-        mDisplayId = -1;
-        mOnPointerDownToken.clear();
+    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);
+
+        if (type == AINPUT_EVENT_TYPE_KEY) {
+            const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
+            EXPECT_EQ(keyEvent.getEventTime(), eventTime);
+            EXPECT_EQ(keyEvent.getAction(), action);
+            EXPECT_EQ(keyEvent.getDisplayId(), displayId);
+        } else if (type == AINPUT_EVENT_TYPE_MOTION) {
+            const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
+            EXPECT_EQ(motionEvent.getEventTime(), eventTime);
+            EXPECT_EQ(motionEvent.getAction(), action);
+            EXPECT_EQ(motionEvent.getDisplayId(), displayId);
+        } else {
+            FAIL() << "Unknown type: " << type;
+        }
+
+        mFilteredEvent = nullptr;
     }
 };
 
+// --- HmacKeyManagerTest ---
+
+class HmacKeyManagerTest : public testing::Test {
+protected:
+    HmacKeyManager mHmacKeyManager;
+};
+
+/**
+ * Ensure that separate calls to sign the same data are generating the same key.
+ * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
+ * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
+ * tests.
+ */
+TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) {
+    KeyEvent event = getTestKeyEvent();
+    VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
+
+    std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(verifiedEvent);
+    std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(verifiedEvent);
+    ASSERT_EQ(hmac1, hmac2);
+}
+
+/**
+ * Ensure that changes in VerifiedKeyEvent produce a different hmac.
+ */
+TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) {
+    KeyEvent event = getTestKeyEvent();
+    VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
+    std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(verifiedEvent);
+
+    verifiedEvent.deviceId += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.source += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.eventTimeNanos += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.displayId += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.action += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.downTimeNanos += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.flags += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.keyCode += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.scanCode += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.metaState += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.repeatCount += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+}
 
 // --- InputDispatcherTest ---
 
@@ -191,23 +361,34 @@
 protected:
     sp<FakeInputDispatcherPolicy> mFakePolicy;
     sp<InputDispatcher> mDispatcher;
-    sp<InputDispatcherThread> mDispatcherThread;
 
-    virtual void SetUp() {
+    virtual void SetUp() override {
         mFakePolicy = new FakeInputDispatcherPolicy();
         mDispatcher = new InputDispatcher(mFakePolicy);
         mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
         //Start InputDispatcher thread
-        mDispatcherThread = new InputDispatcherThread(mDispatcher);
-        mDispatcherThread->run("InputDispatcherTest", PRIORITY_URGENT_DISPLAY);
+        ASSERT_EQ(OK, mDispatcher->start());
     }
 
-    virtual void TearDown() {
-        mDispatcherThread->requestExit();
-        mDispatcherThread.clear();
+    virtual void TearDown() override {
+        ASSERT_EQ(OK, mDispatcher->stop());
         mFakePolicy.clear();
         mDispatcher.clear();
     }
+
+    /**
+     * Used for debugging when writing the test
+     */
+    void dumpDispatcherState() {
+        std::string dump;
+        mDispatcher->dump(dump);
+        std::stringstream ss(dump);
+        std::string to;
+
+        while (std::getline(ss, to, '\n')) {
+            ALOGE("%s", to.c_str());
+        }
+    }
 };
 
 
@@ -215,21 +396,22 @@
     KeyEvent event;
 
     // Rejects undefined key actions.
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
-            /*action*/ -1, 0,
-            AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
-    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
-            &event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
+    event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
+                     INVALID_HMAC,
+                     /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
+                     ARBITRARY_TIME);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 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(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
-            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))
+    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, 0ms, 0))
             << "Should reject key events with ACTION_MULTIPLE.";
 }
 
@@ -250,281 +432,520 @@
     constexpr MotionClassification classification = MotionClassification::NONE;
 
     // Rejects undefined motion actions.
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
-            /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            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))
+    event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */,
+                     1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with undefined action.";
 
     // Rejects pointer down with invalid index.
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
-            AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            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))
+    event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_POINTER_DOWN |
+                             (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with pointer down index too large.";
 
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
-            AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            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))
+    event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_POINTER_DOWN |
+                             (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with pointer down index too small.";
 
     // Rejects pointer up with invalid index.
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
-            AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            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))
+    event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_POINTER_UP |
+                             (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with pointer up index too large.";
 
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
-            AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            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))
+    event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_POINTER_UP |
+                             (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with pointer up index too small.";
 
     // Rejects motion events with invalid number of pointers.
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
-            AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            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))
+    event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
+                     1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 0, pointerProperties, pointerCoords);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with 0 pointers.";
 
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
-            AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            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))
+    event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
+                     1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, 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.
     pointerProperties[0].id = -1;
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
-            AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            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))
+    event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
+                     1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, 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;
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
-            AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            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))
+    event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
+                     1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, 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.
     pointerProperties[0].id = 1;
     pointerProperties[1].id = 1;
-    event.initialize(DEVICE_ID, source, DISPLAY_ID,
-            AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            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))
+    event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification,
+                     1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 2, pointerProperties, pointerCoords);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                            INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0))
             << "Should reject motion events with duplicate pointer ids.";
 }
 
+/* Test InputDispatcher for notifyConfigurationChanged and notifySwitch events */
+
+TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) {
+    constexpr nsecs_t eventTime = 20;
+    NotifyConfigurationChangedArgs args(10 /*id*/, eventTime);
+    mDispatcher->notifyConfigurationChanged(&args);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+
+    mFakePolicy->assertNotifyConfigurationChangedWasCalled(eventTime);
+}
+
+TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) {
+    NotifySwitchArgs args(10 /*id*/, 20 /*eventTime*/, 0 /*policyFlags*/, 1 /*switchValues*/,
+                          2 /*switchMask*/);
+    mDispatcher->notifySwitch(&args);
+
+    // InputDispatcher adds POLICY_FLAG_TRUSTED because the event went through InputListener
+    args.policyFlags |= POLICY_FLAG_TRUSTED;
+    mFakePolicy->assertNotifySwitchWasCalled(args);
+}
+
 // --- InputDispatcherTest SetInputWindowTest ---
-static const int32_t INJECT_EVENT_TIMEOUT = 500;
-static const int32_t DISPATCHING_TIMEOUT = 100;
+static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms;
+static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s;
 
 class FakeApplicationHandle : public InputApplicationHandle {
 public:
-    FakeApplicationHandle() {}
+    FakeApplicationHandle() {
+        mInfo.name = "Fake Application";
+        mInfo.token = new BBinder();
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+    }
     virtual ~FakeApplicationHandle() {}
 
-    virtual bool updateInfo() {
-        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+    virtual bool updateInfo() override {
         return true;
     }
+
+    void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
+        mInfo.dispatchingTimeout = timeout.count();
+    }
 };
 
 class FakeInputReceiver {
 public:
-    void consumeEvent(int32_t expectedEventType, int32_t expectedDisplayId,
-            int32_t expectedFlags = 0) {
+    explicit FakeInputReceiver(const sp<InputChannel>& clientChannel, const std::string name)
+          : mName(name) {
+        mConsumer = std::make_unique<InputConsumer>(clientChannel);
+    }
+
+    InputEvent* consume() {
+        InputEvent* event;
+        std::optional<uint32_t> consumeSeq = receiveEvent(&event);
+        if (!consumeSeq) {
+            return nullptr;
+        }
+        finishEvent(*consumeSeq);
+        return event;
+    }
+
+    /**
+     * Receive an event without acknowledging it.
+     * Return the sequence number that could later be used to send finished signal.
+     */
+    std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
         uint32_t consumeSeq;
         InputEvent* event;
-        status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
-            &consumeSeq, &event);
 
-        ASSERT_EQ(OK, status)
-                << mName.c_str() << ": consumer consume should return OK.";
-        ASSERT_TRUE(event != nullptr)
-                << mName.c_str() << ": consumer should have returned non-NULL event.";
+        std::chrono::time_point start = std::chrono::steady_clock::now();
+        status_t status = WOULD_BLOCK;
+        while (status == WOULD_BLOCK) {
+            status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq,
+                                        &event);
+            std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
+            if (elapsed > 100ms) {
+                break;
+            }
+        }
+
+        if (status == WOULD_BLOCK) {
+            // Just means there's no event available.
+            return std::nullopt;
+        }
+
+        if (status != OK) {
+            ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK.";
+            return std::nullopt;
+        }
+        if (event == nullptr) {
+            ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer";
+            return std::nullopt;
+        }
+        if (outEvent != nullptr) {
+            *outEvent = event;
+        }
+        return consumeSeq;
+    }
+
+    /**
+     * To be used together with "receiveEvent" to complete the consumption of an event.
+     */
+    void finishEvent(uint32_t consumeSeq) {
+        const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true);
+        ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
+    }
+
+    void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
+                      int32_t expectedFlags) {
+        InputEvent* event = consume();
+
+        ASSERT_NE(nullptr, event) << mName.c_str()
+                                  << ": consumer should have returned non-NULL event.";
         ASSERT_EQ(expectedEventType, event->getType())
-                << mName.c_str() << ": event type should match.";
+                << mName.c_str() << " expected " << inputEventTypeToString(expectedEventType)
+                << " event, got " << inputEventTypeToString(event->getType()) << " event";
 
-        ASSERT_EQ(expectedDisplayId, event->getDisplayId())
-                << mName.c_str() << ": event displayId should be the same as expected.";
+        EXPECT_EQ(expectedDisplayId, event->getDisplayId());
 
-        int32_t flags;
         switch (expectedEventType) {
             case AINPUT_EVENT_TYPE_KEY: {
-                KeyEvent* typedEvent = static_cast<KeyEvent*>(event);
-                flags = typedEvent->getFlags();
+                const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event);
+                EXPECT_EQ(expectedAction, keyEvent.getAction());
+                EXPECT_EQ(expectedFlags, keyEvent.getFlags());
                 break;
             }
             case AINPUT_EVENT_TYPE_MOTION: {
-                MotionEvent* typedEvent = static_cast<MotionEvent*>(event);
-                flags = typedEvent->getFlags();
+                const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+                EXPECT_EQ(expectedAction, motionEvent.getAction());
+                EXPECT_EQ(expectedFlags, motionEvent.getFlags());
                 break;
             }
+            case AINPUT_EVENT_TYPE_FOCUS: {
+                FAIL() << "Use 'consumeFocusEvent' for FOCUS events";
+            }
             default: {
                 FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
             }
         }
-        ASSERT_EQ(expectedFlags, flags)
-                << mName.c_str() << ": event flags should be the same as expected.";
+    }
 
-        status = mConsumer->sendFinishedSignal(consumeSeq, handled());
-        ASSERT_EQ(OK, status)
-                << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
+    void consumeFocusEvent(bool hasFocus, bool inTouchMode) {
+        InputEvent* event = consume();
+        ASSERT_NE(nullptr, event) << mName.c_str()
+                                  << ": consumer should have returned non-NULL event.";
+        ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType())
+                << "Got " << inputEventTypeToString(event->getType())
+                << " event instead of FOCUS event";
+
+        ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+                << mName.c_str() << ": event displayId should always be NONE.";
+
+        FocusEvent* focusEvent = static_cast<FocusEvent*>(event);
+        EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+        EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
     }
 
     void assertNoEvents() {
-        uint32_t consumeSeq;
-        InputEvent* event;
-        status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
-            &consumeSeq, &event);
-        ASSERT_NE(OK, status)
-                << mName.c_str()
-                << ": should not have received any events, so consume(..) should not return OK.";
+        InputEvent* event = consume();
+        if (event == nullptr) {
+            return;
+        }
+        if (event->getType() == AINPUT_EVENT_TYPE_KEY) {
+            KeyEvent& keyEvent = static_cast<KeyEvent&>(*event);
+            ADD_FAILURE() << "Received key event "
+                          << KeyEvent::actionToString(keyEvent.getAction());
+        } else if (event->getType() == AINPUT_EVENT_TYPE_MOTION) {
+            MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+            ADD_FAILURE() << "Received motion event "
+                          << MotionEvent::actionToString(motionEvent.getAction());
+        } else if (event->getType() == AINPUT_EVENT_TYPE_FOCUS) {
+            FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
+            ADD_FAILURE() << "Received focus event, hasFocus = "
+                          << (focusEvent.getHasFocus() ? "true" : "false");
+        }
+        FAIL() << mName.c_str()
+               << ": should not have received any events, so consume() should return NULL";
     }
 
+    sp<IBinder> getToken() { return mConsumer->getChannel()->getConnectionToken(); }
+
 protected:
-        explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher,
-            const std::string name, int32_t displayId) :
-                mDispatcher(dispatcher), mName(name), mDisplayId(displayId) {
-            InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
-            mConsumer = new InputConsumer(mClientChannel);
-        }
+    std::unique_ptr<InputConsumer> mConsumer;
+    PreallocatedInputEventFactory mEventFactory;
 
-        virtual ~FakeInputReceiver() {
-        }
-
-        // return true if the event has been handled.
-        virtual bool handled() {
-            return false;
-        }
-
-        sp<InputDispatcher> mDispatcher;
-        sp<InputChannel> mServerChannel, mClientChannel;
-        InputConsumer *mConsumer;
-        PreallocatedInputEventFactory mEventFactory;
-
-        std::string mName;
-        int32_t mDisplayId;
+    std::string mName;
 };
 
-class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
+class FakeWindowHandle : public InputWindowHandle {
 public:
     static const int32_t WIDTH = 600;
     static const int32_t HEIGHT = 800;
 
     FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
-        const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
-            FakeInputReceiver(dispatcher, name, displayId),
-            mFocused(false), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) {
-            mServerChannel->setToken(new BBinder());
-            mDispatcher->registerInputChannel(mServerChannel, displayId);
+                     const sp<InputDispatcher>& dispatcher, const std::string name,
+                     int32_t displayId, sp<IBinder> token = nullptr)
+          : mName(name) {
+        if (token == nullptr) {
+            sp<InputChannel> serverChannel, clientChannel;
+            InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
+            mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
+            dispatcher->registerInputChannel(serverChannel);
+            token = serverChannel->getConnectionToken();
+        }
 
-            inputApplicationHandle->updateInfo();
-            mInfo.applicationInfo = *inputApplicationHandle->getInfo();
-    }
+        inputApplicationHandle->updateInfo();
+        mInfo.applicationInfo = *inputApplicationHandle->getInfo();
 
-    virtual bool updateInfo() {
-        mInfo.token = mServerChannel ? mServerChannel->getToken() : nullptr;
-        mInfo.name = mName;
-        mInfo.layoutParamsFlags = mLayoutParamFlags;
+        mInfo.token = token;
+        mInfo.id = sId++;
+        mInfo.name = name;
+        mInfo.layoutParamsFlags = 0;
         mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
-        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
-        mInfo.frameLeft = mFrame.left;
-        mInfo.frameTop = mFrame.top;
-        mInfo.frameRight = mFrame.right;
-        mInfo.frameBottom = mFrame.bottom;
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count();
+        mInfo.frameLeft = 0;
+        mInfo.frameTop = 0;
+        mInfo.frameRight = WIDTH;
+        mInfo.frameBottom = HEIGHT;
         mInfo.globalScaleFactor = 1.0;
-        mInfo.addTouchableRegion(mFrame);
+        mInfo.touchableRegion.clear();
+        mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
         mInfo.visible = true;
         mInfo.canReceiveKeys = true;
-        mInfo.hasFocus = mFocused;
+        mInfo.hasFocus = false;
         mInfo.hasWallpaper = false;
         mInfo.paused = false;
-        mInfo.layer = 0;
         mInfo.ownerPid = INJECTOR_PID;
         mInfo.ownerUid = INJECTOR_UID;
         mInfo.inputFeatures = 0;
-        mInfo.displayId = mDisplayId;
-
-        return true;
+        mInfo.displayId = displayId;
     }
 
-    void setFocus() {
-        mFocused = true;
+    virtual bool updateInfo() { return true; }
+
+    void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; }
+
+    void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
+        mInfo.dispatchingTimeout = timeout.count();
     }
 
+    void setPaused(bool paused) { mInfo.paused = paused; }
+
     void setFrame(const Rect& frame) {
-        mFrame.set(frame);
+        mInfo.frameLeft = frame.left;
+        mInfo.frameTop = frame.top;
+        mInfo.frameRight = frame.right;
+        mInfo.frameBottom = frame.bottom;
+        mInfo.touchableRegion.clear();
+        mInfo.addTouchableRegion(frame);
     }
 
-    void setLayoutParamFlags(int32_t flags) {
-        mLayoutParamFlags = flags;
+    void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; }
+
+    void setWindowScale(float xScale, float yScale) {
+        mInfo.windowXScale = xScale;
+        mInfo.windowYScale = yScale;
     }
 
-    void releaseChannel() {
-        mServerChannel.clear();
-        InputWindowHandle::releaseChannel();
-    }
-protected:
-    virtual bool handled() {
-        return true;
+    void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
+                     expectedFlags);
     }
 
-    bool mFocused;
-    Rect mFrame;
-    int32_t mLayoutParamFlags;
+    void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, expectedDisplayId, expectedFlags);
+    }
+
+    void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+            int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId,
+                     expectedFlags);
+    }
+
+    void consumeMotionMove(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+            int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, expectedDisplayId,
+                     expectedFlags);
+    }
+
+    void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+            int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
+                     expectedFlags);
+    }
+
+    void consumeMotionPointerDown(int32_t pointerIdx,
+            int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) {
+        int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN
+                | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
+    }
+
+    void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+            int32_t expectedFlags = 0) {
+        int32_t action = AMOTION_EVENT_ACTION_POINTER_UP
+                | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
+    }
+
+    void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+            int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId,
+                     expectedFlags);
+    }
+
+    void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) {
+        ASSERT_NE(mInputReceiver, nullptr)
+                << "Cannot consume events from a window with no receiver";
+        mInputReceiver->consumeFocusEvent(hasFocus, inTouchMode);
+    }
+
+    void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
+                      int32_t expectedFlags) {
+        ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver";
+        mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId,
+                                     expectedFlags);
+    }
+
+    std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
+        if (mInputReceiver == nullptr) {
+            ADD_FAILURE() << "Invalid receive event on window with no receiver";
+            return std::nullopt;
+        }
+        return mInputReceiver->receiveEvent(outEvent);
+    }
+
+    void finishEvent(uint32_t sequenceNum) {
+        ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
+        mInputReceiver->finishEvent(sequenceNum);
+    }
+
+    InputEvent* consume() {
+        if (mInputReceiver == nullptr) {
+            return nullptr;
+        }
+        return mInputReceiver->consume();
+    }
+
+    void assertNoEvents() {
+        ASSERT_NE(mInputReceiver, nullptr)
+                << "Call 'assertNoEvents' on a window with an InputReceiver";
+        mInputReceiver->assertNoEvents();
+    }
+
+    sp<IBinder> getToken() { return mInfo.token; }
+
+    const std::string& getName() { return mName; }
+
+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
 };
 
-static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
-        int32_t displayId = ADISPLAY_ID_NONE) {
+std::atomic<int32_t> FakeWindowHandle::sId{1};
+
+static int32_t injectKey(const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount,
+                         int32_t displayId = ADISPLAY_ID_NONE,
+                         int32_t syncMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+                         std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) {
     KeyEvent event;
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // Define a valid key down event.
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
-            AKEY_EVENT_ACTION_DOWN, /* flags */ 0,
-            AKEYCODE_A, KEY_A, AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime);
+    event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
+                     INVALID_HMAC, action, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
+                     repeatCount, currentTime, currentTime);
 
     // Inject event until dispatch out.
-    return dispatcher->injectInputEvent(
-            &event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
-            INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+    return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, syncMode,
+                                        injectionTimeout,
+                                        POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
 }
 
-static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
-        int32_t displayId, int32_t x = 100, int32_t y = 200) {
+static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
+                             int32_t displayId = ADISPLAY_ID_NONE) {
+    return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId);
+}
+
+static int32_t injectKeyUp(const sp<InputDispatcher>& dispatcher,
+                           int32_t displayId = ADISPLAY_ID_NONE) {
+    return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId);
+}
+
+static int32_t injectMotionEvent(
+        const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId,
+        const PointF& position,
+        const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                        AMOTION_EVENT_INVALID_CURSOR_POSITION},
+        std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
+        int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
+        nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) {
     MotionEvent event;
     PointerProperties pointerProperties[1];
     PointerCoords pointerCoords[1];
@@ -534,57 +955,80 @@
     pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
 
     pointerCoords[0].clear();
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x);
+    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y);
 
-    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid motion down event.
-    event.initialize(DEVICE_ID, source, displayId,
-            AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0,
-            AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
-            /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
-            /* yPrecision */ 0, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties,
-            pointerCoords);
+    event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action,
+                     /* actionButton */ 0,
+                     /* flags */ 0,
+                     /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+                     /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
+                     /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y,
+                     eventTime, eventTime,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
 
     // Inject event until dispatch out.
-    return dispatcher->injectInputEvent(
-            &event,
-            INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT,
-            INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+    return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode,
+                                        injectionTimeout,
+                                        POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+}
+
+static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source,
+                                int32_t displayId, const PointF& location = {100, 200}) {
+    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location);
+}
+
+static int32_t injectMotionUp(const sp<InputDispatcher>& dispatcher, int32_t source,
+                              int32_t displayId, const PointF& location = {100, 200}) {
+    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
 }
 
 static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid key event.
-    NotifyKeyArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
-            displayId, POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0,
-            AKEYCODE_A, KEY_A, AMETA_NONE, currentTime);
+    NotifyKeyArgs args(/* id */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
+                       POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0, AKEYCODE_A, KEY_A,
+                       AMETA_NONE, currentTime);
+
+    return args;
+}
+
+static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId,
+                                           const std::vector<PointF>& points) {
+    size_t pointerCount = points.size();
+    if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) {
+        EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer";
+    }
+
+    PointerProperties pointerProperties[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+
+    for (size_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].clear();
+        pointerProperties[i].id = i;
+        pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+        pointerCoords[i].clear();
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, points[i].x);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, points[i].y);
+    }
+
+    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    // Define a valid motion event.
+    NotifyMotionArgs args(/* id */ 0, currentTime, DEVICE_ID, source, displayId,
+                          POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
+                          AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+                          AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
+                          pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {});
 
     return args;
 }
 
 static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
-    PointerProperties pointerProperties[1];
-    PointerCoords pointerCoords[1];
-
-    pointerProperties[0].clear();
-    pointerProperties[0].id = 0;
-    pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
-
-    pointerCoords[0].clear();
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
-
-    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
-    // Define a valid motion event.
-    NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId,
-            POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
-            AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
-            AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, pointerProperties,
-            pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, currentTime,
-            /* videoFrames */ {});
-
-    return args;
+    return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
 }
 
 TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
@@ -592,16 +1036,62 @@
     sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
             ADISPLAY_ID_DEFAULT);
 
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(window);
-
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
             AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
 
     // Window should receive motion event.
-    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    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.
@@ -612,17 +1102,13 @@
     sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
             ADISPLAY_ID_DEFAULT);
 
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(windowTop);
-    inputWindowHandles.push_back(windowSecond);
-
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
             AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
 
     // Top window should receive the touch down event. Second window should not receive anything.
-    windowTop->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    windowTop->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowSecond->assertNoEvents();
 }
 
@@ -636,19 +1122,17 @@
     // Set focused application.
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
-    // Expect one focus window exist in display.
-    windowSecond->setFocus();
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(windowTop);
-    inputWindowHandles.push_back(windowSecond);
+    // Display should have only one focused window
+    windowSecond->setFocus(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
 
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    windowSecond->consumeFocusEvent(true);
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
 
     // Focused window should receive event.
     windowTop->assertNoEvents();
-    windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
 }
 
 TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) {
@@ -662,18 +1146,16 @@
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
     // Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first)
-    windowTop->setFocus();
-    windowSecond->setFocus();
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(windowTop);
-    inputWindowHandles.push_back(windowSecond);
+    windowTop->setFocus(true);
+    windowSecond->setFocus(true);
 
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+    windowTop->consumeFocusEvent(true);
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
 
     // Top focused window should receive event.
-    windowTop->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    windowTop->consumeKeyDown(ADISPLAY_ID_NONE);
     windowSecond->assertNoEvents();
 }
 
@@ -688,14 +1170,12 @@
     // Set focused application.
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
-    windowTop->setFocus();
-    windowSecond->setFocus();
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(windowTop);
-    inputWindowHandles.push_back(windowSecond);
+    windowTop->setFocus(true);
+    windowSecond->setFocus(true);
     // Release channel for window is no longer valid.
     windowTop->releaseChannel();
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
+    windowSecond->consumeFocusEvent(true);
 
     // Test inject a key down, should dispatch to a valid window.
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
@@ -703,41 +1183,675 @@
 
     // Top window is invalid, so it should not receive any input event.
     windowTop->assertNoEvents();
-    windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+}
+
+TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+    sp<FakeWindowHandle> windowLeft =
+            new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+    windowLeft->setFrame(Rect(0, 0, 600, 800));
+    windowLeft->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+    sp<FakeWindowHandle> windowRight =
+            new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+    windowRight->setFrame(Rect(600, 0, 1200, 800));
+    windowRight->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}});
+
+    // Inject an event with coordinate in the area of right window, with mouse cursor in the area of
+    // left window. This event should be dispatched to the left window.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
+                                ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400}));
+    windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    windowRight->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+    window->setFocus(true);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    window->consumeFocusEvent(true);
+
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+
+    // Window should receive key down event.
+    window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+    // When device reset happens, that key stream should be terminated with FLAG_CANCELED
+    // on the app side.
+    NotifyDeviceResetArgs args(10 /*id*/, 20 /*eventTime*/, DEVICE_ID);
+    mDispatcher->notifyDeviceReset(&args);
+    window->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
+                         AKEY_EVENT_FLAG_CANCELED);
+}
+
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Window should receive motion down event.
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    // When device reset happens, that motion stream should be terminated with ACTION_CANCEL
+    // on the app side.
+    NotifyDeviceResetArgs args(10 /*id*/, 20 /*eventTime*/, DEVICE_ID);
+    mDispatcher->notifyDeviceReset(&args);
+    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, ADISPLAY_ID_DEFAULT,
+                         0 /*expectedFlags*/);
+}
+
+TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+    // Create a couple of windows
+    sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
+            "First Window", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
+            "Second Window", ADISPLAY_ID_DEFAULT);
+
+    // Add the windows to the dispatcher
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+
+    // Send down to the first window
+    NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&downMotionArgs);
+    // Only the first window should get the down event
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+
+    // Transfer touch focus to the second window
+    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+    // The first window gets cancel and the second gets down
+    firstWindow->consumeMotionCancel();
+    secondWindow->consumeMotionDown();
+
+    // Send up event to the second window
+    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&upMotionArgs);
+    // The first  window gets no events and the second gets up
+    firstWindow->assertNoEvents();
+    secondWindow->consumeMotionUp();
+}
+
+TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+    PointF touchPoint = {10, 10};
+
+    // Create a couple of windows
+    sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
+            "First Window", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
+            "Second Window", ADISPLAY_ID_DEFAULT);
+
+    // Add the windows to the dispatcher
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+
+    // Send down to the first window
+    NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint});
+    mDispatcher->notifyMotion(&downMotionArgs);
+    // Only the first window should get the down event
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+
+    // Send pointer down to the first window
+    NotifyMotionArgs pointerDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
+            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint});
+    mDispatcher->notifyMotion(&pointerDownMotionArgs);
+    // Only the first window should get the pointer down event
+    firstWindow->consumeMotionPointerDown(1);
+    secondWindow->assertNoEvents();
+
+    // Transfer touch focus to the second window
+    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+    // The first window gets cancel and the second gets down and pointer down
+    firstWindow->consumeMotionCancel();
+    secondWindow->consumeMotionDown();
+    secondWindow->consumeMotionPointerDown(1);
+
+    // Send pointer up to the second window
+    NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP
+            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint});
+    mDispatcher->notifyMotion(&pointerUpMotionArgs);
+    // The first window gets nothing and the second gets pointer up
+    firstWindow->assertNoEvents();
+    secondWindow->consumeMotionPointerUp(1);
+
+    // Send up event to the second window
+    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&upMotionArgs);
+    // The first window gets nothing and the second gets up
+    firstWindow->assertNoEvents();
+    secondWindow->consumeMotionUp();
+}
+
+TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+    // Create a non touch modal window that supports split touch
+    sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
+            "First Window", ADISPLAY_ID_DEFAULT);
+    firstWindow->setFrame(Rect(0, 0, 600, 400));
+    firstWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
+            | InputWindowInfo::FLAG_SPLIT_TOUCH);
+
+    // Create a non touch modal window that supports split touch
+    sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
+            "Second Window", ADISPLAY_ID_DEFAULT);
+    secondWindow->setFrame(Rect(0, 400, 600, 800));
+    secondWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
+            | InputWindowInfo::FLAG_SPLIT_TOUCH);
+
+    // Add the windows to the dispatcher
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}});
+
+    PointF pointInFirst = {300, 200};
+    PointF pointInSecond = {300, 600};
+
+    // Send down to the first window
+    NotifyMotionArgs firstDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst});
+    mDispatcher->notifyMotion(&firstDownMotionArgs);
+    // Only the first window should get the down event
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+
+    // Send down to the second window
+    NotifyMotionArgs secondDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
+            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond});
+    mDispatcher->notifyMotion(&secondDownMotionArgs);
+    // The first window gets a move and the second a down
+    firstWindow->consumeMotionMove();
+    secondWindow->consumeMotionDown();
+
+    // Transfer touch focus to the second window
+    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+    // The first window gets cancel and the new gets pointer down (it already saw down)
+    firstWindow->consumeMotionCancel();
+    secondWindow->consumeMotionPointerDown(1);
+
+    // Send pointer up to the second window
+    NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP
+            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond});
+    mDispatcher->notifyMotion(&pointerUpMotionArgs);
+    // The first window gets nothing and the second gets pointer up
+    firstWindow->assertNoEvents();
+    secondWindow->consumeMotionPointerUp(1);
+
+    // Send up event to the second window
+    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&upMotionArgs);
+    // The first window gets nothing and the second gets up
+    firstWindow->assertNoEvents();
+    secondWindow->consumeMotionUp();
+}
+
+TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    window->setFocus(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    window->consumeFocusEvent(true);
+
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+
+    // Window should receive key down event.
+    window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+}
+
+TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+    mDispatcher->waitForIdle();
+
+    window->assertNoEvents();
+}
+
+// If a window is touchable, but does not have focus, it should receive motion events, but not keys
+TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    // Send key
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+    // Send motion
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Window should receive only the motion event
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    window->assertNoEvents(); // Key event or focus event will not be received
+}
+
+class FakeMonitorReceiver {
+public:
+    FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
+                        int32_t displayId, bool isGestureMonitor = false) {
+        sp<InputChannel> serverChannel, clientChannel;
+        InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
+        mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
+        dispatcher->registerInputMonitor(serverChannel, displayId, isGestureMonitor);
+    }
+
+    sp<IBinder> getToken() { return mInputReceiver->getToken(); }
+
+    void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN,
+                                     expectedDisplayId, expectedFlags);
+    }
+
+    std::optional<int32_t> receiveEvent() { return mInputReceiver->receiveEvent(); }
+
+    void finishEvent(uint32_t consumeSeq) { return mInputReceiver->finishEvent(consumeSeq); }
+
+    void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN,
+                                     expectedDisplayId, expectedFlags);
+    }
+
+    void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP,
+                                     expectedDisplayId, expectedFlags);
+    }
+
+    void assertNoEvents() { mInputReceiver->assertNoEvents(); }
+
+private:
+    std::unique_ptr<FakeInputReceiver> mInputReceiver;
+};
+
+// Tests for gesture monitors
+TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+                                                      true /*isGestureMonitor*/);
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
+TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+    window->setFocus(true);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    window->consumeFocusEvent(true);
+
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+                                                      true /*isGestureMonitor*/);
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+    monitor.assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStream) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+                                                      true /*isGestureMonitor*/);
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    window->releaseChannel();
+
+    mDispatcher->pilferPointers(monitor.getToken());
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+}
+
+TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) {
+    FakeMonitorReceiver monitor =
+            FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+                                true /*isGestureMonitor*/);
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+    std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
+    ASSERT_TRUE(consumeSeq);
+
+    mFakePolicy->assertNotifyAnrWasCalled(DISPATCHING_TIMEOUT, nullptr, monitor.getToken());
+    monitor.finishEvent(*consumeSeq);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+TEST_F(InputDispatcherTest, TestMoveEvent) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->notifyMotion(&motionArgs);
+    // Window should receive motion down event.
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    motionArgs.action = AMOTION_EVENT_ACTION_MOVE;
+    motionArgs.id += 1;
+    motionArgs.eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    motionArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
+                                             motionArgs.pointerCoords[0].getX() - 10);
+
+    mDispatcher->notifyMotion(&motionArgs);
+    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, ADISPLAY_ID_DEFAULT,
+                         0 /*expectedFlags*/);
+}
+
+/**
+ * Dispatcher has touch mode enabled by default. Typically, the policy overrides that value to
+ * the device default right away. In the test scenario, we check both the default value,
+ * and the action of enabling / disabling.
+ */
+TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+
+    // Set focused application.
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+    window->setFocus(true);
+
+    SCOPED_TRACE("Check default value of touch mode");
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+    SCOPED_TRACE("Remove the window to trigger focus loss");
+    window->setFocus(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/);
+
+    SCOPED_TRACE("Disable touch mode");
+    mDispatcher->setInTouchMode(false);
+    window->setFocus(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    window->consumeFocusEvent(true /*hasFocus*/, false /*inTouchMode*/);
+
+    SCOPED_TRACE("Remove the window to trigger focus loss");
+    window->setFocus(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/);
+
+    SCOPED_TRACE("Enable touch mode again");
+    mDispatcher->setInTouchMode(true);
+    window->setFocus(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+    window->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+    window->setFocus(true);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+    window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN);
+    mDispatcher->notifyKey(&keyArgs);
+
+    InputEvent* event = window->consume();
+    ASSERT_NE(event, nullptr);
+
+    std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
+    ASSERT_NE(verified, nullptr);
+    ASSERT_EQ(verified->type, VerifiedInputEvent::Type::KEY);
+
+    ASSERT_EQ(keyArgs.eventTime, verified->eventTimeNanos);
+    ASSERT_EQ(keyArgs.deviceId, verified->deviceId);
+    ASSERT_EQ(keyArgs.source, verified->source);
+    ASSERT_EQ(keyArgs.displayId, verified->displayId);
+
+    const VerifiedKeyEvent& verifiedKey = static_cast<const VerifiedKeyEvent&>(*verified);
+
+    ASSERT_EQ(keyArgs.action, verifiedKey.action);
+    ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos);
+    ASSERT_EQ(keyArgs.flags & VERIFIED_KEY_EVENT_FLAGS, verifiedKey.flags);
+    ASSERT_EQ(keyArgs.keyCode, verifiedKey.keyCode);
+    ASSERT_EQ(keyArgs.scanCode, verifiedKey.scanCode);
+    ASSERT_EQ(keyArgs.metaState, verifiedKey.metaState);
+    ASSERT_EQ(0, verifiedKey.repeatCount);
+}
+
+TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    InputEvent* event = window->consume();
+    ASSERT_NE(event, nullptr);
+
+    std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
+    ASSERT_NE(verified, nullptr);
+    ASSERT_EQ(verified->type, VerifiedInputEvent::Type::MOTION);
+
+    EXPECT_EQ(motionArgs.eventTime, verified->eventTimeNanos);
+    EXPECT_EQ(motionArgs.deviceId, verified->deviceId);
+    EXPECT_EQ(motionArgs.source, verified->source);
+    EXPECT_EQ(motionArgs.displayId, verified->displayId);
+
+    const VerifiedMotionEvent& verifiedMotion = static_cast<const VerifiedMotionEvent&>(*verified);
+
+    EXPECT_EQ(motionArgs.pointerCoords[0].getX(), verifiedMotion.rawX);
+    EXPECT_EQ(motionArgs.pointerCoords[0].getY(), verifiedMotion.rawY);
+    EXPECT_EQ(motionArgs.action & AMOTION_EVENT_ACTION_MASK, verifiedMotion.actionMasked);
+    EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos);
+    EXPECT_EQ(motionArgs.flags & VERIFIED_MOTION_EVENT_FLAGS, verifiedMotion.flags);
+    EXPECT_EQ(motionArgs.metaState, verifiedMotion.metaState);
+    EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
+}
+
+class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
+protected:
+    static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
+    static constexpr nsecs_t KEY_REPEAT_DELAY = 40 * 1000000;   // 40 ms
+
+    sp<FakeApplicationHandle> mApp;
+    sp<FakeWindowHandle> mWindow;
+
+    virtual void SetUp() override {
+        mFakePolicy = new FakeInputDispatcherPolicy();
+        mFakePolicy->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
+        mDispatcher = new InputDispatcher(mFakePolicy);
+        mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+        ASSERT_EQ(OK, mDispatcher->start());
+
+        setUpWindow();
+    }
+
+    void setUpWindow() {
+        mApp = new FakeApplicationHandle();
+        mWindow = new FakeWindowHandle(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+        mWindow->setFocus(true);
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+
+        mWindow->consumeFocusEvent(true);
+    }
+
+    void sendAndConsumeKeyDown() {
+        NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+        keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Otherwise it won't generate repeat event
+        mDispatcher->notifyKey(&keyArgs);
+
+        // Window should receive key down event.
+        mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+    }
+
+    void expectKeyRepeatOnce(int32_t repeatCount) {
+        SCOPED_TRACE(StringPrintf("Checking event with repeat count %" PRId32, repeatCount));
+        InputEvent* repeatEvent = mWindow->consume();
+        ASSERT_NE(nullptr, repeatEvent);
+
+        uint32_t eventType = repeatEvent->getType();
+        ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, eventType);
+
+        KeyEvent* repeatKeyEvent = static_cast<KeyEvent*>(repeatEvent);
+        uint32_t eventAction = repeatKeyEvent->getAction();
+        EXPECT_EQ(AKEY_EVENT_ACTION_DOWN, eventAction);
+        EXPECT_EQ(repeatCount, repeatKeyEvent->getRepeatCount());
+    }
+
+    void sendAndConsumeKeyUp() {
+        NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+        keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Unless it won't generate repeat event
+        mDispatcher->notifyKey(&keyArgs);
+
+        // Window should receive key down event.
+        mWindow->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
+                              0 /*expectedFlags*/);
+    }
+};
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeat) {
+    sendAndConsumeKeyDown();
+    for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
+        expectKeyRepeatOnce(repeatCount);
+    }
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterUp) {
+    sendAndConsumeKeyDown();
+    expectKeyRepeatOnce(1 /*repeatCount*/);
+    sendAndConsumeKeyUp();
+    mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFromInputDispatcher) {
+    sendAndConsumeKeyDown();
+    for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
+        InputEvent* repeatEvent = mWindow->consume();
+        ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
+        EXPECT_EQ(IdGenerator::Source::INPUT_DISPATCHER,
+                  IdGenerator::getSource(repeatEvent->getId()));
+    }
+}
+
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEventId) {
+    sendAndConsumeKeyDown();
+
+    std::unordered_set<int32_t> idSet;
+    for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) {
+        InputEvent* repeatEvent = mWindow->consume();
+        ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount;
+        int32_t id = repeatEvent->getId();
+        EXPECT_EQ(idSet.end(), idSet.find(id));
+        idSet.insert(id);
+    }
 }
 
 /* Test InputDispatcher for MultiDisplay */
 class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
 public:
     static constexpr int32_t SECOND_DISPLAY_ID = 1;
-    virtual void SetUp() {
+    virtual void SetUp() override {
         InputDispatcherTest::SetUp();
 
         application1 = new FakeApplicationHandle();
         windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1",
                 ADISPLAY_ID_DEFAULT);
-        std::vector<sp<InputWindowHandle>> inputWindowHandles;
-        inputWindowHandles.push_back(windowInPrimary);
+
         // Set focus window for primary display, but focused display would be second one.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
-        windowInPrimary->setFocus();
-        mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+        windowInPrimary->setFocus(true);
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary}}});
+        windowInPrimary->consumeFocusEvent(true);
 
         application2 = new FakeApplicationHandle();
         windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
                 SECOND_DISPLAY_ID);
         // Set focus to second display window.
-        std::vector<sp<InputWindowHandle>> inputWindowHandles_Second;
-        inputWindowHandles_Second.push_back(windowInSecondary);
         // Set focus display to second one.
         mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
         // Set focus window for second display.
         mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
-        windowInSecondary->setFocus();
-        mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
+        windowInSecondary->setFocus(true);
+        mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}});
+        windowInSecondary->consumeFocusEvent(true);
     }
 
-    virtual void TearDown() {
+    virtual void TearDown() override {
         InputDispatcherTest::TearDown();
 
         application1.clear();
@@ -758,7 +1872,7 @@
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
             AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowInSecondary->assertNoEvents();
 
     // Test touch down on second display.
@@ -766,71 +1880,61 @@
             AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     windowInPrimary->assertNoEvents();
-    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+    windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
 }
 
 TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
     // Test inject a key down with display id specified.
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_DEFAULT);
+    windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
     windowInSecondary->assertNoEvents();
 
     // Test inject a key down without display id specified.
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     windowInPrimary->assertNoEvents();
-    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
 
-    // Remove secondary display.
-    std::vector<sp<InputWindowHandle>> noWindows;
-    mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID);
+    // Remove all windows in secondary display.
+    mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}});
 
     // Expect old focus should receive a cancel event.
-    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE,
-            AKEY_EVENT_FLAG_CANCELED);
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
+                                    AKEY_EVENT_FLAG_CANCELED);
 
     // Test inject a key down, should timeout because of no target window.
     ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
             << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT";
     windowInPrimary->assertNoEvents();
+    windowInSecondary->consumeFocusEvent(false);
     windowInSecondary->assertNoEvents();
 }
 
-class FakeMonitorReceiver : public FakeInputReceiver, public RefBase {
-public:
-    FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
-            int32_t displayId, bool isGestureMonitor = false)
-            : FakeInputReceiver(dispatcher, name, displayId) {
-        mServerChannel->setToken(new BBinder());
-        mDispatcher->registerInputMonitor(mServerChannel, displayId, isGestureMonitor);
-    }
-};
-
 // Test per-display input monitors for motion event.
 TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
-    sp<FakeMonitorReceiver> monitorInPrimary =
-            new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
-    sp<FakeMonitorReceiver> monitorInSecondary =
-            new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+    FakeMonitorReceiver monitorInPrimary =
+            FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    FakeMonitorReceiver monitorInSecondary =
+            FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
 
     // Test touch down on primary display.
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
             AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
-    monitorInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowInSecondary->assertNoEvents();
-    monitorInSecondary->assertNoEvents();
+    monitorInSecondary.assertNoEvents();
 
     // Test touch down on second display.
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
             AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     windowInPrimary->assertNoEvents();
-    monitorInPrimary->assertNoEvents();
-    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
-    monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+    monitorInPrimary.assertNoEvents();
+    windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
+    monitorInSecondary.consumeMotionDown(SECOND_DISPLAY_ID);
 
     // Test inject a non-pointer motion event.
     // If specific a display, it will dispatch to the focused window of particular display,
@@ -839,26 +1943,26 @@
         AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     windowInPrimary->assertNoEvents();
-    monitorInPrimary->assertNoEvents();
-    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
-    monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
+    monitorInPrimary.assertNoEvents();
+    windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
+    monitorInSecondary.consumeMotionDown(ADISPLAY_ID_NONE);
 }
 
 // Test per-display input monitors for key event.
 TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
     //Input monitor per display.
-    sp<FakeMonitorReceiver> monitorInPrimary =
-            new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
-    sp<FakeMonitorReceiver> monitorInSecondary =
-            new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+    FakeMonitorReceiver monitorInPrimary =
+            FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    FakeMonitorReceiver monitorInSecondary =
+            FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
 
     // Test inject a key down.
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     windowInPrimary->assertNoEvents();
-    monitorInPrimary->assertNoEvents();
-    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
-    monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    monitorInPrimary.assertNoEvents();
+    windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
+    monitorInSecondary.consumeKeyDown(ADISPLAY_ID_NONE);
 }
 
 class InputFilterTest : public InputDispatcherTest {
@@ -874,9 +1978,9 @@
         motionArgs = generateMotionArgs(
                 AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
         mDispatcher->notifyMotion(&motionArgs);
-
+        ASSERT_TRUE(mDispatcher->waitForIdle());
         if (expectToBeFiltered) {
-            mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&motionArgs);
+            mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
         } else {
             mFakePolicy->assertFilterInputEventWasNotCalled();
         }
@@ -889,9 +1993,10 @@
         mDispatcher->notifyKey(&keyArgs);
         keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP);
         mDispatcher->notifyKey(&keyArgs);
+        ASSERT_TRUE(mDispatcher->waitForIdle());
 
         if (expectToBeFiltered) {
-            mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&keyArgs);
+            mFakePolicy->assertFilterInputEventWasCalled(keyArgs);
         } else {
             mFakePolicy->assertFilterInputEventWasNotCalled();
         }
@@ -934,7 +2039,7 @@
 }
 
 class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
-    virtual void SetUp() {
+    virtual void SetUp() override {
         InputDispatcherTest::SetUp();
 
         sp<FakeApplicationHandle> application = new FakeApplicationHandle();
@@ -945,46 +2050,44 @@
         // window.
         mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
 
-        mWindowFocused = new FakeWindowHandle(application, mDispatcher, "Second",
-                ADISPLAY_ID_DEFAULT);
-        mWindowFocused->setFrame(Rect(50, 50, 100, 100));
-        mWindowFocused->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
-        mWindowFocusedTouchPoint = 60;
+        mFocusedWindow =
+                new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+        mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
+        mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
 
         // Set focused application.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
-        mWindowFocused->setFocus();
+        mFocusedWindow->setFocus(true);
 
         // Expect one focus window exist in display.
-        std::vector<sp<InputWindowHandle>> inputWindowHandles;
-        inputWindowHandles.push_back(mUnfocusedWindow);
-        inputWindowHandles.push_back(mWindowFocused);
-        mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+        mFocusedWindow->consumeFocusEvent(true);
     }
 
-    virtual void TearDown() {
+    virtual void TearDown() override {
         InputDispatcherTest::TearDown();
 
         mUnfocusedWindow.clear();
-        mWindowFocused.clear();
+        mFocusedWindow.clear();
     }
 
 protected:
     sp<FakeWindowHandle> mUnfocusedWindow;
-    sp<FakeWindowHandle> mWindowFocused;
-    int32_t mWindowFocusedTouchPoint;
+    sp<FakeWindowHandle> mFocusedWindow;
+    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";
-    // Call monitor to wait for the command queue to get flushed.
-    mDispatcher->monitor();
+    mUnfocusedWindow->consumeMotionDown();
 
+    ASSERT_TRUE(mDispatcher->waitForIdle());
     mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken());
 }
 
@@ -992,13 +2095,13 @@
 // 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";
-    // Call monitor to wait for the command queue to get flushed.
-    mDispatcher->monitor();
+    mFocusedWindow->consumeMotionDown();
 
-    mFakePolicy->assertOnPointerDownEquals(nullptr);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertOnPointerDownWasNotCalled();
 }
 
 // Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
@@ -1006,10 +2109,10 @@
 TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    // Call monitor to wait for the command queue to get flushed.
-    mDispatcher->monitor();
+    mFocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
 
-    mFakePolicy->assertOnPointerDownEquals(nullptr);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertOnPointerDownWasNotCalled();
 }
 
 // Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
@@ -1017,14 +2120,915 @@
 // onPointerDownOutsideFocus callback.
 TEST_F(InputDispatcherOnPointerDownOutsideFocus,
         OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, mWindowFocusedTouchPoint,
-            mWindowFocusedTouchPoint))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               FOCUSED_WINDOW_TOUCH_POINT))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    // Call monitor to wait for the command queue to get flushed.
-    mDispatcher->monitor();
+    mFocusedWindow->consumeMotionDown();
 
-    mFakePolicy->assertOnPointerDownEquals(nullptr);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertOnPointerDownWasNotCalled();
+}
+
+// These tests ensures we can send touch events to a single client when there are multiple input
+// windows that point to the same client token.
+class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest {
+    virtual void SetUp() override {
+        InputDispatcherTest::SetUp();
+
+        sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+        mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1",
+                                        ADISPLAY_ID_DEFAULT);
+        // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window.
+        // 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->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->setFrame(Rect(100, 100, 200, 200));
+
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}});
+    }
+
+protected:
+    sp<FakeWindowHandle> mWindow1;
+    sp<FakeWindowHandle> mWindow2;
+
+    // Helper function to convert the point from screen coordinates into the window's space
+    static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) {
+        float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft);
+        float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop);
+        return {x, y};
+    }
+
+    void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
+                            const std::vector<PointF>& points) {
+        const std::string name = window->getName();
+        InputEvent* event = window->consume();
+
+        ASSERT_NE(nullptr, event) << name.c_str()
+                                  << ": consumer should have returned non-NULL event.";
+
+        ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
+                << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION)
+                << " event, got " << inputEventTypeToString(event->getType()) << " event";
+
+        const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+        EXPECT_EQ(expectedAction, motionEvent.getAction());
+
+        for (size_t i = 0; i < points.size(); i++) {
+            float expectedX = points[i].x;
+            float expectedY = points[i].y;
+
+            EXPECT_EQ(expectedX, motionEvent.getX(i))
+                    << "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str()
+                    << ", got " << motionEvent.getX(i);
+            EXPECT_EQ(expectedY, motionEvent.getY(i))
+                    << "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str()
+                    << ", got " << motionEvent.getY(i);
+        }
+    }
+};
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) {
+    // Touch Window 1
+    PointF touchedPoint = {10, 10};
+    PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+
+    // Release touch on Window 1
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+    // consume the UP event
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+
+    // Touch Window 2
+    touchedPoint = {150, 150};
+    expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Consuming from window1 since it's the window that has the InputReceiver
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) {
+    mWindow2->setWindowScale(0.5f, 0.5f);
+
+    // Touch Window 1
+    PointF touchedPoint = {10, 10};
+    PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+
+    // Release touch on Window 1
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+    // consume the UP event
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+
+    // Touch Window 2
+    touchedPoint = {150, 150};
+    expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Consuming from window1 since it's the window that has the InputReceiver
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) {
+    mWindow2->setWindowScale(0.5f, 0.5f);
+
+    // Touch Window 1
+    std::vector<PointF> touchedPoints = {PointF{10, 10}};
+    std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+    // Touch Window 2
+    int32_t actionPointerDown =
+            AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+    touchedPoints.emplace_back(PointF{150, 150});
+    expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+    motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Consuming from window1 since it's the window that has the InputReceiver
+    consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) {
+    mWindow2->setWindowScale(0.5f, 0.5f);
+
+    // Touch Window 1
+    std::vector<PointF> touchedPoints = {PointF{10, 10}};
+    std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+    // Touch Window 2
+    int32_t actionPointerDown =
+            AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+    touchedPoints.emplace_back(PointF{150, 150});
+    expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+    motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Consuming from window1 since it's the window that has the InputReceiver
+    consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+
+    // Move both windows
+    touchedPoints = {{20, 20}, {175, 175}};
+    expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+                      getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
+    mWindow1->setWindowScale(0.5f, 0.5f);
+
+    // Touch Window 1
+    std::vector<PointF> touchedPoints = {PointF{10, 10}};
+    std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+    // Touch Window 2
+    int32_t actionPointerDown =
+            AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+    touchedPoints.emplace_back(PointF{150, 150});
+    expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+    motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Consuming from window1 since it's the window that has the InputReceiver
+    consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+
+    // Move both windows
+    touchedPoints = {{20, 20}, {175, 175}};
+    expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+                      getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+}
+
+class InputDispatcherSingleWindowAnr : public InputDispatcherTest {
+    virtual void SetUp() override {
+        InputDispatcherTest::SetUp();
+
+        mApplication = new FakeApplicationHandle();
+        mApplication->setDispatchingTimeout(20ms);
+        mWindow =
+                new FakeWindowHandle(mApplication, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+        mWindow->setFrame(Rect(0, 0, 30, 30));
+        mWindow->setDispatchingTimeout(10ms);
+        mWindow->setFocus(true);
+        // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+        // window.
+        mWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+
+        // Set focused application.
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+        mWindow->consumeFocusEvent(true);
+    }
+
+    virtual void TearDown() override {
+        InputDispatcherTest::TearDown();
+        mWindow.clear();
+    }
+
+protected:
+    sp<FakeApplicationHandle> mApplication;
+    sp<FakeWindowHandle> mWindow;
+    static constexpr PointF WINDOW_LOCATION = {20, 20};
+
+    void tapOnWindow() {
+        ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+                  injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                   WINDOW_LOCATION));
+        ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+                  injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                 WINDOW_LOCATION));
+    }
+};
+
+// Send a tap and respond, which should not cause an ANR.
+TEST_F(InputDispatcherSingleWindowAnr, WhenTouchIsConsumed_NoAnr) {
+    tapOnWindow();
+    mWindow->consumeMotionDown();
+    mWindow->consumeMotionUp();
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// Send a regular key and respond, which should not cause an ANR.
+TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) {
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+    mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// Send an event to the app and have the app not respond right away.
+// When ANR is raised, policy will tell the dispatcher to cancel the events for that window.
+// So InputDispatcher will enqueue ACTION_CANCEL event as well.
+TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               WINDOW_LOCATION));
+
+    std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN
+    ASSERT_TRUE(sequenceNum);
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+
+    // The remaining lines are not really needed for the test, but kept as a sanity check
+    mWindow->finishEvent(*sequenceNum);
+    mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
+                          ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// Send a key to the app and have the app not respond right away.
+TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) {
+    // Inject a key, and don't respond - expect that ANR is called.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher));
+    std::optional<uint32_t> sequenceNum = mWindow->receiveEvent();
+    ASSERT_TRUE(sequenceNum);
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// We have a focused application, but no focused window
+TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) {
+    mWindow->setFocus(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+    mWindow->consumeFocusEvent(false);
+
+    // taps on the window work as normal
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               WINDOW_LOCATION));
+    ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
+    mDispatcher->waitForIdle();
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+
+    // Once a focused event arrives, we get an ANR for this application
+    // We specify the injection timeout to be smaller than the application timeout, to ensure that
+    // injection times out (instead of failing).
+    const int32_t result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+                      INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+    const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// We have a focused application, but no focused window
+// If the policy wants to keep waiting on the focused window to be added, make sure
+// that this timeout extension is honored and ANR is raised again.
+TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) {
+    mWindow->setFocus(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+    mWindow->consumeFocusEvent(false);
+    const std::chrono::duration timeout = 5ms;
+    mFakePolicy->setAnrTimeout(timeout);
+
+    // Once a focused event arrives, we get an ANR for this application
+    // We specify the injection timeout to be smaller than the application timeout, to ensure that
+    // injection times out (instead of failing).
+    const int32_t result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+                      INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+    const std::chrono::duration appTimeout =
+            mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(appTimeout, mApplication, nullptr /*windowToken*/);
+
+    // After the extended time has passed, ANR should be raised again
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+
+    // If we stop extending the timeout, dispatcher should go to idle.
+    // Another ANR may be raised during this time
+    mFakePolicy->setAnrTimeout(0ms);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// We have a focused application, but no focused window
+TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) {
+    mWindow->setFocus(false);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+    mWindow->consumeFocusEvent(false);
+
+    // Once a focused event arrives, we get an ANR for this application
+    const int32_t result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+                      INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+
+    const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/);
+
+    // Future focused events get dropped right away
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, injectKeyDown(mDispatcher));
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mWindow->assertNoEvents();
+}
+
+/**
+ * Ensure that the implementation is valid. Since we are using multiset to keep track of the
+ * ANR timeouts, we are allowing entries with identical timestamps in the same connection.
+ * If we process 1 of the events, but ANR on the second event with the same timestamp,
+ * the ANR mechanism should still work.
+ *
+ * In this test, we are injecting DOWN and UP events with the same timestamps, and acknowledging the
+ * DOWN event, while not responding on the second one.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) {
+    nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                      ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
+                      {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                       AMOTION_EVENT_INVALID_CURSOR_POSITION},
+                      500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime);
+
+    // Now send ACTION_UP, with identical timestamp
+    injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                      ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
+                      {AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                       AMOTION_EVENT_INVALID_CURSOR_POSITION},
+                      500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime);
+
+    // We have now sent down and up. Let's consume first event and then ANR on the second.
+    mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+}
+
+// If an app is not responding to a key event, gesture monitors should continue to receive
+// new motion events
+TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnKey) {
+    FakeMonitorReceiver monitor =
+            FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+                                true /*isGestureMonitor*/);
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT));
+    mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT));
+
+    // Stuck on the ACTION_UP
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+
+    // New tap will go to the gesture monitor, but not to the window
+    tapOnWindow();
+    monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+    mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion
+    mDispatcher->waitForIdle();
+    mWindow->assertNoEvents();
+    monitor.assertNoEvents();
+}
+
+// If an app is not responding to a motion event, gesture monitors should continue to receive
+// new motion events
+TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnMotion) {
+    FakeMonitorReceiver monitor =
+            FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT,
+                                true /*isGestureMonitor*/);
+
+    tapOnWindow();
+    monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+    mWindow->consumeMotionDown();
+    // Stuck on the ACTION_UP
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr, mWindow->getToken());
+
+    // New tap will go to the gesture monitor, but not to the window
+    tapOnWindow();
+    monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+
+    mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion
+    mDispatcher->waitForIdle();
+    mWindow->assertNoEvents();
+    monitor.assertNoEvents();
+}
+
+// If a window is unresponsive, then you get anr. if the window later catches up and starts to
+// process events, you don't get an anr. When the window later becomes unresponsive again, you
+// get an ANR again.
+// 1. tap -> block on ACTION_UP -> receive ANR
+// 2. consume all pending events (= queue becomes healthy again)
+// 3. tap again -> block on ACTION_UP again -> receive ANR second time
+TEST_F(InputDispatcherSingleWindowAnr, SameWindow_CanReceiveAnrTwice) {
+    tapOnWindow();
+
+    mWindow->consumeMotionDown();
+    // Block on ACTION_UP
+    const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+    mWindow->consumeMotionUp(); // Now the connection should be healthy again
+    mDispatcher->waitForIdle();
+    mWindow->assertNoEvents();
+
+    tapOnWindow();
+    mWindow->consumeMotionDown();
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+    mWindow->consumeMotionUp();
+
+    mDispatcher->waitForIdle();
+    mWindow->assertNoEvents();
+}
+
+// If the policy tells us to raise ANR again after some time, ensure that the timeout extension
+// is honored
+TEST_F(InputDispatcherSingleWindowAnr, Policy_CanExtendTimeout) {
+    const std::chrono::duration timeout = 5ms;
+    mFakePolicy->setAnrTimeout(timeout);
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               WINDOW_LOCATION));
+
+    const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(windowTimeout, nullptr /*application*/,
+                                          mWindow->getToken());
+
+    // Since the policy wanted to extend ANR, make sure it is called again after the extension
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/, mWindow->getToken());
+    mFakePolicy->setAnrTimeout(0ms);
+    std::this_thread::sleep_for(windowTimeout);
+    // We are not checking if ANR has been called, because it may have been called again by the
+    // time we set the timeout to 0
+
+    // When the policy finally says stop, we should get ACTION_CANCEL
+    mWindow->consumeMotionDown();
+    mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL,
+                          ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+    mWindow->assertNoEvents();
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not to to the focused window until the motion is processed.
+ *
+ * Warning!!!
+ * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
+ * and the injection timeout that we specify when injecting the key.
+ * We must have the injection timeout (10ms) be smaller than
+ *  KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
+ *
+ * If that value changes, this test should also change.
+ */
+TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) {
+    mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+
+    tapOnWindow();
+    std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
+    ASSERT_TRUE(downSequenceNum);
+    std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent();
+    ASSERT_TRUE(upSequenceNum);
+    // Don't finish the events yet, and send a key
+    // Injection will "succeed" because we will eventually give up and send the key to the focused
+    // window even if motions are still being processed. But because the injection timeout is short,
+    // we will receive INJECTION_TIMED_OUT as the result.
+
+    int32_t result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT,
+                      INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result);
+    // Key will not be sent to the window, yet, because the window is still processing events
+    // and the key remains pending, waiting for the touch events to be processed
+    std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
+    ASSERT_FALSE(keySequenceNum);
+
+    std::this_thread::sleep_for(500ms);
+    // if we wait long enough though, dispatcher will give up, and still send the key
+    // to the focused window, even though we have not yet finished the motion event
+    mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+    mWindow->finishEvent(*downSequenceNum);
+    mWindow->finishEvent(*upSequenceNum);
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not go to the focused window until the motion is processed.
+ * If then a new motion comes in, then the pending key event should be going to the currently
+ * focused window right away.
+ */
+TEST_F(InputDispatcherSingleWindowAnr,
+       PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) {
+    mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}});
+
+    tapOnWindow();
+    std::optional<uint32_t> downSequenceNum = mWindow->receiveEvent();
+    ASSERT_TRUE(downSequenceNum);
+    std::optional<uint32_t> upSequenceNum = mWindow->receiveEvent();
+    ASSERT_TRUE(upSequenceNum);
+    // Don't finish the events yet, and send a key
+    // Injection is async, so it will succeed
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */,
+                        ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_NONE));
+    // At this point, key is still pending, and should not be sent to the application yet.
+    std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent();
+    ASSERT_FALSE(keySequenceNum);
+
+    // Now tap down again. It should cause the pending key to go to the focused window right away.
+    tapOnWindow();
+    mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); // it doesn't matter that we haven't ack'd
+    // the other events yet. We can finish events in any order.
+    mWindow->finishEvent(*downSequenceNum); // first tap's ACTION_DOWN
+    mWindow->finishEvent(*upSequenceNum);   // first tap's ACTION_UP
+    mWindow->consumeMotionDown();
+    mWindow->consumeMotionUp();
+    mWindow->assertNoEvents();
+}
+
+class InputDispatcherMultiWindowAnr : public InputDispatcherTest {
+    virtual void SetUp() override {
+        InputDispatcherTest::SetUp();
+
+        mApplication = new FakeApplicationHandle();
+        mApplication->setDispatchingTimeout(10ms);
+        mUnfocusedWindow =
+                new FakeWindowHandle(mApplication, mDispatcher, "Unfocused", ADISPLAY_ID_DEFAULT);
+        mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
+        // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this
+        // window.
+        // Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped
+        mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
+                                              InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH |
+                                              InputWindowInfo::FLAG_SPLIT_TOUCH);
+
+        mFocusedWindow =
+                new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT);
+        mFocusedWindow->setDispatchingTimeout(10ms);
+        mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
+        mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
+                                            InputWindowInfo::FLAG_SPLIT_TOUCH);
+
+        // Set focused application.
+        mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+        mFocusedWindow->setFocus(true);
+
+        // Expect one focus window exist in display.
+        mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+        mFocusedWindow->consumeFocusEvent(true);
+    }
+
+    virtual void TearDown() override {
+        InputDispatcherTest::TearDown();
+
+        mUnfocusedWindow.clear();
+        mFocusedWindow.clear();
+    }
+
+protected:
+    sp<FakeApplicationHandle> mApplication;
+    sp<FakeWindowHandle> mUnfocusedWindow;
+    sp<FakeWindowHandle> mFocusedWindow;
+    static constexpr PointF UNFOCUSED_WINDOW_LOCATION = {20, 20};
+    static constexpr PointF FOCUSED_WINDOW_LOCATION = {75, 75};
+    static constexpr PointF LOCATION_OUTSIDE_ALL_WINDOWS = {40, 40};
+
+    void tapOnFocusedWindow() { tap(FOCUSED_WINDOW_LOCATION); }
+
+    void tapOnUnfocusedWindow() { tap(UNFOCUSED_WINDOW_LOCATION); }
+
+private:
+    void tap(const PointF& location) {
+        ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+                  injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                   location));
+        ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+                  injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                                 location));
+    }
+};
+
+// If we have 2 windows that are both unresponsive, the one with the shortest timeout
+// should be ANR'd first.
+TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) {
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               FOCUSED_WINDOW_LOCATION))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    mFocusedWindow->consumeMotionDown();
+    mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+                                   ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+    // We consumed all events, so no ANR
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               FOCUSED_WINDOW_LOCATION));
+    std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent();
+    ASSERT_TRUE(unfocusedSequenceNum);
+    std::optional<uint32_t> focusedSequenceNum = mFocusedWindow->receiveEvent();
+    ASSERT_TRUE(focusedSequenceNum);
+
+    const std::chrono::duration timeout =
+            mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+                                          mFocusedWindow->getToken());
+
+    mFocusedWindow->finishEvent(*focusedSequenceNum);
+    mUnfocusedWindow->finishEvent(*unfocusedSequenceNum);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+}
+
+// If we have 2 windows with identical timeouts that are both unresponsive,
+// it doesn't matter which order they should have ANR.
+// But we should receive ANR for both.
+TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout) {
+    // Set the timeout for unfocused window to match the focused window
+    mUnfocusedWindow->setDispatchingTimeout(10ms);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+
+    tapOnFocusedWindow();
+    // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window
+    std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData1 =
+            mFakePolicy->getNotifyAnrData(10ms);
+    std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData2 =
+            mFakePolicy->getNotifyAnrData(0ms);
+
+    // We don't know which window will ANR first. But both of them should happen eventually.
+    ASSERT_TRUE(mFocusedWindow->getToken() == anrData1.second ||
+                mFocusedWindow->getToken() == anrData2.second);
+    ASSERT_TRUE(mUnfocusedWindow->getToken() == anrData1.second ||
+                mUnfocusedWindow->getToken() == anrData2.second);
+
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// If a window is already not responding, the second tap on the same window should be ignored.
+// We should also log an error to account for the dropped event (not tested here).
+// At the same time, FLAG_WATCH_OUTSIDE_TOUCH targets should not receive any events.
+TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) {
+    tapOnFocusedWindow();
+    mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+                                   ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+    // Receive the events, but don't respond
+    std::optional<uint32_t> downEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_DOWN
+    ASSERT_TRUE(downEventSequenceNum);
+    std::optional<uint32_t> upEventSequenceNum = mFocusedWindow->receiveEvent(); // ACTION_UP
+    ASSERT_TRUE(upEventSequenceNum);
+    const std::chrono::duration timeout =
+            mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+                                          mFocusedWindow->getToken());
+
+    // Tap once again
+    // We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               FOCUSED_WINDOW_LOCATION));
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                             FOCUSED_WINDOW_LOCATION));
+    // Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a
+    // valid touch target
+    mUnfocusedWindow->assertNoEvents();
+
+    // Consume the first tap
+    mFocusedWindow->finishEvent(*downEventSequenceNum);
+    mFocusedWindow->finishEvent(*upEventSequenceNum);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    // The second tap did not go to the focused window
+    mFocusedWindow->assertNoEvents();
+    // should not have another ANR after the window just became healthy again
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// If you tap outside of all windows, there will not be ANR
+TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) {
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               LOCATION_OUTSIDE_ALL_WINDOWS));
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+}
+
+// Since the focused window is paused, tapping on it should not produce any events
+TEST_F(InputDispatcherMultiWindowAnr, Window_CanBePaused) {
+    mFocusedWindow->setPaused(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}});
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               FOCUSED_WINDOW_LOCATION));
+
+    std::this_thread::sleep_for(mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT));
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    // Should not ANR because the window is paused, and touches shouldn't go to it
+    mFakePolicy->assertNotifyAnrWasNotCalled();
+
+    mFocusedWindow->assertNoEvents();
+    mUnfocusedWindow->assertNoEvents();
+}
+
+/**
+ * If a window is processing a motion event, and then a key event comes in, the key event should
+ * not to to the focused window until the motion is processed.
+ * If a different window becomes focused at this time, the key should go to that window instead.
+ *
+ * Warning!!!
+ * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT
+ * and the injection timeout that we specify when injecting the key.
+ * We must have the injection timeout (10ms) be smaller than
+ *  KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms).
+ *
+ * If that value changes, this test should also change.
+ */
+TEST_F(InputDispatcherMultiWindowAnr, PendingKey_GoesToNewlyFocusedWindow) {
+    // Set a long ANR timeout to prevent it from triggering
+    mFocusedWindow->setDispatchingTimeout(2s);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+
+    tapOnUnfocusedWindow();
+    std::optional<uint32_t> downSequenceNum = mUnfocusedWindow->receiveEvent();
+    ASSERT_TRUE(downSequenceNum);
+    std::optional<uint32_t> upSequenceNum = mUnfocusedWindow->receiveEvent();
+    ASSERT_TRUE(upSequenceNum);
+    // Don't finish the events yet, and send a key
+    // Injection will succeed because we will eventually give up and send the key to the focused
+    // window even if motions are still being processed.
+
+    int32_t result =
+            injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT,
+                      INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/);
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result);
+    // Key will not be sent to the window, yet, because the window is still processing events
+    // and the key remains pending, waiting for the touch events to be processed
+    std::optional<uint32_t> keySequenceNum = mFocusedWindow->receiveEvent();
+    ASSERT_FALSE(keySequenceNum);
+
+    // Switch the focus to the "unfocused" window that we tapped. Expect the key to go there
+    mFocusedWindow->setFocus(false);
+    mUnfocusedWindow->setFocus(true);
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}});
+
+    // Focus events should precede the key events
+    mUnfocusedWindow->consumeFocusEvent(true);
+    mFocusedWindow->consumeFocusEvent(false);
+
+    // Finish the tap events, which should unblock dispatcher
+    mUnfocusedWindow->finishEvent(*downSequenceNum);
+    mUnfocusedWindow->finishEvent(*upSequenceNum);
+
+    // Now that all queues are cleared and no backlog in the connections, the key event
+    // can finally go to the newly focused "mUnfocusedWindow".
+    mUnfocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+    mFocusedWindow->assertNoEvents();
+    mUnfocusedWindow->assertNoEvents();
+}
+
+// When the touch stream is split across 2 windows, and one of them does not respond,
+// then ANR should be raised and the touch should be canceled for the unresponsive window.
+// The other window should not be affected by that.
+TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) {
+    // Touch Window 1
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {FOCUSED_WINDOW_LOCATION});
+    mDispatcher->notifyMotion(&motionArgs);
+    mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE,
+                                   ADISPLAY_ID_DEFAULT, 0 /*flags*/);
+
+    // Touch Window 2
+    int32_t actionPointerDown =
+            AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+    motionArgs =
+            generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               {FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION});
+    mDispatcher->notifyMotion(&motionArgs);
+
+    const std::chrono::duration timeout =
+            mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
+    mFakePolicy->assertNotifyAnrWasCalled(timeout, nullptr /*application*/,
+                                          mFocusedWindow->getToken());
+
+    mUnfocusedWindow->consumeMotionDown();
+    mFocusedWindow->consumeMotionDown();
+    // Focused window may or may not receive ACTION_MOVE
+    // But it should definitely receive ACTION_CANCEL due to the ANR
+    InputEvent* event;
+    std::optional<int32_t> moveOrCancelSequenceNum = mFocusedWindow->receiveEvent(&event);
+    ASSERT_TRUE(moveOrCancelSequenceNum);
+    mFocusedWindow->finishEvent(*moveOrCancelSequenceNum);
+    ASSERT_NE(nullptr, event);
+    ASSERT_EQ(event->getType(), AINPUT_EVENT_TYPE_MOTION);
+    MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+    if (motionEvent.getAction() == AMOTION_EVENT_ACTION_MOVE) {
+        mFocusedWindow->consumeMotionCancel();
+    } else {
+        ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction());
+    }
+
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mUnfocusedWindow->assertNoEvents();
+    mFocusedWindow->assertNoEvents();
 }
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 8e80b63..18bd3d0 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -18,21 +18,29 @@
 #include <InputDevice.h>
 #include <InputMapper.h>
 #include <InputReader.h>
+#include <InputReaderBase.h>
+#include <InputReaderFactory.h>
 #include <KeyboardInputMapper.h>
 #include <MultiTouchInputMapper.h>
 #include <SingleTouchInputMapper.h>
 #include <SwitchInputMapper.h>
 #include <TestInputListener.h>
 #include <TouchInputMapper.h>
+#include <UinputDevice.h>
+#include <android-base/thread_annotations.h>
 #include <gtest/gtest.h>
 #include <inttypes.h>
 #include <math.h>
 
 #include <memory>
-#include <unordered_map>
 
 namespace android {
 
+using std::chrono_literals::operator""ms;
+
+// Timeout for waiting for an expected event
+static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
+
 // An arbitrary time value.
 static const nsecs_t ARBITRARY_TIME = 1234;
 
@@ -164,9 +172,13 @@
 // --- FakeInputReaderPolicy ---
 
 class FakeInputReaderPolicy : public InputReaderPolicyInterface {
+    std::mutex mLock;
+    std::condition_variable mDevicesChangedCondition;
+
     InputReaderConfiguration mConfig;
     std::unordered_map<int32_t, std::shared_ptr<FakePointerController>> mPointerControllers;
-    std::vector<InputDeviceInfo> mInputDevices;
+    std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
+    bool mInputDevicesChanged GUARDED_BY(mLock){false};
     std::vector<DisplayViewport> mViewports;
     TouchAffineTransformation transform;
 
@@ -177,6 +189,22 @@
     FakeInputReaderPolicy() {
     }
 
+    void assertInputDevicesChanged() {
+        waitForInputDevices([](bool devicesChanged) {
+            if (!devicesChanged) {
+                FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
+            }
+        });
+    }
+
+    void assertInputDevicesNotChanged() {
+        waitForInputDevices([](bool devicesChanged) {
+            if (devicesChanged) {
+                FAIL() << "Expected notifyInputDevicesChanged() to not be called.";
+            }
+        });
+    }
+
     virtual void clearViewports() {
         mViewports.clear();
         mConfig.setDisplayViewports(mViewports);
@@ -202,6 +230,20 @@
         mConfig.setDisplayViewports(mViewports);
     }
 
+    bool updateViewport(const DisplayViewport& viewport) {
+        size_t count = mViewports.size();
+        for (size_t i = 0; i < count; i++) {
+            const DisplayViewport& currentViewport = mViewports[i];
+            if (currentViewport.displayId == viewport.displayId) {
+                mViewports[i] = viewport;
+                mConfig.setDisplayViewports(mViewports);
+                return true;
+            }
+        }
+        // no viewport found.
+        return false;
+    }
+
     void addExcludedDeviceName(const std::string& deviceName) {
         mConfig.excludedDeviceNames.push_back(deviceName);
     }
@@ -210,21 +252,9 @@
         mConfig.portAssociations.insert({inputPort, displayPort});
     }
 
-    void addDisabledDevice(int32_t deviceId) {
-        ssize_t index = mConfig.disabledDevices.indexOf(deviceId);
-        bool currentlyEnabled = index < 0;
-        if (currentlyEnabled) {
-            mConfig.disabledDevices.add(deviceId);
-        }
-    }
+    void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); }
 
-    void removeDisabledDevice(int32_t deviceId) {
-        ssize_t index = mConfig.disabledDevices.indexOf(deviceId);
-        bool currentlyEnabled = index < 0;
-        if (!currentlyEnabled) {
-            mConfig.disabledDevices.remove(deviceId);
-        }
-    }
+    void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); }
 
     void setPointerController(int32_t deviceId, std::shared_ptr<FakePointerController> controller) {
         mPointerControllers.insert_or_assign(deviceId, std::move(controller));
@@ -293,7 +323,10 @@
     }
 
     virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
+        std::scoped_lock<std::mutex> lock(mLock);
         mInputDevices = inputDevices;
+        mInputDevicesChanged = true;
+        mDevicesChangedCondition.notify_all();
     }
 
     virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier&) {
@@ -303,6 +336,18 @@
     virtual std::string getDeviceAlias(const InputDeviceIdentifier&) {
         return "";
     }
+
+    void waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
+        std::unique_lock<std::mutex> lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+
+        const bool devicesChanged =
+                mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+                    return mInputDevicesChanged;
+                });
+        ASSERT_NO_FATAL_FAILURE(processDevicesChanged(devicesChanged));
+        mInputDevicesChanged = false;
+    }
 };
 
 // --- FakeEventHub ---
@@ -344,19 +389,21 @@
         }
     };
 
+    std::mutex mLock;
+    std::condition_variable mEventsCondition;
+
     KeyedVector<int32_t, Device*> mDevices;
     std::vector<std::string> mExcludedDevices;
-    List<RawEvent> mEvents;
+    List<RawEvent> mEvents GUARDED_BY(mLock);
     std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
 
-protected:
+public:
     virtual ~FakeEventHub() {
         for (size_t i = 0; i < mDevices.size(); i++) {
             delete mDevices.valueAt(i);
         }
     }
 
-public:
     FakeEventHub() { }
 
     void addDevice(int32_t deviceId, const std::string& name, uint32_t classes) {
@@ -499,6 +546,7 @@
 
     void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type,
             int32_t code, int32_t value) {
+        std::scoped_lock<std::mutex> lock(mLock);
         RawEvent event;
         event.when = when;
         event.deviceId = deviceId;
@@ -518,8 +566,14 @@
     }
 
     void assertQueueIsEmpty() {
-        ASSERT_EQ(size_t(0), mEvents.size())
-                << "Expected the event queue to be empty (fully consumed).";
+        std::unique_lock<std::mutex> lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+        const bool queueIsEmpty =
+                mEventsCondition.wait_for(lock, WAIT_TIMEOUT,
+                                          [this]() REQUIRES(mLock) { return mEvents.size() == 0; });
+        if (!queueIsEmpty) {
+            FAIL() << "Timed out waiting for EventHub queue to be emptied.";
+        }
     }
 
 private:
@@ -552,7 +606,7 @@
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
             RawAbsoluteAxisInfo* outAxisInfo) const {
         Device* device = getDevice(deviceId);
-        if (device) {
+        if (device && device->enabled) {
             ssize_t index = device->absoluteAxes.indexOfKey(axis);
             if (index >= 0) {
                 *outAxisInfo = device->absoluteAxes.valueAt(index);
@@ -622,12 +676,14 @@
     }
 
     virtual size_t getEvents(int, RawEvent* buffer, size_t) {
+        std::scoped_lock<std::mutex> lock(mLock);
         if (mEvents.empty()) {
             return 0;
         }
 
         *buffer = *mEvents.begin();
         mEvents.erase(mEvents.begin());
+        mEventsCondition.notify_all();
         return 1;
     }
 
@@ -784,21 +840,24 @@
 // --- FakeInputReaderContext ---
 
 class FakeInputReaderContext : public InputReaderContext {
-    sp<EventHubInterface> mEventHub;
+    std::shared_ptr<EventHubInterface> mEventHub;
     sp<InputReaderPolicyInterface> mPolicy;
     sp<InputListenerInterface> mListener;
     int32_t mGlobalMetaState;
     bool mUpdateGlobalMetaStateWasCalled;
     int32_t mGeneration;
-    uint32_t mNextSequenceNum;
+    int32_t mNextId;
+    std::weak_ptr<PointerControllerInterface> mPointerController;
 
 public:
-    FakeInputReaderContext(const sp<EventHubInterface>& eventHub,
-            const sp<InputReaderPolicyInterface>& policy,
-            const sp<InputListenerInterface>& listener) :
-            mEventHub(eventHub), mPolicy(policy), mListener(listener),
-            mGlobalMetaState(0), mNextSequenceNum(1) {
-    }
+    FakeInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
+                           const sp<InputReaderPolicyInterface>& policy,
+                           const sp<InputListenerInterface>& listener)
+          : mEventHub(eventHub),
+            mPolicy(policy),
+            mListener(listener),
+            mGlobalMetaState(0),
+            mNextId(1) {}
 
     virtual ~FakeInputReaderContext() { }
 
@@ -816,6 +875,18 @@
         return mGeneration;
     }
 
+    void updatePointerDisplay() {
+        std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
+        if (controller != nullptr) {
+            InputReaderConfiguration config;
+            mPolicy->getReaderConfiguration(&config);
+            auto viewport = config.getDisplayViewportById(config.defaultPointerDisplayId);
+            if (viewport) {
+                controller->setDisplayViewport(*viewport);
+            }
+        }
+    }
+
 private:
     virtual void updateGlobalMetaState() {
         mUpdateGlobalMetaStateWasCalled = true;
@@ -840,8 +911,16 @@
     virtual void disableVirtualKeysUntil(nsecs_t) {
     }
 
-    virtual bool shouldDropVirtualKey(nsecs_t, InputDevice*, int32_t, int32_t) {
-        return false;
+    virtual bool shouldDropVirtualKey(nsecs_t, int32_t, int32_t) { return false; }
+
+    virtual std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) {
+        std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
+        if (controller == nullptr) {
+            controller = mPolicy->obtainPointerController(deviceId);
+            mPointerController = controller;
+            updatePointerDisplay();
+        }
+        return controller;
     }
 
     virtual void fadePointer() {
@@ -862,9 +941,7 @@
 
     }
 
-    virtual uint32_t getNextSequenceNum() {
-        return mNextSequenceNum++;
-    }
+    virtual int32_t getNextId() { return mNextId++; }
 };
 
 
@@ -878,20 +955,24 @@
     KeyedVector<int32_t, int32_t> mScanCodeStates;
     KeyedVector<int32_t, int32_t> mSwitchStates;
     std::vector<int32_t> mSupportedKeyCodes;
-    RawEvent mLastEvent;
 
-    bool mConfigureWasCalled;
-    bool mResetWasCalled;
-    bool mProcessWasCalled;
+    std::mutex mLock;
+    std::condition_variable mStateChangedCondition;
+    bool mConfigureWasCalled GUARDED_BY(mLock);
+    bool mResetWasCalled GUARDED_BY(mLock);
+    bool mProcessWasCalled GUARDED_BY(mLock);
+    RawEvent mLastEvent GUARDED_BY(mLock);
 
     std::optional<DisplayViewport> mViewport;
 public:
-    FakeInputMapper(InputDevice* device, uint32_t sources) :
-            InputMapper(device),
-            mSources(sources), mKeyboardType(AINPUT_KEYBOARD_TYPE_NONE),
+    FakeInputMapper(InputDeviceContext& deviceContext, uint32_t sources)
+          : InputMapper(deviceContext),
+            mSources(sources),
+            mKeyboardType(AINPUT_KEYBOARD_TYPE_NONE),
             mMetaState(0),
-            mConfigureWasCalled(false), mResetWasCalled(false), mProcessWasCalled(false) {
-    }
+            mConfigureWasCalled(false),
+            mResetWasCalled(false),
+            mProcessWasCalled(false) {}
 
     virtual ~FakeInputMapper() { }
 
@@ -904,20 +985,41 @@
     }
 
     void assertConfigureWasCalled() {
-        ASSERT_TRUE(mConfigureWasCalled)
-                << "Expected configure() to have been called.";
+        std::unique_lock<std::mutex> lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+        const bool configureCalled =
+                mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+                    return mConfigureWasCalled;
+                });
+        if (!configureCalled) {
+            FAIL() << "Expected configure() to have been called.";
+        }
         mConfigureWasCalled = false;
     }
 
     void assertResetWasCalled() {
-        ASSERT_TRUE(mResetWasCalled)
-                << "Expected reset() to have been called.";
+        std::unique_lock<std::mutex> lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+        const bool resetCalled =
+                mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+                    return mResetWasCalled;
+                });
+        if (!resetCalled) {
+            FAIL() << "Expected reset() to have been called.";
+        }
         mResetWasCalled = false;
     }
 
     void assertProcessWasCalled(RawEvent* outLastEvent = nullptr) {
-        ASSERT_TRUE(mProcessWasCalled)
-                << "Expected process() to have been called.";
+        std::unique_lock<std::mutex> lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+        const bool processCalled =
+                mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+                    return mProcessWasCalled;
+                });
+        if (!processCalled) {
+            FAIL() << "Expected process() to have been called.";
+        }
         if (outLastEvent) {
             *outLastEvent = mLastEvent;
         }
@@ -954,22 +1056,29 @@
     }
 
     virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) {
+        std::scoped_lock<std::mutex> lock(mLock);
         mConfigureWasCalled = true;
 
         // Find the associated viewport if exist.
-        const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+        const std::optional<uint8_t> displayPort = getDeviceContext().getAssociatedDisplayPort();
         if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
             mViewport = config->getDisplayViewportByPort(*displayPort);
         }
+
+        mStateChangedCondition.notify_all();
     }
 
     virtual void reset(nsecs_t) {
+        std::scoped_lock<std::mutex> lock(mLock);
         mResetWasCalled = true;
+        mStateChangedCondition.notify_all();
     }
 
     virtual void process(const RawEvent* rawEvent) {
+        std::scoped_lock<std::mutex> lock(mLock);
         mLastEvent = *rawEvent;
         mProcessWasCalled = true;
+        mStateChangedCondition.notify_all();
     }
 
     virtual int32_t getKeyCodeState(uint32_t, int32_t keyCode) {
@@ -1020,45 +1129,39 @@
 // --- InstrumentedInputReader ---
 
 class InstrumentedInputReader : public InputReader {
-    InputDevice* mNextDevice;
+    std::shared_ptr<InputDevice> mNextDevice;
 
 public:
-    InstrumentedInputReader(const sp<EventHubInterface>& eventHub,
-            const sp<InputReaderPolicyInterface>& policy,
-            const sp<InputListenerInterface>& listener) :
-            InputReader(eventHub, policy, listener),
-            mNextDevice(nullptr) {
-    }
+    InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
+                            const sp<InputReaderPolicyInterface>& policy,
+                            const sp<InputListenerInterface>& listener)
+          : InputReader(eventHub, policy, listener), mNextDevice(nullptr) {}
 
-    virtual ~InstrumentedInputReader() {
-        if (mNextDevice) {
-            delete mNextDevice;
-        }
-    }
+    virtual ~InstrumentedInputReader() {}
 
-    void setNextDevice(InputDevice* device) {
-        mNextDevice = device;
-    }
+    void setNextDevice(std::shared_ptr<InputDevice> device) { mNextDevice = device; }
 
-    InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const std::string& name,
-            uint32_t classes, const std::string& location = "") {
+    std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+                                           const std::string& location = "") {
         InputDeviceIdentifier identifier;
         identifier.name = name;
         identifier.location = location;
         int32_t generation = deviceId + 1;
-        return new InputDevice(&mContext, deviceId, generation, controllerNumber, identifier,
-                classes);
+        return std::make_shared<InputDevice>(&mContext, deviceId, generation, identifier);
     }
 
+    // Make the protected loopOnce method accessible to tests.
+    using InputReader::loopOnce;
+
 protected:
-    virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
-            const InputDeviceIdentifier& identifier, uint32_t classes) {
+    virtual std::shared_ptr<InputDevice> createDeviceLocked(
+            int32_t eventHubId, const InputDeviceIdentifier& identifier) {
         if (mNextDevice) {
-            InputDevice* device = mNextDevice;
+            std::shared_ptr<InputDevice> device(mNextDevice);
             mNextDevice = nullptr;
             return device;
         }
-        return InputReader::createDeviceLocked(deviceId, controllerNumber, identifier, classes);
+        return InputReader::createDeviceLocked(eventHubId, identifier);
     }
 
     friend class InputReaderTest;
@@ -1069,12 +1172,8 @@
 protected:
     sp<FakeInputReaderPolicy> mFakePolicy;
 
-    virtual void SetUp() {
-        mFakePolicy = new FakeInputReaderPolicy();
-    }
-    virtual void TearDown() {
-        mFakePolicy.clear();
-    }
+    virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
+    virtual void TearDown() override { mFakePolicy.clear(); }
 };
 
 /**
@@ -1256,60 +1355,55 @@
 protected:
     sp<TestInputListener> mFakeListener;
     sp<FakeInputReaderPolicy> mFakePolicy;
-    sp<FakeEventHub> mFakeEventHub;
-    sp<InstrumentedInputReader> mReader;
+    std::shared_ptr<FakeEventHub> mFakeEventHub;
+    std::unique_ptr<InstrumentedInputReader> mReader;
 
-    virtual void SetUp() {
-        mFakeEventHub = new FakeEventHub();
+    virtual void SetUp() override {
+        mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
 
-        mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
+        mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+                                                            mFakeListener);
     }
 
-    virtual void TearDown() {
-        mReader.clear();
-
+    virtual void TearDown() override {
         mFakeListener.clear();
         mFakePolicy.clear();
-        mFakeEventHub.clear();
     }
 
-    void addDevice(int32_t deviceId, const std::string& name, uint32_t classes,
-            const PropertyMap* configuration) {
-        mFakeEventHub->addDevice(deviceId, name, classes);
+    void addDevice(int32_t eventHubId, const std::string& name, uint32_t classes,
+                   const PropertyMap* configuration) {
+        mFakeEventHub->addDevice(eventHubId, name, classes);
 
         if (configuration) {
-            mFakeEventHub->addConfigurationMap(deviceId, configuration);
+            mFakeEventHub->addConfigurationMap(eventHubId, configuration);
         }
         mFakeEventHub->finishDeviceScan();
         mReader->loopOnce();
         mReader->loopOnce();
-        mFakeEventHub->assertQueueIsEmpty();
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+        ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
     }
 
-    void disableDevice(int32_t deviceId, InputDevice* device) {
+    void disableDevice(int32_t deviceId) {
         mFakePolicy->addDisabledDevice(deviceId);
-        configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
     }
 
-    void enableDevice(int32_t deviceId, InputDevice* device) {
+    void enableDevice(int32_t deviceId) {
         mFakePolicy->removeDisabledDevice(deviceId);
-        configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
     }
 
-    void configureDevice(uint32_t changes, InputDevice* device) {
-        device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
-    }
-
-    FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
-            const std::string& name, uint32_t classes, uint32_t sources,
-            const PropertyMap* configuration) {
-        InputDevice* device = mReader->newDevice(deviceId, controllerNumber, name, classes);
-        FakeInputMapper* mapper = new FakeInputMapper(device, sources);
-        device->addMapper(mapper);
+    FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t eventHubId,
+                                                  const std::string& name, uint32_t classes,
+                                                  uint32_t sources,
+                                                  const PropertyMap* configuration) {
+        std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, name);
+        FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(eventHubId, sources);
         mReader->setNextDevice(device);
-        addDevice(deviceId, name, classes, configuration);
+        addDevice(eventHubId, name, classes, configuration);
         return mapper;
     }
 };
@@ -1320,12 +1414,10 @@
     ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored",
             0, nullptr)); // no classes so device will be ignored
 
-
     std::vector<InputDeviceInfo> inputDevices;
     mReader->getInputDevices(inputDevices);
-
     ASSERT_EQ(1U, inputDevices.size());
-    ASSERT_EQ(1, inputDevices[0].getId());
+    ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId());
     ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
     ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
@@ -1334,7 +1426,7 @@
     // Should also have received a notification describing the new input devices.
     inputDevices = mFakePolicy->getInputDevices();
     ASSERT_EQ(1U, inputDevices.size());
-    ASSERT_EQ(1, inputDevices[0].getId());
+    ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId());
     ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
     ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType());
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources());
@@ -1342,62 +1434,65 @@
 }
 
 TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
-    constexpr int32_t deviceId = 1;
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
-    InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
+    constexpr int32_t eventHubId = 1;
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     // Must add at least one mapper or the device will be ignored!
-    FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
-    device->addMapper(mapper);
+    device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
     mReader->setNextDevice(device);
-    addDevice(deviceId, "fake", deviceClass, nullptr);
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
-    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
     ASSERT_EQ(deviceId, resetArgs.deviceId);
 
     ASSERT_EQ(device->isEnabled(), true);
-    disableDevice(deviceId, device);
+    disableDevice(deviceId);
     mReader->loopOnce();
 
-    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
-    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_EQ(deviceId, resetArgs.deviceId);
     ASSERT_EQ(device->isEnabled(), false);
 
-    disableDevice(deviceId, device);
+    disableDevice(deviceId);
     mReader->loopOnce();
-    mFakeListener->assertNotifyDeviceResetWasNotCalled();
-    mFakeListener->assertNotifyConfigurationChangedWasNotCalled();
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled());
     ASSERT_EQ(device->isEnabled(), false);
 
-    enableDevice(deviceId, device);
+    enableDevice(deviceId);
     mReader->loopOnce();
-    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
-    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_EQ(deviceId, resetArgs.deviceId);
     ASSERT_EQ(device->isEnabled(), true);
 }
 
 TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
-    FakeInputMapper* mapper = nullptr;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
-    mapper->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr int32_t eventHubId = 1;
+    FakeInputMapper& mapper =
+            addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
+                                         AINPUT_SOURCE_KEYBOARD, nullptr);
+    mapper.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(0,
             AINPUT_SOURCE_ANY, AKEYCODE_A))
             << "Should return unknown when the device id is >= 0 but unknown.";
 
-    ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(1,
-            AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
-            << "Should return unknown when the device id is valid but the sources are not supported by the device.";
+    ASSERT_EQ(AKEY_STATE_UNKNOWN,
+              mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
+            << "Should return unknown when the device id is valid but the sources are not "
+               "supported by the device.";
 
-    ASSERT_EQ(AKEY_STATE_DOWN, mReader->getKeyCodeState(1,
-            AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
-            << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
+    ASSERT_EQ(AKEY_STATE_DOWN,
+              mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL,
+                                       AKEYCODE_A))
+            << "Should return value provided by mapper when device id is valid and the device "
+               "supports some of the sources.";
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(-1,
             AINPUT_SOURCE_TRACKBALL, AKEYCODE_A))
@@ -1409,22 +1504,28 @@
 }
 
 TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) {
-    FakeInputMapper* mapper = nullptr;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
-    mapper->setScanCodeState(KEY_A, AKEY_STATE_DOWN);
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr int32_t eventHubId = 1;
+    FakeInputMapper& mapper =
+            addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
+                                         AINPUT_SOURCE_KEYBOARD, nullptr);
+    mapper.setScanCodeState(KEY_A, AKEY_STATE_DOWN);
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(0,
             AINPUT_SOURCE_ANY, KEY_A))
             << "Should return unknown when the device id is >= 0 but unknown.";
 
-    ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(1,
-            AINPUT_SOURCE_TRACKBALL, KEY_A))
-            << "Should return unknown when the device id is valid but the sources are not supported by the device.";
+    ASSERT_EQ(AKEY_STATE_UNKNOWN,
+              mReader->getScanCodeState(deviceId, AINPUT_SOURCE_TRACKBALL, KEY_A))
+            << "Should return unknown when the device id is valid but the sources are not "
+               "supported by the device.";
 
-    ASSERT_EQ(AKEY_STATE_DOWN, mReader->getScanCodeState(1,
-            AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, KEY_A))
-            << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
+    ASSERT_EQ(AKEY_STATE_DOWN,
+              mReader->getScanCodeState(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL,
+                                        KEY_A))
+            << "Should return value provided by mapper when device id is valid and the device "
+               "supports some of the sources.";
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(-1,
             AINPUT_SOURCE_TRACKBALL, KEY_A))
@@ -1436,22 +1537,28 @@
 }
 
 TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) {
-    FakeInputMapper* mapper = nullptr;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
-    mapper->setSwitchState(SW_LID, AKEY_STATE_DOWN);
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr int32_t eventHubId = 1;
+    FakeInputMapper& mapper =
+            addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
+                                         AINPUT_SOURCE_KEYBOARD, nullptr);
+    mapper.setSwitchState(SW_LID, AKEY_STATE_DOWN);
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(0,
             AINPUT_SOURCE_ANY, SW_LID))
             << "Should return unknown when the device id is >= 0 but unknown.";
 
-    ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(1,
-            AINPUT_SOURCE_TRACKBALL, SW_LID))
-            << "Should return unknown when the device id is valid but the sources are not supported by the device.";
+    ASSERT_EQ(AKEY_STATE_UNKNOWN,
+              mReader->getSwitchState(deviceId, AINPUT_SOURCE_TRACKBALL, SW_LID))
+            << "Should return unknown when the device id is valid but the sources are not "
+               "supported by the device.";
 
-    ASSERT_EQ(AKEY_STATE_DOWN, mReader->getSwitchState(1,
-            AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, SW_LID))
-            << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
+    ASSERT_EQ(AKEY_STATE_DOWN,
+              mReader->getSwitchState(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL,
+                                      SW_LID))
+            << "Should return value provided by mapper when device id is valid and the device "
+               "supports some of the sources.";
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(-1,
             AINPUT_SOURCE_TRACKBALL, SW_LID))
@@ -1463,12 +1570,15 @@
 }
 
 TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) {
-    FakeInputMapper* mapper = nullptr;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr int32_t eventHubId = 1;
+    FakeInputMapper& mapper =
+            addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
+                                         AINPUT_SOURCE_KEYBOARD, nullptr);
 
-    mapper->addSupportedKeyCode(AKEYCODE_A);
-    mapper->addSupportedKeyCode(AKEYCODE_B);
+    mapper.addSupportedKeyCode(AKEYCODE_A);
+    mapper.addSupportedKeyCode(AKEYCODE_B);
 
     const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 };
     uint8_t flags[4] = { 0, 0, 0, 1 };
@@ -1478,13 +1588,16 @@
     ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]);
 
     flags[3] = 1;
-    ASSERT_FALSE(mReader->hasKeys(1, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
-            << "Should return false when device id is valid but the sources are not supported by the device.";
+    ASSERT_FALSE(mReader->hasKeys(deviceId, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
+            << "Should return false when device id is valid but the sources are not supported by "
+               "the device.";
     ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]);
 
     flags[3] = 1;
-    ASSERT_TRUE(mReader->hasKeys(1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags))
-            << "Should return value provided by mapper when device id is valid and the device supports some of the sources.";
+    ASSERT_TRUE(mReader->hasKeys(deviceId, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4,
+                                 keyCodes, flags))
+            << "Should return value provided by mapper when device id is valid and the device "
+               "supports some of the sources.";
     ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]);
 
     flags[3] = 1;
@@ -1499,7 +1612,8 @@
 }
 
 TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
-    addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr);
+    constexpr int32_t eventHubId = 1;
+    addDevice(eventHubId, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr);
 
     NotifyConfigurationChangedArgs args;
 
@@ -1508,66 +1622,83 @@
 }
 
 TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
-    FakeInputMapper* mapper = nullptr;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr int32_t eventHubId = 1;
+    FakeInputMapper& mapper =
+            addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
+                                         AINPUT_SOURCE_KEYBOARD, nullptr);
 
-    mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1);
+    mFakeEventHub->enqueueEvent(0, eventHubId, EV_KEY, KEY_A, 1);
     mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
 
     RawEvent event;
-    ASSERT_NO_FATAL_FAILURE(mapper->assertProcessWasCalled(&event));
+    ASSERT_NO_FATAL_FAILURE(mapper.assertProcessWasCalled(&event));
     ASSERT_EQ(0, event.when);
-    ASSERT_EQ(1, event.deviceId);
+    ASSERT_EQ(eventHubId, event.deviceId);
     ASSERT_EQ(EV_KEY, event.type);
     ASSERT_EQ(KEY_A, event.code);
     ASSERT_EQ(1, event.value);
 }
 
-TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) {
-    constexpr int32_t deviceId = 1;
+TEST_F(InputReaderTest, DeviceReset_RandomId) {
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
-    InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
+    constexpr int32_t eventHubId = 1;
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
     // Must add at least one mapper or the device will be ignored!
-    FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
-    device->addMapper(mapper);
+    device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
     mReader->setNextDevice(device);
-    addDevice(deviceId, "fake", deviceClass, nullptr);
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
-    uint32_t prevSequenceNum = resetArgs.sequenceNum;
+    int32_t prevId = resetArgs.id;
 
-    disableDevice(deviceId, device);
+    disableDevice(deviceId);
     mReader->loopOnce();
-    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
-    ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
-    prevSequenceNum = resetArgs.sequenceNum;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_NE(prevId, resetArgs.id);
+    prevId = resetArgs.id;
 
-    enableDevice(deviceId, device);
+    enableDevice(deviceId);
     mReader->loopOnce();
-    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
-    ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
-    prevSequenceNum = resetArgs.sequenceNum;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_NE(prevId, resetArgs.id);
+    prevId = resetArgs.id;
 
-    disableDevice(deviceId, device);
+    disableDevice(deviceId);
     mReader->loopOnce();
-    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
-    ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
-    prevSequenceNum = resetArgs.sequenceNum;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_NE(prevId, resetArgs.id);
+    prevId = resetArgs.id;
+}
+
+TEST_F(InputReaderTest, DeviceReset_GenerateIdWithInputReaderSource) {
+    constexpr int32_t deviceId = 1;
+    constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr int32_t eventHubId = 1;
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake");
+    // Must add at least one mapper or the device will be ignored!
+    device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
+    mReader->setNextDevice(device);
+    ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
+
+    NotifyDeviceResetArgs resetArgs;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_EQ(IdGenerator::Source::INPUT_READER, IdGenerator::getSource(resetArgs.id));
 }
 
 TEST_F(InputReaderTest, Device_CanDispatchToDisplay) {
-    constexpr int32_t deviceId = 1;
+    constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
+    constexpr int32_t eventHubId = 1;
     const char* DEVICE_LOCATION = "USB1";
-    InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass,
-            DEVICE_LOCATION);
-    FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN);
-    device->addMapper(mapper);
+    std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+    FakeInputMapper& mapper =
+            device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_TOUCHSCREEN);
     mReader->setNextDevice(device);
-    addDevice(deviceId, "fake", deviceClass, nullptr);
 
     const uint8_t hdmi1 = 1;
 
@@ -1575,6 +1706,7 @@
     mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
 
     // Add default and second display.
+    mFakePolicy->clearViewports();
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
             DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
@@ -1582,68 +1714,348 @@
     mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     mReader->loopOnce();
 
-    // Check device.
+    // Add the device, and make sure all of the callbacks are triggered.
+    // The device is added after the input port associations are processed since
+    // we do not yet support dynamic device-to-display associations.
+    ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
+
+    // Device should only dispatch to the specified display.
     ASSERT_EQ(deviceId, device->getId());
     ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, DISPLAY_ID));
     ASSERT_TRUE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
+
+    // Can't dispatch event from a disabled device.
+    disableDevice(deviceId);
+    mReader->loopOnce();
+    ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
 }
 
+// --- InputReaderIntegrationTest ---
+
+// These tests create and interact with the InputReader only through its interface.
+// The InputReader is started during SetUp(), which starts its processing in its own
+// thread. The tests use linux uinput to emulate input devices.
+// NOTE: Interacting with the physical device while these tests are running may cause
+// the tests to fail.
+class InputReaderIntegrationTest : public testing::Test {
+protected:
+    sp<TestInputListener> mTestListener;
+    sp<FakeInputReaderPolicy> mFakePolicy;
+    sp<InputReaderInterface> mReader;
+
+    virtual void SetUp() override {
+        mFakePolicy = new FakeInputReaderPolicy();
+        mTestListener = new TestInputListener(2000ms /*eventHappenedTimeout*/,
+                                              30ms /*eventDidNotHappenTimeout*/);
+
+        mReader = new InputReader(std::make_shared<EventHub>(), mFakePolicy, mTestListener);
+        ASSERT_EQ(mReader->start(), OK);
+
+        // Since this test is run on a real device, all the input devices connected
+        // to the test device will show up in mReader. We wait for those input devices to
+        // show up before beginning the tests.
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+    }
+
+    virtual void TearDown() override {
+        ASSERT_EQ(mReader->stop(), OK);
+        mTestListener.clear();
+        mFakePolicy.clear();
+    }
+};
+
+TEST_F(InputReaderIntegrationTest, TestInvalidDevice) {
+    // An invalid input device that is only used for this test.
+    class InvalidUinputDevice : public UinputDevice {
+    public:
+        InvalidUinputDevice() : UinputDevice("Invalid Device") {}
+
+    private:
+        void configureDevice(int fd, uinput_user_dev* device) override {}
+    };
+
+    const size_t numDevices = mFakePolicy->getInputDevices().size();
+
+    // UinputDevice does not set any event or key bits, so InputReader should not
+    // consider it as a valid device.
+    std::unique_ptr<UinputDevice> invalidDevice = createUinputDevice<InvalidUinputDevice>();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesNotChanged());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasNotCalled());
+    ASSERT_EQ(numDevices, mFakePolicy->getInputDevices().size());
+
+    invalidDevice.reset();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesNotChanged());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasNotCalled());
+    ASSERT_EQ(numDevices, mFakePolicy->getInputDevices().size());
+}
+
+TEST_F(InputReaderIntegrationTest, AddNewDevice) {
+    const size_t initialNumDevices = mFakePolicy->getInputDevices().size();
+
+    std::unique_ptr<UinputHomeKey> keyboard = createUinputDevice<UinputHomeKey>();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+    ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size());
+
+    // Find the test device by its name.
+    std::vector<InputDeviceInfo> inputDevices;
+    mReader->getInputDevices(inputDevices);
+    InputDeviceInfo* keyboardInfo = nullptr;
+    const char* keyboardName = keyboard->getName();
+    for (unsigned int i = 0; i < initialNumDevices + 1; i++) {
+        if (!strcmp(inputDevices[i].getIdentifier().name.c_str(), keyboardName)) {
+            keyboardInfo = &inputDevices[i];
+            break;
+        }
+    }
+    ASSERT_NE(keyboardInfo, nullptr);
+    ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, keyboardInfo->getKeyboardType());
+    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyboardInfo->getSources());
+    ASSERT_EQ(0U, keyboardInfo->getMotionRanges().size());
+
+    keyboard.reset();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+    ASSERT_EQ(initialNumDevices, mFakePolicy->getInputDevices().size());
+}
+
+TEST_F(InputReaderIntegrationTest, SendsEventsToInputListener) {
+    std::unique_ptr<UinputHomeKey> keyboard = createUinputDevice<UinputHomeKey>();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+
+    NotifyConfigurationChangedArgs configChangedArgs;
+    ASSERT_NO_FATAL_FAILURE(
+            mTestListener->assertNotifyConfigurationChangedWasCalled(&configChangedArgs));
+    int32_t prevId = configChangedArgs.id;
+    nsecs_t prevTimestamp = configChangedArgs.eventTime;
+
+    NotifyKeyArgs keyArgs;
+    keyboard->pressAndReleaseHomeKey();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
+    ASSERT_NE(prevId, keyArgs.id);
+    prevId = keyArgs.id;
+    ASSERT_LE(prevTimestamp, keyArgs.eventTime);
+    prevTimestamp = keyArgs.eventTime;
+
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs));
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
+    ASSERT_NE(prevId, keyArgs.id);
+    ASSERT_LE(prevTimestamp, keyArgs.eventTime);
+}
+
+/**
+ * The Steam controller sends BTN_GEAR_DOWN and BTN_GEAR_UP for the two "paddle" buttons
+ * on the back. In this test, we make sure that BTN_GEAR_DOWN / BTN_WHEEL and BTN_GEAR_UP
+ * are passed to the listener.
+ */
+static_assert(BTN_GEAR_DOWN == BTN_WHEEL);
+TEST_F(InputReaderIntegrationTest, SendsGearDownAndUpToInputListener) {
+    std::unique_ptr<UinputSteamController> controller = createUinputDevice<UinputSteamController>();
+    ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+    NotifyKeyArgs keyArgs;
+
+    controller->pressAndReleaseKey(BTN_GEAR_DOWN);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_DOWN
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_UP
+    ASSERT_EQ(BTN_GEAR_DOWN, keyArgs.scanCode);
+
+    controller->pressAndReleaseKey(BTN_GEAR_UP);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_DOWN
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs)); // ACTION_UP
+    ASSERT_EQ(BTN_GEAR_UP, keyArgs.scanCode);
+}
+
+// --- TouchProcessTest ---
+class TouchIntegrationTest : public InputReaderIntegrationTest {
+protected:
+    static const int32_t FIRST_SLOT = 0;
+    static const int32_t SECOND_SLOT = 1;
+    static const int32_t FIRST_TRACKING_ID = 0;
+    static const int32_t SECOND_TRACKING_ID = 1;
+    const std::string UNIQUE_ID = "local:0";
+
+    virtual void SetUp() override {
+        InputReaderIntegrationTest::SetUp();
+        // At least add an internal display.
+        setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                                     DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT,
+                                     ViewportType::VIEWPORT_INTERNAL);
+
+        mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+        ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+    }
+
+    void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+                                      int32_t orientation, const std::string& uniqueId,
+                                      std::optional<uint8_t> physicalPort,
+                                      ViewportType viewportType) {
+        mFakePolicy->addDisplayViewport(displayId, width, height, orientation, uniqueId,
+                                        physicalPort, viewportType);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    }
+
+    std::unique_ptr<UinputTouchScreen> mDevice;
+};
+
+TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
+    NotifyMotionArgs args;
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // ACTION_DOWN
+    mDevice->sendDown(centerPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+
+    // ACTION_MOVE
+    mDevice->sendMove(centerPoint + Point(1, 1));
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+    // ACTION_UP
+    mDevice->sendUp();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+}
+
+TEST_F(TouchIntegrationTest, InputEvent_ProcessMultiTouch) {
+    NotifyMotionArgs args;
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // ACTION_DOWN
+    mDevice->sendDown(centerPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+
+    // ACTION_POINTER_DOWN (Second slot)
+    const Point secondPoint = centerPoint + Point(100, 100);
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendTrackingId(SECOND_TRACKING_ID);
+    mDevice->sendDown(secondPoint + Point(1, 1));
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              args.action);
+
+    // ACTION_MOVE (Second slot)
+    mDevice->sendMove(secondPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+    // ACTION_POINTER_UP (Second slot)
+    mDevice->sendUp();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              args.action);
+
+    // ACTION_UP
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendUp();
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+}
+
+TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) {
+    NotifyMotionArgs args;
+    const Point centerPoint = mDevice->getCenterPoint();
+
+    // ACTION_DOWN
+    mDevice->sendDown(centerPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+
+    // ACTION_POINTER_DOWN (Second slot)
+    const Point secondPoint = centerPoint + Point(100, 100);
+    mDevice->sendSlot(SECOND_SLOT);
+    mDevice->sendTrackingId(SECOND_TRACKING_ID);
+    mDevice->sendDown(secondPoint);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+              args.action);
+
+    // ACTION_MOVE (Second slot)
+    mDevice->sendMove(secondPoint + Point(1, 1));
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+    // Send MT_TOOL_PALM, which indicates that the touch IC has determined this to be a grip event.
+    // Expect to receive ACTION_CANCEL, to abort the entire gesture.
+    mDevice->sendToolType(MT_TOOL_PALM);
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, args.action);
+
+    // ACTION_POINTER_UP (Second slot)
+    mDevice->sendUp();
+
+    // ACTION_UP
+    mDevice->sendSlot(FIRST_SLOT);
+    mDevice->sendUp();
+
+    // Expect no event received after abort the entire gesture.
+    ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+}
 
 // --- InputDeviceTest ---
-
 class InputDeviceTest : public testing::Test {
 protected:
     static const char* DEVICE_NAME;
+    static const char* DEVICE_LOCATION;
     static const int32_t DEVICE_ID;
     static const int32_t DEVICE_GENERATION;
     static const int32_t DEVICE_CONTROLLER_NUMBER;
     static const uint32_t DEVICE_CLASSES;
+    static const int32_t EVENTHUB_ID;
 
-    sp<FakeEventHub> mFakeEventHub;
+    std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<TestInputListener> mFakeListener;
     FakeInputReaderContext* mFakeContext;
 
-    InputDevice* mDevice;
+    std::shared_ptr<InputDevice> mDevice;
 
-    virtual void SetUp() {
-        mFakeEventHub = new FakeEventHub();
+    virtual void SetUp() override {
+        mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
 
-        mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0);
+        mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, 0);
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
-        mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
-                DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
+        identifier.location = DEVICE_LOCATION;
+        mDevice = std::make_shared<InputDevice>(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
+                                                identifier);
     }
 
-    virtual void TearDown() {
-        delete mDevice;
-
+    virtual void TearDown() override {
+        mDevice = nullptr;
         delete mFakeContext;
         mFakeListener.clear();
         mFakePolicy.clear();
-        mFakeEventHub.clear();
     }
 };
 
 const char* InputDeviceTest::DEVICE_NAME = "device";
-const int32_t InputDeviceTest::DEVICE_ID = 1;
+const char* InputDeviceTest::DEVICE_LOCATION = "USB1";
+const int32_t InputDeviceTest::DEVICE_ID = END_RESERVED_ID + 1000;
 const int32_t InputDeviceTest::DEVICE_GENERATION = 2;
 const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0;
 const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD
         | INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_JOYSTICK;
+const int32_t InputDeviceTest::EVENTHUB_ID = 1;
 
 TEST_F(InputDeviceTest, ImmutableProperties) {
     ASSERT_EQ(DEVICE_ID, mDevice->getId());
     ASSERT_STREQ(DEVICE_NAME, mDevice->getName().c_str());
-    ASSERT_EQ(DEVICE_CLASSES, mDevice->getClasses());
+    ASSERT_EQ(0U, mDevice->getClasses());
 }
 
-TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsTrue) {
-    ASSERT_EQ(mDevice->isEnabled(), true);
+TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) {
+    ASSERT_EQ(mDevice->isEnabled(), false);
 }
 
 TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
@@ -1690,23 +2102,23 @@
 
 TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) {
     // Configuration.
-    mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8("key"), String8("value"));
+    mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8("key"), String8("value"));
 
-    FakeInputMapper* mapper1 = new FakeInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD);
-    mapper1->setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    mapper1->setMetaState(AMETA_ALT_ON);
-    mapper1->addSupportedKeyCode(AKEYCODE_A);
-    mapper1->addSupportedKeyCode(AKEYCODE_B);
-    mapper1->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
-    mapper1->setKeyCodeState(AKEYCODE_B, AKEY_STATE_UP);
-    mapper1->setScanCodeState(2, AKEY_STATE_DOWN);
-    mapper1->setScanCodeState(3, AKEY_STATE_UP);
-    mapper1->setSwitchState(4, AKEY_STATE_DOWN);
-    mDevice->addMapper(mapper1);
+    FakeInputMapper& mapper1 =
+            mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD);
+    mapper1.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    mapper1.setMetaState(AMETA_ALT_ON);
+    mapper1.addSupportedKeyCode(AKEYCODE_A);
+    mapper1.addSupportedKeyCode(AKEYCODE_B);
+    mapper1.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
+    mapper1.setKeyCodeState(AKEYCODE_B, AKEY_STATE_UP);
+    mapper1.setScanCodeState(2, AKEY_STATE_DOWN);
+    mapper1.setScanCodeState(3, AKEY_STATE_UP);
+    mapper1.setSwitchState(4, AKEY_STATE_DOWN);
 
-    FakeInputMapper* mapper2 = new FakeInputMapper(mDevice, AINPUT_SOURCE_TOUCHSCREEN);
-    mapper2->setMetaState(AMETA_SHIFT_ON);
-    mDevice->addMapper(mapper2);
+    FakeInputMapper& mapper2 =
+            mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN);
+    mapper2.setMetaState(AMETA_SHIFT_ON);
 
     InputReaderConfiguration config;
     mDevice->configure(ARBITRARY_TIME, &config, 0);
@@ -1716,13 +2128,13 @@
             << "Device should have read configuration during configuration phase.";
     ASSERT_STREQ("value", propertyValue.string());
 
-    ASSERT_NO_FATAL_FAILURE(mapper1->assertConfigureWasCalled());
-    ASSERT_NO_FATAL_FAILURE(mapper2->assertConfigureWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper1.assertConfigureWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper2.assertConfigureWasCalled());
 
     // Reset
     mDevice->reset(ARBITRARY_TIME);
-    ASSERT_NO_FATAL_FAILURE(mapper1->assertResetWasCalled());
-    ASSERT_NO_FATAL_FAILURE(mapper2->assertResetWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper1.assertResetWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper2.assertResetWasCalled());
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -1776,12 +2188,55 @@
 
     // Event handling.
     RawEvent event;
+    event.deviceId = EVENTHUB_ID;
     mDevice->process(&event, 1);
 
-    ASSERT_NO_FATAL_FAILURE(mapper1->assertProcessWasCalled());
-    ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper1.assertProcessWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled());
 }
 
+// A single input device is associated with a specific display. Check that:
+// 1. Device is disabled if the viewport corresponding to the associated display is not found
+// 2. Device is disabled when setEnabled API is called
+TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) {
+    mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_TOUCHSCREEN);
+
+    // First Configuration.
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+
+    // Device should be enabled by default.
+    ASSERT_TRUE(mDevice->isEnabled());
+
+    // Prepare associated info.
+    constexpr uint8_t hdmi = 1;
+    const std::string UNIQUE_ID = "local:1";
+
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi);
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    // Device should be disabled because it is associated with a specific display via
+    // input port <-> display port association, but the corresponding display is not found
+    ASSERT_FALSE(mDevice->isEnabled());
+
+    // Prepare displays.
+    mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                                    DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi,
+                                    ViewportType::VIEWPORT_INTERNAL);
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    ASSERT_TRUE(mDevice->isEnabled());
+
+    // Device should be disabled after set disable.
+    mFakePolicy->addDisabledDevice(mDevice->getId());
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_ENABLED_STATE);
+    ASSERT_FALSE(mDevice->isEnabled());
+
+    // Device should still be disabled even found the associated display.
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    ASSERT_FALSE(mDevice->isEnabled());
+}
 
 // --- InputMapperTest ---
 
@@ -1793,47 +2248,53 @@
     static const int32_t DEVICE_GENERATION;
     static const int32_t DEVICE_CONTROLLER_NUMBER;
     static const uint32_t DEVICE_CLASSES;
+    static const int32_t EVENTHUB_ID;
 
-    sp<FakeEventHub> mFakeEventHub;
+    std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<TestInputListener> mFakeListener;
     FakeInputReaderContext* mFakeContext;
     InputDevice* mDevice;
 
-    virtual void SetUp() {
-        mFakeEventHub = new FakeEventHub();
+    virtual void SetUp(uint32_t classes) {
+        mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
         identifier.location = DEVICE_LOCATION;
-        mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
-                DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
+        mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, identifier);
 
-        mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
+        mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, classes);
     }
 
-    virtual void TearDown() {
+    virtual void SetUp() override { SetUp(DEVICE_CLASSES); }
+
+    virtual void TearDown() override {
         delete mDevice;
         delete mFakeContext;
         mFakeListener.clear();
         mFakePolicy.clear();
-        mFakeEventHub.clear();
     }
 
     void addConfigurationProperty(const char* key, const char* value) {
-        mFakeEventHub->addConfigurationProperty(mDevice->getId(), String8(key), String8(value));
+        mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, String8(key), String8(value));
     }
 
     void configureDevice(uint32_t changes) {
+        if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
+            mFakeContext->updatePointerDisplay();
+        }
         mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
     }
 
-    void addMapperAndConfigure(InputMapper* mapper) {
-        mDevice->addMapper(mapper);
+    template <class T, typename... Args>
+    T& addMapperAndConfigure(Args... args) {
+        T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
         configureDevice(0);
         mDevice->reset(ARBITRARY_TIME);
+        return mapper;
     }
 
     void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
@@ -1848,15 +2309,15 @@
         mFakePolicy->clearViewports();
     }
 
-    static void process(InputMapper* mapper, nsecs_t when, int32_t type,
-            int32_t code, int32_t value) {
+    static void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code,
+                        int32_t value) {
         RawEvent event;
         event.when = when;
-        event.deviceId = mapper->getDeviceId();
+        event.deviceId = mapper.getDeviceContext().getEventHubId();
         event.type = type;
         event.code = code;
         event.value = value;
-        mapper->process(&event);
+        mapper.process(&event);
     }
 
     static void assertMotionRange(const InputDeviceInfo& info,
@@ -1897,11 +2358,11 @@
 
 const char* InputMapperTest::DEVICE_NAME = "device";
 const char* InputMapperTest::DEVICE_LOCATION = "USB1";
-const int32_t InputMapperTest::DEVICE_ID = 1;
+const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000;
 const int32_t InputMapperTest::DEVICE_GENERATION = 2;
 const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0;
 const uint32_t InputMapperTest::DEVICE_CLASSES = 0; // not needed for current tests
-
+const int32_t InputMapperTest::EVENTHUB_ID = 1;
 
 // --- SwitchInputMapperTest ---
 
@@ -1910,26 +2371,23 @@
 };
 
 TEST_F(SwitchInputMapperTest, GetSources) {
-    SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
-    addMapperAndConfigure(mapper);
+    SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
 
-    ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper->getSources());
+    ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper.getSources());
 }
 
 TEST_F(SwitchInputMapperTest, GetSwitchState) {
-    SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
-    addMapperAndConfigure(mapper);
+    SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
 
-    mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 1);
-    ASSERT_EQ(1, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
+    mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 1);
+    ASSERT_EQ(1, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
 
-    mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 0);
-    ASSERT_EQ(0, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
+    mFakeEventHub->setSwitchState(EVENTHUB_ID, SW_LID, 0);
+    ASSERT_EQ(0, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
 }
 
 TEST_F(SwitchInputMapperTest, Process) {
-    SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
-    addMapperAndConfigure(mapper);
+    SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
 
     process(mapper, ARBITRARY_TIME, EV_SW, SW_LID, 1);
     process(mapper, ARBITRARY_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
@@ -1954,8 +2412,9 @@
 
     void prepareDisplay(int32_t orientation);
 
-    void testDPadKeyRotation(KeyboardInputMapper* mapper,
-            int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode);
+    void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
+                             int32_t originalKeyCode, int32_t rotatedKeyCode,
+                             int32_t displayId = ADISPLAY_ID_NONE);
 };
 
 /* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
@@ -1966,8 +2425,9 @@
             orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
 }
 
-void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
-        int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) {
+void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
+                                                  int32_t originalScanCode, int32_t originalKeyCode,
+                                                  int32_t rotatedKeyCode, int32_t displayId) {
     NotifyKeyArgs args;
 
     process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 1);
@@ -1975,32 +2435,33 @@
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
     ASSERT_EQ(originalScanCode, args.scanCode);
     ASSERT_EQ(rotatedKeyCode, args.keyCode);
+    ASSERT_EQ(displayId, args.displayId);
 
     process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
     ASSERT_EQ(originalScanCode, args.scanCode);
     ASSERT_EQ(rotatedKeyCode, args.keyCode);
+    ASSERT_EQ(displayId, args.displayId);
 }
 
-
 TEST_F(KeyboardInputMapperTest, GetSources) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper.getSources());
 }
 
 TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) {
     const int32_t USAGE_A = 0x070004;
     const int32_t USAGE_UNKNOWN = 0x07ffff;
-    mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
-    mFakeEventHub->addKey(DEVICE_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     // Key down by scan code.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
@@ -2093,53 +2554,53 @@
 }
 
 TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
-    mFakeEventHub->addKey(DEVICE_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     // Initial metastate.
-    ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 
     // Metakey down.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
     NotifyKeyArgs args;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
-    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
     ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
 
     // Key down.
     process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
-    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
 
     // Key up.
     process(mapper, ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
-    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
 
     // Metakey up.
     process(mapper, ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_NONE, args.metaState);
-    ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
     ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
 }
 
 TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) {
-    mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -2153,58 +2614,58 @@
 }
 
 TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) {
-    mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addConfigurationProperty("keyboard.orientationAware", "1");
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     prepareDisplay(DISPLAY_ORIENTATION_0);
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_LEFT, DISPLAY_ID));
 
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_90);
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_UP, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_DOWN, DISPLAY_ID));
 
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_180);
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_UP, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
 
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_270);
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_UP, DISPLAY_ID));
 
     // Special case: if orientation changes while key is down, we still emit the same keycode
     // in the key up as we did in the key down.
@@ -2229,11 +2690,11 @@
 TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware) {
     // If the keyboard is not orientation aware,
     // key events should not be associated with a specific display id
-    mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     NotifyKeyArgs args;
 
     // Display id should be ADISPLAY_ID_NONE without any display configuration.
@@ -2254,12 +2715,12 @@
 TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) {
     // If the keyboard is orientation aware,
     // key events should be associated with the internal viewport
-    mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addConfigurationProperty("keyboard.orientationAware", "1");
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     NotifyKeyArgs args;
 
     // Display id should be ADISPLAY_ID_NONE without any display configuration.
@@ -2285,109 +2746,274 @@
 }
 
 TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 1);
-    ASSERT_EQ(1, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+    mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 1);
+    ASSERT_EQ(1, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
 
-    mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 0);
-    ASSERT_EQ(0, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+    mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 0);
+    ASSERT_EQ(0, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
 }
 
 TEST_F(KeyboardInputMapperTest, GetScanCodeState) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 1);
-    ASSERT_EQ(1, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+    mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 1);
+    ASSERT_EQ(1, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
 
-    mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 0);
-    ASSERT_EQ(0, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+    mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 0);
+    ASSERT_EQ(0, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
 }
 
 TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
 
     const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B };
     uint8_t flags[2] = { 0, 0 };
-    ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 1, keyCodes, flags));
+    ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, 1, keyCodes, flags));
     ASSERT_TRUE(flags[0]);
     ASSERT_FALSE(flags[1]);
 }
 
 TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) {
-    mFakeEventHub->addLed(DEVICE_ID, LED_CAPSL, true /*initially on*/);
-    mFakeEventHub->addLed(DEVICE_ID, LED_NUML, false /*initially off*/);
-    mFakeEventHub->addLed(DEVICE_ID, LED_SCROLLL, false /*initially off*/);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+    mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+    mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
+    mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     // Initialization should have turned all of the lights off.
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
 
     // Toggle caps lock on.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
-    ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper->getMetaState());
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
 
     // Toggle num lock on.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
-    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper->getMetaState());
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
 
     // Toggle caps lock off.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
-    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper->getMetaState());
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
 
     // Toggle scroll lock on.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
-    ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper->getMetaState());
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
 
     // Toggle num lock off.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
-    ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper->getMetaState());
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
 
     // Toggle scroll lock off.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
-    ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
-    ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 }
 
+TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
+    // keyboard 1.
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+
+    // keyboard 2.
+    const std::string USB2 = "USB2";
+    constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+    constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
+    InputDeviceIdentifier identifier;
+    identifier.name = "KEYBOARD2";
+    identifier.location = USB2;
+    std::unique_ptr<InputDevice> device2 =
+            std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
+                                          identifier);
+    mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+
+    KeyboardInputMapper& mapper2 =
+            device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
+                                                    AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
+    device2->reset(ARBITRARY_TIME);
+
+    // Prepared displays and associated info.
+    constexpr uint8_t hdmi1 = 0;
+    constexpr uint8_t hdmi2 = 1;
+    const std::string SECONDARY_UNIQUE_ID = "local:1";
+
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+    mFakePolicy->addInputPortAssociation(USB2, hdmi2);
+
+    // No associated display viewport found, should disable the device.
+    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    ASSERT_FALSE(device2->isEnabled());
+
+    // Prepare second display.
+    constexpr int32_t newDisplayId = 2;
+    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+                                 UNIQUE_ID, hdmi1, ViewportType::VIEWPORT_INTERNAL);
+    setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+                                 SECONDARY_UNIQUE_ID, hdmi2, ViewportType::VIEWPORT_EXTERNAL);
+    // Default device will reconfigure above, need additional reconfiguration for another device.
+    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+    // Device should be enabled after the associated display is found.
+    ASSERT_TRUE(mDevice->isEnabled());
+    ASSERT_TRUE(device2->isEnabled());
+
+    // Test pad key events
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_RIGHT, newDisplayId));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_DOWN, newDisplayId));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_LEFT, newDisplayId));
+}
+
+// --- KeyboardInputMapperTest_ExternalDevice ---
+
+class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
+protected:
+    virtual void SetUp() override {
+        InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL);
+    }
+};
+
+TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) {
+    // For external devices, non-media keys will trigger wake on key down. Media keys need to be
+    // marked as WAKE in the keylayout file to trigger wake.
+
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE,
+                          POLICY_FLAG_WAKE);
+
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+    NotifyKeyArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+}
+
+TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) {
+    // Tv Remote key's wake behavior is prescribed by the keylayout file.
+
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE);
+
+    addConfigurationProperty("keyboard.doNotWakeByDefault", "1");
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+    NotifyKeyArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+}
 
 // --- CursorInputMapperTest ---
 
@@ -2397,15 +3023,15 @@
 
     std::shared_ptr<FakePointerController> mFakePointerController;
 
-    virtual void SetUp() {
+    virtual void SetUp() override {
         InputMapperTest::SetUp();
 
         mFakePointerController = std::make_shared<FakePointerController>();
         mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController);
     }
 
-    void testMotionRotation(CursorInputMapper* mapper,
-            int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY);
+    void testMotionRotation(CursorInputMapper& mapper, int32_t originalX, int32_t originalY,
+                            int32_t rotatedX, int32_t rotatedY);
 
     void prepareDisplay(int32_t orientation) {
         const std::string uniqueId = "local:0";
@@ -2417,8 +3043,9 @@
 
 const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
 
-void CursorInputMapperTest::testMotionRotation(CursorInputMapper* mapper,
-        int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY) {
+void CursorInputMapperTest::testMotionRotation(CursorInputMapper& mapper, int32_t originalX,
+                                               int32_t originalY, int32_t rotatedX,
+                                               int32_t rotatedY) {
     NotifyMotionArgs args;
 
     process(mapper, ARBITRARY_TIME, EV_REL, REL_X, originalX);
@@ -2433,28 +3060,25 @@
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsNavigation_GetSources_ReturnsTrackball) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper.getSources());
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeFromPointerController) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     InputDeviceInfo info;
-    mapper->populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(&info);
 
     // Initially there may not be a valid motion range.
     ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
@@ -2466,7 +3090,7 @@
     mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
 
     InputDeviceInfo info2;
-    mapper->populateDeviceInfo(&info2);
+    mapper.populateDeviceInfo(&info2);
 
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
             AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE,
@@ -2480,12 +3104,11 @@
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsScaledRange) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     InputDeviceInfo info;
-    mapper->populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(&info);
 
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
             AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL,
@@ -2499,9 +3122,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -2592,9 +3214,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -2616,9 +3237,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -2650,9 +3270,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -2698,9 +3317,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
@@ -2714,10 +3332,9 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
     addConfigurationProperty("cursor.orientationAware", "1");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     prepareDisplay(DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
@@ -2761,9 +3378,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
     mFakePointerController->setPosition(100, 200);
@@ -3049,9 +3665,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerAround) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
     mFakePointerController->setPosition(100, 200);
@@ -3071,10 +3686,9 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_PointerCapture) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
     mFakePolicy->setPointerCapture(true);
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -3160,8 +3774,7 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     // Setup for second display.
     constexpr int32_t SECOND_DISPLAY_ID = 1;
@@ -3189,7 +3802,6 @@
     ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId);
 }
 
-
 // --- TouchInputMapperTest ---
 
 class TouchInputMapperTest : public InputMapperTest {
@@ -3312,10 +3924,10 @@
 }
 
 void TouchInputMapperTest::prepareVirtualKeys() {
-    mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[0]);
-    mFakeEventHub->addVirtualKeyDefinition(DEVICE_ID, VIRTUAL_KEYS[1]);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
-    mFakeEventHub->addKey(DEVICE_ID, KEY_MENU, 0, AKEYCODE_MENU, POLICY_FLAG_WAKE);
+    mFakeEventHub->addVirtualKeyDefinition(EVENTHUB_ID, VIRTUAL_KEYS[0]);
+    mFakeEventHub->addVirtualKeyDefinition(EVENTHUB_ID, VIRTUAL_KEYS[1]);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_MENU, 0, AKEYCODE_MENU, POLICY_FLAG_WAKE);
 }
 
 void TouchInputMapperTest::prepareLocationCalibration() {
@@ -3364,145 +3976,133 @@
     void prepareButtons();
     void prepareAxes(int axes);
 
-    void processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y);
-    void processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y);
-    void processUp(SingleTouchInputMapper* mappery);
-    void processPressure(SingleTouchInputMapper* mapper, int32_t pressure);
-    void processToolMajor(SingleTouchInputMapper* mapper, int32_t toolMajor);
-    void processDistance(SingleTouchInputMapper* mapper, int32_t distance);
-    void processTilt(SingleTouchInputMapper* mapper, int32_t tiltX, int32_t tiltY);
-    void processKey(SingleTouchInputMapper* mapper, int32_t code, int32_t value);
-    void processSync(SingleTouchInputMapper* mapper);
+    void processDown(SingleTouchInputMapper& mapper, int32_t x, int32_t y);
+    void processMove(SingleTouchInputMapper& mapper, int32_t x, int32_t y);
+    void processUp(SingleTouchInputMapper& mappery);
+    void processPressure(SingleTouchInputMapper& mapper, int32_t pressure);
+    void processToolMajor(SingleTouchInputMapper& mapper, int32_t toolMajor);
+    void processDistance(SingleTouchInputMapper& mapper, int32_t distance);
+    void processTilt(SingleTouchInputMapper& mapper, int32_t tiltX, int32_t tiltY);
+    void processKey(SingleTouchInputMapper& mapper, int32_t code, int32_t value);
+    void processSync(SingleTouchInputMapper& mapper);
 };
 
 void SingleTouchInputMapperTest::prepareButtons() {
-    mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
 }
 
 void SingleTouchInputMapperTest::prepareAxes(int axes) {
     if (axes & POSITION) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_X,
-                RAW_X_MIN, RAW_X_MAX, 0, 0);
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_Y,
-                RAW_Y_MIN, RAW_Y_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, RAW_X_MIN, RAW_X_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0);
     }
     if (axes & PRESSURE) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_PRESSURE,
-                RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_PRESSURE, RAW_PRESSURE_MIN,
+                                       RAW_PRESSURE_MAX, 0, 0);
     }
     if (axes & TOOL) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TOOL_WIDTH,
-                RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_TOOL_WIDTH, RAW_TOOL_MIN, RAW_TOOL_MAX, 0,
+                                       0);
     }
     if (axes & DISTANCE) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_DISTANCE,
-                RAW_DISTANCE_MIN, RAW_DISTANCE_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_DISTANCE, RAW_DISTANCE_MIN,
+                                       RAW_DISTANCE_MAX, 0, 0);
     }
     if (axes & TILT) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TILT_X,
-                RAW_TILT_MIN, RAW_TILT_MAX, 0, 0);
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_TILT_Y,
-                RAW_TILT_MIN, RAW_TILT_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_TILT_X, RAW_TILT_MIN, RAW_TILT_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_TILT_Y, RAW_TILT_MIN, RAW_TILT_MAX, 0, 0);
     }
 }
 
-void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
+void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper& mapper, int32_t x, int32_t y) {
     process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y);
 }
 
-void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
+void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper& mapper, int32_t x, int32_t y) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y);
 }
 
-void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper* mapper) {
+void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 0);
 }
 
-void SingleTouchInputMapperTest::processPressure(
-        SingleTouchInputMapper* mapper, int32_t pressure) {
+void SingleTouchInputMapperTest::processPressure(SingleTouchInputMapper& mapper, int32_t pressure) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_PRESSURE, pressure);
 }
 
-void SingleTouchInputMapperTest::processToolMajor(
-        SingleTouchInputMapper* mapper, int32_t toolMajor) {
+void SingleTouchInputMapperTest::processToolMajor(SingleTouchInputMapper& mapper,
+                                                  int32_t toolMajor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
 }
 
-void SingleTouchInputMapperTest::processDistance(
-        SingleTouchInputMapper* mapper, int32_t distance) {
+void SingleTouchInputMapperTest::processDistance(SingleTouchInputMapper& mapper, int32_t distance) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_DISTANCE, distance);
 }
 
-void SingleTouchInputMapperTest::processTilt(
-        SingleTouchInputMapper* mapper, int32_t tiltX, int32_t tiltY) {
+void SingleTouchInputMapperTest::processTilt(SingleTouchInputMapper& mapper, int32_t tiltX,
+                                             int32_t tiltY) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_X, tiltX);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_Y, tiltY);
 }
 
-void SingleTouchInputMapperTest::processKey(
-        SingleTouchInputMapper* mapper, int32_t code, int32_t value) {
+void SingleTouchInputMapperTest::processKey(SingleTouchInputMapper& mapper, int32_t code,
+                                            int32_t value) {
     process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
 }
 
-void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) {
+void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 }
 
-
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsACursor_ReturnsTouchPad) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
-    mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_X);
-    mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_Y);
+    mFakeEventHub->addRelativeAxis(EVENTHUB_ID, REL_X);
+    mFakeEventHub->addRelativeAxis(EVENTHUB_ID, REL_Y);
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchPad_ReturnsTouchPad) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareButtons();
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchPad");
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_ReturnsTouchScreen) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareButtons();
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     // Unknown key.
-    ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+    ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
 
     // Virtual key is down.
     int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
@@ -3511,27 +4111,26 @@
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
-    ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
+    ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
 
     // Virtual key is up.
     processUp(mapper);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
-    ASSERT_EQ(AKEY_STATE_UP, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
+    ASSERT_EQ(AKEY_STATE_UP, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
 }
 
 TEST_F(SingleTouchInputMapperTest, GetScanCodeState) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     // Unknown key.
-    ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+    ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
 
     // Virtual key is down.
     int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
@@ -3540,40 +4139,38 @@
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
-    ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
+    ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
 
     // Virtual key is up.
     processUp(mapper);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
-    ASSERT_EQ(AKEY_STATE_UP, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
+    ASSERT_EQ(AKEY_STATE_UP, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
 }
 
 TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     const int32_t keys[2] = { AKEYCODE_HOME, AKEYCODE_A };
     uint8_t flags[2] = { 0, 0 };
-    ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 2, keys, flags));
+    ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, 2, keys, flags));
     ASSERT_TRUE(flags[0]);
     ASSERT_FALSE(flags[1]);
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -3618,13 +4215,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -3740,13 +4336,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -3814,7 +4409,6 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture_VirtualDisplay) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     addConfigurationProperty("touch.displayId", VIRTUAL_DISPLAY_UNIQUE_ID);
 
@@ -3822,7 +4416,7 @@
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -3913,13 +4507,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -4004,12 +4597,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotateMotions) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareButtons();
     prepareAxes(POSITION);
     addConfigurationProperty("touch.orientationAware", "0");
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -4028,11 +4620,10 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -4094,12 +4685,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE | TOOL | DISTANCE | TILT);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -4139,13 +4729,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareLocationCalibration();
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     int32_t rawX = 100;
     int32_t rawY = 200;
@@ -4163,12 +4752,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     NotifyKeyArgs keyArgs;
@@ -4407,12 +4995,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -4543,13 +5130,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    mFakeEventHub->addKey(DEVICE_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0);
-    addMapperAndConfigure(mapper);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -4616,12 +5202,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsValueIsZero) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -4687,161 +5272,142 @@
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
 }
 
-
 // --- MultiTouchInputMapperTest ---
 
 class MultiTouchInputMapperTest : public TouchInputMapperTest {
 protected:
     void prepareAxes(int axes);
 
-    void processPosition(MultiTouchInputMapper* mapper, int32_t x, int32_t y);
-    void processTouchMajor(MultiTouchInputMapper* mapper, int32_t touchMajor);
-    void processTouchMinor(MultiTouchInputMapper* mapper, int32_t touchMinor);
-    void processToolMajor(MultiTouchInputMapper* mapper, int32_t toolMajor);
-    void processToolMinor(MultiTouchInputMapper* mapper, int32_t toolMinor);
-    void processOrientation(MultiTouchInputMapper* mapper, int32_t orientation);
-    void processPressure(MultiTouchInputMapper* mapper, int32_t pressure);
-    void processDistance(MultiTouchInputMapper* mapper, int32_t distance);
-    void processId(MultiTouchInputMapper* mapper, int32_t id);
-    void processSlot(MultiTouchInputMapper* mapper, int32_t slot);
-    void processToolType(MultiTouchInputMapper* mapper, int32_t toolType);
-    void processKey(MultiTouchInputMapper* mapper, int32_t code, int32_t value);
-    void processTimestamp(MultiTouchInputMapper* mapper, uint32_t value);
-    void processMTSync(MultiTouchInputMapper* mapper);
-    void processSync(MultiTouchInputMapper* mapper);
+    void processPosition(MultiTouchInputMapper& mapper, int32_t x, int32_t y);
+    void processTouchMajor(MultiTouchInputMapper& mapper, int32_t touchMajor);
+    void processTouchMinor(MultiTouchInputMapper& mapper, int32_t touchMinor);
+    void processToolMajor(MultiTouchInputMapper& mapper, int32_t toolMajor);
+    void processToolMinor(MultiTouchInputMapper& mapper, int32_t toolMinor);
+    void processOrientation(MultiTouchInputMapper& mapper, int32_t orientation);
+    void processPressure(MultiTouchInputMapper& mapper, int32_t pressure);
+    void processDistance(MultiTouchInputMapper& mapper, int32_t distance);
+    void processId(MultiTouchInputMapper& mapper, int32_t id);
+    void processSlot(MultiTouchInputMapper& mapper, int32_t slot);
+    void processToolType(MultiTouchInputMapper& mapper, int32_t toolType);
+    void processKey(MultiTouchInputMapper& mapper, int32_t code, int32_t value);
+    void processMTSync(MultiTouchInputMapper& mapper);
+    void processSync(MultiTouchInputMapper& mapper);
 };
 
 void MultiTouchInputMapperTest::prepareAxes(int axes) {
     if (axes & POSITION) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_POSITION_X,
-                RAW_X_MIN, RAW_X_MAX, 0, 0);
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_POSITION_Y,
-                RAW_Y_MIN, RAW_Y_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0);
     }
     if (axes & TOUCH) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOUCH_MAJOR,
-                RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MAJOR, RAW_TOUCH_MIN,
+                                       RAW_TOUCH_MAX, 0, 0);
         if (axes & MINOR) {
-            mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOUCH_MINOR,
-                    RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0);
+            mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOUCH_MINOR, RAW_TOUCH_MIN,
+                                           RAW_TOUCH_MAX, 0, 0);
         }
     }
     if (axes & TOOL) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_WIDTH_MAJOR,
-                RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MAJOR, RAW_TOOL_MIN, RAW_TOOL_MAX,
+                                       0, 0);
         if (axes & MINOR) {
-            mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_WIDTH_MINOR,
-                    RAW_TOOL_MAX, RAW_TOOL_MAX, 0, 0);
+            mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_WIDTH_MINOR, RAW_TOOL_MAX,
+                                           RAW_TOOL_MAX, 0, 0);
         }
     }
     if (axes & ORIENTATION) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_ORIENTATION,
-                RAW_ORIENTATION_MIN, RAW_ORIENTATION_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_ORIENTATION, RAW_ORIENTATION_MIN,
+                                       RAW_ORIENTATION_MAX, 0, 0);
     }
     if (axes & PRESSURE) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_PRESSURE,
-                RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_PRESSURE, RAW_PRESSURE_MIN,
+                                       RAW_PRESSURE_MAX, 0, 0);
     }
     if (axes & DISTANCE) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_DISTANCE,
-                RAW_DISTANCE_MIN, RAW_DISTANCE_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_DISTANCE, RAW_DISTANCE_MIN,
+                                       RAW_DISTANCE_MAX, 0, 0);
     }
     if (axes & ID) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TRACKING_ID,
-                RAW_ID_MIN, RAW_ID_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX, 0,
+                                       0);
     }
     if (axes & SLOT) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_SLOT,
-                RAW_SLOT_MIN, RAW_SLOT_MAX, 0, 0);
-        mFakeEventHub->setAbsoluteAxisValue(DEVICE_ID, ABS_MT_SLOT, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX, 0, 0);
+        mFakeEventHub->setAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, 0);
     }
     if (axes & TOOL_TYPE) {
-        mFakeEventHub->addAbsoluteAxis(DEVICE_ID, ABS_MT_TOOL_TYPE,
-                0, MT_TOOL_MAX, 0, 0);
+        mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
     }
 }
 
-void MultiTouchInputMapperTest::processPosition(
-        MultiTouchInputMapper* mapper, int32_t x, int32_t y) {
+void MultiTouchInputMapperTest::processPosition(MultiTouchInputMapper& mapper, int32_t x,
+                                                int32_t y) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, x);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, y);
 }
 
-void MultiTouchInputMapperTest::processTouchMajor(
-        MultiTouchInputMapper* mapper, int32_t touchMajor) {
+void MultiTouchInputMapperTest::processTouchMajor(MultiTouchInputMapper& mapper,
+                                                  int32_t touchMajor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
 }
 
-void MultiTouchInputMapperTest::processTouchMinor(
-        MultiTouchInputMapper* mapper, int32_t touchMinor) {
+void MultiTouchInputMapperTest::processTouchMinor(MultiTouchInputMapper& mapper,
+                                                  int32_t touchMinor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
 }
 
-void MultiTouchInputMapperTest::processToolMajor(
-        MultiTouchInputMapper* mapper, int32_t toolMajor) {
+void MultiTouchInputMapperTest::processToolMajor(MultiTouchInputMapper& mapper, int32_t toolMajor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
 }
 
-void MultiTouchInputMapperTest::processToolMinor(
-        MultiTouchInputMapper* mapper, int32_t toolMinor) {
+void MultiTouchInputMapperTest::processToolMinor(MultiTouchInputMapper& mapper, int32_t toolMinor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
 }
 
-void MultiTouchInputMapperTest::processOrientation(
-        MultiTouchInputMapper* mapper, int32_t orientation) {
+void MultiTouchInputMapperTest::processOrientation(MultiTouchInputMapper& mapper,
+                                                   int32_t orientation) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_ORIENTATION, orientation);
 }
 
-void MultiTouchInputMapperTest::processPressure(
-        MultiTouchInputMapper* mapper, int32_t pressure) {
+void MultiTouchInputMapperTest::processPressure(MultiTouchInputMapper& mapper, int32_t pressure) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_PRESSURE, pressure);
 }
 
-void MultiTouchInputMapperTest::processDistance(
-        MultiTouchInputMapper* mapper, int32_t distance) {
+void MultiTouchInputMapperTest::processDistance(MultiTouchInputMapper& mapper, int32_t distance) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_DISTANCE, distance);
 }
 
-void MultiTouchInputMapperTest::processId(
-        MultiTouchInputMapper* mapper, int32_t id) {
+void MultiTouchInputMapperTest::processId(MultiTouchInputMapper& mapper, int32_t id) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, id);
 }
 
-void MultiTouchInputMapperTest::processSlot(
-        MultiTouchInputMapper* mapper, int32_t slot) {
+void MultiTouchInputMapperTest::processSlot(MultiTouchInputMapper& mapper, int32_t slot) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, slot);
 }
 
-void MultiTouchInputMapperTest::processToolType(
-        MultiTouchInputMapper* mapper, int32_t toolType) {
+void MultiTouchInputMapperTest::processToolType(MultiTouchInputMapper& mapper, int32_t toolType) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
 }
 
-void MultiTouchInputMapperTest::processKey(
-        MultiTouchInputMapper* mapper, int32_t code, int32_t value) {
+void MultiTouchInputMapperTest::processKey(MultiTouchInputMapper& mapper, int32_t code,
+                                           int32_t value) {
     process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
 }
 
-void MultiTouchInputMapperTest::processTimestamp(MultiTouchInputMapper* mapper, uint32_t value) {
-    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, value);
-}
-
-void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) {
+void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, EV_SYN, SYN_MT_REPORT, 0);
 }
 
-void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper* mapper) {
+void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 }
 
-
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5113,12 +5679,11 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5289,12 +5854,11 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5460,11 +6024,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR | DISTANCE);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -5510,12 +6073,11 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | MINOR);
     addConfigurationProperty("touch.size.calibration", "geometric");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -5548,7 +6110,6 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_SummedLinearCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL);
@@ -5556,7 +6117,7 @@
     addConfigurationProperty("touch.size.scale", "10");
     addConfigurationProperty("touch.size.bias", "160");
     addConfigurationProperty("touch.size.isSummed", "1");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     // Note: We only provide a single common touch/tool value because the device is assumed
@@ -5601,14 +6162,13 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_AreaCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL);
     addConfigurationProperty("touch.size.calibration", "area");
     addConfigurationProperty("touch.size.scale", "43");
     addConfigurationProperty("touch.size.bias", "3");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -5635,16 +6195,15 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_PressureAxis_AmplitudeCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | PRESSURE);
     addConfigurationProperty("touch.pressure.calibration", "amplitude");
     addConfigurationProperty("touch.pressure.scale", "0.01");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     InputDeviceInfo info;
-    mapper->populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(&info);
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
             AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_TOUCHSCREEN,
             0.0f, RAW_PRESSURE_MAX * 0.01, 0.0f, 0.0f));
@@ -5670,11 +6229,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     NotifyKeyArgs keyArgs;
@@ -5914,11 +6472,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6065,12 +6622,11 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT);
-    mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
-    addMapperAndConfigure(mapper);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6137,11 +6693,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfItsValueIsZero) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6208,71 +6763,12 @@
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
 }
 
-TEST_F(MultiTouchInputMapperTest, Process_HandlesTimestamp) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
-
-    addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
-    NotifyMotionArgs args;
-
-    // By default, deviceTimestamp should be zero
-    processPosition(mapper, 100, 100);
-    processMTSync(mapper);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(0U, args.deviceTimestamp);
-
-    // Now the timestamp of 1000 is reported by evdev and should appear in MotionArgs
-    processPosition(mapper, 0, 0);
-    processTimestamp(mapper, 1000);
-    processMTSync(mapper);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(1000U, args.deviceTimestamp);
-}
-
-TEST_F(MultiTouchInputMapperTest, WhenMapperIsReset_TimestampIsCleared) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
-
-    addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
-    NotifyMotionArgs args;
-
-    // Send a touch event with a timestamp
-    processPosition(mapper, 100, 100);
-    processTimestamp(mapper, 1);
-    processMTSync(mapper);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(1U, args.deviceTimestamp);
-
-    // Since the data accumulates, and new timestamp has not arrived, deviceTimestamp won't change
-    processPosition(mapper, 100, 200);
-    processMTSync(mapper);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(1U, args.deviceTimestamp);
-
-    mapper->reset(/* when */ 0);
-    // After the mapper is reset, deviceTimestamp should become zero again
-    processPosition(mapper, 100, 300);
-    processMTSync(mapper);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(0U, args.deviceTimestamp);
-}
-
 /**
  * Set the input device port <--> display port associations, and check that the
  * events are routed to the display that matches the display port.
  * This can be checked by looking at the displayId of the resulting NotifyMotionArgs.
  */
 TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     const std::string usb2 = "USB2";
     const uint8_t hdmi1 = 0;
     const uint8_t hdmi2 = 1;
@@ -6281,7 +6777,7 @@
 
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
     mFakePolicy->addInputPortAssociation(usb2, hdmi2);
@@ -6308,36 +6804,6 @@
     ASSERT_EQ(DISPLAY_ID, args.displayId);
 }
 
-/**
- * Expect fallback to internal viewport if device is external and external viewport is not present.
- */
-TEST_F(MultiTouchInputMapperTest, Viewports_Fallback) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
-    prepareAxes(POSITION);
-    addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    mDevice->setExternal(true);
-    addMapperAndConfigure(mapper);
-
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
-
-    NotifyMotionArgs motionArgs;
-
-    // Expect the event to be sent to the internal viewport,
-    // because an external viewport is not present.
-    processPosition(mapper, 100, 100);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId);
-
-    // Expect the event to be sent to the external viewport if it is present.
-    prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
-    processPosition(mapper, 100, 100);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
-    ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
-}
-
 TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
     // Setup for second display.
     std::shared_ptr<FakePointerController> fakePointerController =
@@ -6350,13 +6816,12 @@
     mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
     prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
 
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // Check source is mouse that would obtain the PointerController.
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 
     NotifyMotionArgs motionArgs;
     processPosition(mapper, 100, 100);
@@ -6369,35 +6834,35 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
     // Setup the first touch screen device.
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareAxes(POSITION | ID | SLOT);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // Create the second touch screen device, and enable multi fingers.
     const std::string USB2 = "USB2";
-    const int32_t SECOND_DEVICE_ID = 2;
+    constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+    constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
     InputDeviceIdentifier identifier;
-    identifier.name = DEVICE_NAME;
+    identifier.name = "TOUCHSCREEN2";
     identifier.location = USB2;
-    InputDevice* device2 = new InputDevice(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
-            DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
-    mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/);
-    mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX,
-            0 /*flat*/, 0 /*fuzz*/);
-    mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX,
-            0 /*flat*/, 0 /*fuzz*/);
-    mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX,
-            0 /*flat*/, 0 /*fuzz*/);
-    mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX,
-            0 /*flat*/, 0 /*fuzz*/);
-    mFakeEventHub->setAbsoluteAxisValue(SECOND_DEVICE_ID, ABS_MT_SLOT, 0 /*value*/);
-    mFakeEventHub->addConfigurationProperty(SECOND_DEVICE_ID, String8("touch.deviceType"),
-            String8("touchScreen"));
+    std::unique_ptr<InputDevice> device2 =
+            std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
+                                          identifier);
+    mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/);
+    mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX,
+                                   0 /*flat*/, 0 /*fuzz*/);
+    mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX,
+                                   0 /*flat*/, 0 /*fuzz*/);
+    mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX,
+                                   0 /*flat*/, 0 /*fuzz*/);
+    mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX,
+                                   0 /*flat*/, 0 /*fuzz*/);
+    mFakeEventHub->setAbsoluteAxisValue(SECOND_EVENTHUB_ID, ABS_MT_SLOT, 0 /*value*/);
+    mFakeEventHub->addConfigurationProperty(SECOND_EVENTHUB_ID, String8("touch.deviceType"),
+                                            String8("touchScreen"));
 
     // Setup the second touch screen device.
-    MultiTouchInputMapper* mapper2 = new MultiTouchInputMapper(device2);
-    device2->addMapper(mapper2);
+    MultiTouchInputMapper& mapper2 = device2->addMapper<MultiTouchInputMapper>(SECOND_EVENTHUB_ID);
     device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
     device2->reset(ARBITRARY_TIME);
 
@@ -6450,17 +6915,16 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     // Unrotated video frame
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
     std::vector<TouchVideoFrame> frames{frame};
-    mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
+    mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
     processPosition(mapper, 100, 200);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -6476,10 +6940,9 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
     // Unrotated video frame
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
     NotifyMotionArgs motionArgs;
@@ -6491,7 +6954,7 @@
         clearViewports();
         prepareDisplay(orientation);
         std::vector<TouchVideoFrame> frames{frame};
-        mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
+        mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
         processPosition(mapper, 100, 200);
         processSync(mapper);
         ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -6501,10 +6964,9 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
     // Unrotated video frames. There's no rule that they must all have the same dimensions,
     // so mix these.
     TouchVideoFrame frame1(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
@@ -6514,7 +6976,7 @@
     NotifyMotionArgs motionArgs;
 
     prepareDisplay(DISPLAY_ORIENTATION_90);
-    mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
+    mFakeEventHub->setVideoFrames({{EVENTHUB_ID, frames}});
     processPosition(mapper, 100, 200);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -6523,4 +6985,357 @@
     ASSERT_EQ(frames, motionArgs.videoFrames);
 }
 
+/**
+ * If we had defined port associations, but the viewport is not ready, the touch device would be
+ * expected to be disabled, and it should be enabled after the viewport has found.
+ */
+TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) {
+    constexpr uint8_t hdmi2 = 1;
+    const std::string secondaryUniqueId = "uniqueId2";
+    constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi2);
+
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareAxes(POSITION);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    ASSERT_EQ(mDevice->isEnabled(), false);
+
+    // Add display on hdmi2, the device should be enabled and can receive touch event.
+    prepareSecondaryDisplay(type, hdmi2);
+    ASSERT_EQ(mDevice->isEnabled(), true);
+
+    // Send a touch event.
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId);
+}
+
+
+
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    NotifyMotionArgs motionArgs;
+
+    constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
+    // finger down
+    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);
+
+    // finger move
+    processId(mapper, 1);
+    processPosition(mapper, x2, y2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // finger up.
+    processId(mapper, -1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // new finger down
+    processId(mapper, 1);
+    processPosition(mapper, x3, y3);
+    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);
+}
+
+/**
+ * Test touch should be canceled when received the MT_TOOL_PALM event, and the following MOVE and
+ * UP events should be ignored.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) {
+    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);
+
+    // Tool changed to MT_TOOL_PALM expect sending the cancel event.
+    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.
+    processId(mapper, 1);
+    processPosition(mapper, x2, y2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // finger up.
+    processId(mapper, -1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // new finger down
+    processToolType(mapper, MT_TOOL_FINGER);
+    processId(mapper, 1);
+    processPosition(mapper, x3, y3);
+    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);
+}
+
+/**
+ * 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 {
+protected:
+    virtual void SetUp() override {
+        InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL);
+    }
+};
+
+/**
+ * Expect fallback to internal viewport if device is external and external viewport is not present.
+ */
+TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) {
+    prepareAxes(POSITION);
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
+
+    NotifyMotionArgs motionArgs;
+
+    // Expect the event to be sent to the internal viewport,
+    // because an external viewport is not present.
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId);
+
+    // Expect the event to be sent to the external viewport if it is present.
+    prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
+}
+
+/**
+ * Test touch should not work if outside of surface.
+ */
+class MultiTouchInputMapperTest_SurfaceRange : public MultiTouchInputMapperTest {
+protected:
+    void halfDisplayToCenterHorizontal(int32_t orientation) {
+        std::optional<DisplayViewport> internalViewport =
+                mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+
+        // Half display to (width/4, 0, width * 3/4, height) to make display has offset.
+        internalViewport->orientation = orientation;
+        if (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270) {
+            internalViewport->logicalLeft = 0;
+            internalViewport->logicalTop = 0;
+            internalViewport->logicalRight = DISPLAY_HEIGHT;
+            internalViewport->logicalBottom = DISPLAY_WIDTH / 2;
+
+            internalViewport->physicalLeft = 0;
+            internalViewport->physicalTop = DISPLAY_WIDTH / 4;
+            internalViewport->physicalRight = DISPLAY_HEIGHT;
+            internalViewport->physicalBottom = DISPLAY_WIDTH * 3 / 4;
+
+            internalViewport->deviceWidth = DISPLAY_HEIGHT;
+            internalViewport->deviceHeight = DISPLAY_WIDTH;
+        } else {
+            internalViewport->logicalLeft = 0;
+            internalViewport->logicalTop = 0;
+            internalViewport->logicalRight = DISPLAY_WIDTH / 2;
+            internalViewport->logicalBottom = DISPLAY_HEIGHT;
+
+            internalViewport->physicalLeft = DISPLAY_WIDTH / 4;
+            internalViewport->physicalTop = 0;
+            internalViewport->physicalRight = DISPLAY_WIDTH * 3 / 4;
+            internalViewport->physicalBottom = DISPLAY_HEIGHT;
+
+            internalViewport->deviceWidth = DISPLAY_WIDTH;
+            internalViewport->deviceHeight = DISPLAY_HEIGHT;
+        }
+
+        mFakePolicy->updateViewport(internalViewport.value());
+        configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    }
+
+    void processPositionAndVerify(MultiTouchInputMapper& mapper, int32_t xInside, int32_t yInside,
+                                  int32_t xOutside, int32_t yOutside, int32_t xExpected,
+                                  int32_t yExpected) {
+        // touch on outside area should not work.
+        processPosition(mapper, toRawX(xOutside), toRawY(yOutside));
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+        // touch on inside area should receive the event.
+        NotifyMotionArgs args;
+        processPosition(mapper, toRawX(xInside), toRawY(yInside));
+        processSync(mapper);
+        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+        ASSERT_NEAR(xExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+        ASSERT_NEAR(yExpected, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+
+        // Reset.
+        mapper.reset(ARBITRARY_TIME);
+    }
+};
+
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // Touch on center of normal display should work.
+    const int32_t x = DISPLAY_WIDTH / 4;
+    const int32_t y = DISPLAY_HEIGHT / 2;
+    processPosition(mapper, toRawX(x), toRawY(y));
+    processSync(mapper);
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], x, y, 1.0f, 0.0f, 0.0f, 0.0f,
+                                                0.0f, 0.0f, 0.0f, 0.0f));
+    // Reset.
+    mapper.reset(ARBITRARY_TIME);
+
+    // Let physical display be different to device, and make surface and physical could be 1:1.
+    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_0);
+
+    const int32_t xExpected = (x + 1) - (DISPLAY_WIDTH / 4);
+    const int32_t yExpected = y;
+    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+}
+
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_90) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // Half display to (width/4, 0, width * 3/4, height) and rotate 90-degrees.
+    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_90);
+
+    const int32_t x = DISPLAY_WIDTH / 4;
+    const int32_t y = DISPLAY_HEIGHT / 2;
+
+    // expect x/y = swap x/y then reverse y.
+    const int32_t xExpected = y;
+    const int32_t yExpected = (DISPLAY_WIDTH * 3 / 4) - (x + 1);
+    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+}
+
+TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // Half display to (width/4, 0, width * 3/4, height) and rotate 270-degrees.
+    halfDisplayToCenterHorizontal(DISPLAY_ORIENTATION_270);
+
+    const int32_t x = DISPLAY_WIDTH / 4;
+    const int32_t y = DISPLAY_HEIGHT / 2;
+
+    // expect x/y = swap x/y then reverse x.
+    constexpr int32_t xExpected = DISPLAY_HEIGHT - y;
+    constexpr int32_t yExpected = (x + 1) - DISPLAY_WIDTH / 4;
+    processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected);
+}
 } // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 3ee33f1..9bff166 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -23,93 +23,126 @@
 
 // --- TestInputListener ---
 
-TestInputListener::TestInputListener() { }
+TestInputListener::TestInputListener(std::chrono::milliseconds eventHappenedTimeout,
+                                     std::chrono::milliseconds eventDidNotHappenTimeout)
+      : mEventHappenedTimeout(eventHappenedTimeout),
+        mEventDidNotHappenTimeout(eventDidNotHappenTimeout) {}
 
 TestInputListener::~TestInputListener() { }
 
 void TestInputListener::assertNotifyConfigurationChangedWasCalled(
         NotifyConfigurationChangedArgs* outEventArgs) {
-    ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
-            << "Expected notifyConfigurationChanged() to have been called.";
-    if (outEventArgs) {
-        *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
-    }
-    mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
+    ASSERT_NO_FATAL_FAILURE(
+            assertCalled<NotifyConfigurationChangedArgs>(outEventArgs,
+                                                         "Expected notifyConfigurationChanged() "
+                                                         "to have been called."));
 }
 
 void TestInputListener::assertNotifyConfigurationChangedWasNotCalled() {
-    ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty())
-            << "Expected notifyConfigurationChanged() to not have been called.";
+    ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyConfigurationChangedArgs>(
+            "notifyConfigurationChanged() should not be called."));
 }
 
 void TestInputListener::assertNotifyDeviceResetWasCalled(
         NotifyDeviceResetArgs* outEventArgs) {
-    ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty())
-            << "Expected notifyDeviceReset() to have been called.";
-    if (outEventArgs) {
-        *outEventArgs = *mNotifyDeviceResetArgsQueue.begin();
-    }
-    mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin());
+    ASSERT_NO_FATAL_FAILURE(
+            assertCalled<
+                    NotifyDeviceResetArgs>(outEventArgs,
+                                           "Expected notifyDeviceReset() to have been called."));
 }
 
 void TestInputListener::assertNotifyDeviceResetWasNotCalled() {
-    ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty())
-            << "Expected notifyDeviceReset() to not have been called.";
+    ASSERT_NO_FATAL_FAILURE(
+            assertNotCalled<NotifyDeviceResetArgs>("notifyDeviceReset() should not be called."));
 }
 
 void TestInputListener::assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs) {
-    ASSERT_FALSE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to have been called.";
-    if (outEventArgs) {
-        *outEventArgs = *mNotifyKeyArgsQueue.begin();
-    }
-    mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin());
+    ASSERT_NO_FATAL_FAILURE(
+            assertCalled<NotifyKeyArgs>(outEventArgs, "Expected notifyKey() to have been called."));
 }
 
 void TestInputListener::assertNotifyKeyWasNotCalled() {
-    ASSERT_TRUE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to not have been called.";
+    ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called."));
 }
 
 void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) {
-    ASSERT_FALSE(mNotifyMotionArgsQueue.empty()) << "Expected notifyMotion() to have been called.";
-    if (outEventArgs) {
-        *outEventArgs = *mNotifyMotionArgsQueue.begin();
-    }
-    mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin());
+    ASSERT_NO_FATAL_FAILURE(
+            assertCalled<NotifyMotionArgs>(outEventArgs,
+                                           "Expected notifyMotion() to have been called."));
 }
 
 void TestInputListener::assertNotifyMotionWasNotCalled() {
-    ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
-            << "Expected notifyMotion() to not have been called.";
+    ASSERT_NO_FATAL_FAILURE(
+            assertNotCalled<NotifySwitchArgs>("notifySwitch() should not be called."));
 }
 
 void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) {
-    ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
-            << "Expected notifySwitch() to have been called.";
-    if (outEventArgs) {
-        *outEventArgs = *mNotifySwitchArgsQueue.begin();
+    ASSERT_NO_FATAL_FAILURE(
+            assertCalled<NotifySwitchArgs>(outEventArgs,
+                                           "Expected notifySwitch() to have been called."));
+}
+
+template <class NotifyArgsType>
+void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
+    std::unique_lock<std::mutex> lock(mLock);
+    base::ScopedLockAssertion assumeLocked(mLock);
+
+    std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
+    if (queue.empty()) {
+        const bool eventReceived =
+                mCondition.wait_for(lock, mEventHappenedTimeout,
+                                    [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+        if (!eventReceived) {
+            FAIL() << "Timed out waiting for event: " << message.c_str();
+        }
     }
-    mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());
+    if (outEventArgs) {
+        *outEventArgs = *queue.begin();
+    }
+    queue.erase(queue.begin());
+}
+
+template <class NotifyArgsType>
+void TestInputListener::assertNotCalled(std::string message) {
+    std::unique_lock<std::mutex> lock(mLock);
+    base::ScopedLockAssertion assumeLocked(mLock);
+
+    std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
+    const bool eventReceived =
+            mCondition.wait_for(lock, mEventDidNotHappenTimeout,
+                                [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+    if (eventReceived) {
+        FAIL() << "Unexpected event: " << message.c_str();
+    }
+}
+
+template <class NotifyArgsType>
+void TestInputListener::notify(const NotifyArgsType* args) {
+    std::scoped_lock<std::mutex> lock(mLock);
+
+    std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
+    queue.push_back(*args);
+    mCondition.notify_all();
 }
 
 void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
-    mNotifyConfigurationChangedArgsQueue.push_back(*args);
+    notify<NotifyConfigurationChangedArgs>(args);
 }
 
 void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
-    mNotifyDeviceResetArgsQueue.push_back(*args);
+    notify<NotifyDeviceResetArgs>(args);
 }
 
 void TestInputListener::notifyKey(const NotifyKeyArgs* args) {
-    mNotifyKeyArgsQueue.push_back(*args);
+    notify<NotifyKeyArgs>(args);
 }
 
 void TestInputListener::notifyMotion(const NotifyMotionArgs* args) {
-    mNotifyMotionArgsQueue.push_back(*args);
+    notify<NotifyMotionArgs>(args);
 }
 
 void TestInputListener::notifySwitch(const NotifySwitchArgs* args) {
-        mNotifySwitchArgsQueue.push_back(*args);
-    }
-
+    notify<NotifySwitchArgs>(args);
+}
 
 } // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 085d343..d50c6bc 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -17,26 +17,23 @@
 #ifndef _UI_TEST_INPUT_LISTENER_H
 #define _UI_TEST_INPUT_LISTENER_H
 
+#include <android-base/thread_annotations.h>
 #include <gtest/gtest.h>
 #include "InputListener.h"
 
+using std::chrono_literals::operator""ms;
+
 namespace android {
 
 // --- TestInputListener ---
 
 class TestInputListener : public InputListenerInterface {
-private:
-    std::vector<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgsQueue;
-    std::vector<NotifyDeviceResetArgs> mNotifyDeviceResetArgsQueue;
-    std::vector<NotifyKeyArgs> mNotifyKeyArgsQueue;
-    std::vector<NotifyMotionArgs> mNotifyMotionArgsQueue;
-    std::vector<NotifySwitchArgs> mNotifySwitchArgsQueue;
-
 protected:
     virtual ~TestInputListener();
 
 public:
-    TestInputListener();
+    TestInputListener(std::chrono::milliseconds eventHappenedTimeout = 0ms,
+                      std::chrono::milliseconds eventDidNotHappenTimeout = 0ms);
 
     void assertNotifyConfigurationChangedWasCalled(
             NotifyConfigurationChangedArgs* outEventArgs = nullptr);
@@ -58,15 +55,36 @@
     void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
 
 private:
-    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
+    template <class NotifyArgsType>
+    void assertCalled(NotifyArgsType* outEventArgs, std::string message);
 
-    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
+    template <class NotifyArgsType>
+    void assertNotCalled(std::string message);
 
-    virtual void notifyKey(const NotifyKeyArgs* args);
+    template <class NotifyArgsType>
+    void notify(const NotifyArgsType* args);
 
-    virtual void notifyMotion(const NotifyMotionArgs* args);
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
 
-    virtual void notifySwitch(const NotifySwitchArgs* args);
+    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+
+    virtual void notifyKey(const NotifyKeyArgs* args) override;
+
+    virtual void notifyMotion(const NotifyMotionArgs* args) override;
+
+    virtual void notifySwitch(const NotifySwitchArgs* args) override;
+
+    std::mutex mLock;
+    std::condition_variable mCondition;
+    const std::chrono::milliseconds mEventHappenedTimeout;
+    const std::chrono::milliseconds mEventDidNotHappenTimeout;
+
+    std::tuple<std::vector<NotifyConfigurationChangedArgs>, //
+               std::vector<NotifyDeviceResetArgs>,          //
+               std::vector<NotifyKeyArgs>,                  //
+               std::vector<NotifyMotionArgs>,               //
+               std::vector<NotifySwitchArgs>>               //
+            mQueues GUARDED_BY(mLock);
 };
 
 } // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
new file mode 100644
index 0000000..0659511
--- /dev/null
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UinputDevice.h"
+
+#include <android-base/stringprintf.h>
+
+namespace android {
+
+// --- UinputDevice ---
+
+UinputDevice::UinputDevice(const char* name) : mName(name) {}
+
+UinputDevice::~UinputDevice() {
+    if (ioctl(mDeviceFd, UI_DEV_DESTROY)) {
+        ALOGE("Error while destroying uinput device: %s", strerror(errno));
+    }
+    mDeviceFd.reset();
+}
+
+void UinputDevice::init() {
+    mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
+    if (mDeviceFd < 0) {
+        FAIL() << "Can't open /dev/uinput :" << strerror(errno);
+    }
+
+    struct uinput_user_dev device = {};
+    strlcpy(device.name, mName, UINPUT_MAX_NAME_SIZE);
+    device.id.bustype = BUS_USB;
+    device.id.vendor = 0x01;
+    device.id.product = 0x01;
+    device.id.version = 1;
+
+    ASSERT_NO_FATAL_FAILURE(configureDevice(mDeviceFd, &device));
+
+    if (write(mDeviceFd, &device, sizeof(device)) < 0) {
+        FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: "
+               << strerror(errno);
+    }
+
+    if (ioctl(mDeviceFd, UI_DEV_CREATE)) {
+        FAIL() << "Error in ioctl : UI_DEV_CREATE: " << strerror(errno);
+    }
+}
+
+void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) {
+    struct input_event event = {};
+    event.type = type;
+    event.code = code;
+    event.value = value;
+    event.time = {}; // uinput ignores the timestamp
+
+    if (write(mDeviceFd, &event, sizeof(input_event)) < 0) {
+        std::string msg = base::StringPrintf("Could not write event %" PRIu16 " %" PRIu16
+                                             " with value %" PRId32 " : %s",
+                                             type, code, value, strerror(errno));
+        ALOGE("%s", msg.c_str());
+        FAIL() << msg.c_str();
+    }
+}
+
+// --- UinputKeyboard ---
+
+UinputKeyboard::UinputKeyboard(std::initializer_list<int> keys)
+      : UinputDevice(UinputKeyboard::KEYBOARD_NAME), mKeys(keys.begin(), keys.end()) {}
+
+void UinputKeyboard::configureDevice(int fd, uinput_user_dev* device) {
+    // enable key press/release event
+    if (ioctl(fd, UI_SET_EVBIT, EV_KEY)) {
+        FAIL() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno);
+    }
+
+    // enable set of KEY events
+    std::for_each(mKeys.begin(), mKeys.end(), [fd](int key) {
+        if (ioctl(fd, UI_SET_KEYBIT, key)) {
+            FAIL() << "Error in ioctl : UI_SET_KEYBIT : " << key << " : " << strerror(errno);
+        }
+    });
+
+    // enable synchronization event
+    if (ioctl(fd, UI_SET_EVBIT, EV_SYN)) {
+        FAIL() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno);
+    }
+}
+
+void UinputKeyboard::pressKey(int key) {
+    if (mKeys.find(key) == mKeys.end()) {
+        FAIL() << mName << ": Cannot inject key press: Key not found: " << key;
+    }
+    injectEvent(EV_KEY, key, 1);
+    injectEvent(EV_SYN, SYN_REPORT, 0);
+}
+
+void UinputKeyboard::releaseKey(int key) {
+    if (mKeys.find(key) == mKeys.end()) {
+        FAIL() << mName << ": Cannot inject key release: Key not found: " << key;
+    }
+    injectEvent(EV_KEY, key, 0);
+    injectEvent(EV_SYN, SYN_REPORT, 0);
+}
+
+void UinputKeyboard::pressAndReleaseKey(int key) {
+    pressKey(key);
+    releaseKey(key);
+}
+
+// --- UinputHomeKey ---
+
+UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {}
+
+void UinputHomeKey::pressAndReleaseHomeKey() {
+    pressAndReleaseKey(KEY_HOME);
+}
+
+// --- UinputSteamController
+UinputSteamController::UinputSteamController() : UinputKeyboard({BTN_GEAR_DOWN, BTN_GEAR_UP}) {}
+
+// --- UinputTouchScreen ---
+UinputTouchScreen::UinputTouchScreen(const Rect* size)
+      : UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {}
+
+void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) {
+    // Setup the touch screen device
+    ioctl(fd, UI_SET_EVBIT, EV_KEY);
+    ioctl(fd, UI_SET_EVBIT, EV_REL);
+    ioctl(fd, UI_SET_EVBIT, EV_ABS);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
+    ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
+    ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+    ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+
+    device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN;
+    device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX;
+    device->absmin[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MIN;
+    device->absmax[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MAX;
+    device->absmin[ABS_MT_POSITION_X] = mSize.left;
+    device->absmax[ABS_MT_POSITION_X] = mSize.right - 1;
+    device->absmin[ABS_MT_POSITION_Y] = mSize.top;
+    device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1;
+    device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN;
+    device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX;
+}
+
+void UinputTouchScreen::sendSlot(int32_t slot) {
+    injectEvent(EV_ABS, ABS_MT_SLOT, slot);
+}
+
+void UinputTouchScreen::sendTrackingId(int32_t trackingId) {
+    injectEvent(EV_ABS, ABS_MT_TRACKING_ID, trackingId);
+}
+
+void UinputTouchScreen::sendDown(const Point& point) {
+    injectEvent(EV_KEY, BTN_TOUCH, 1);
+    injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x);
+    injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y);
+    injectEvent(EV_SYN, SYN_REPORT, 0);
+}
+
+void UinputTouchScreen::sendMove(const Point& point) {
+    injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x);
+    injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y);
+    injectEvent(EV_SYN, SYN_REPORT, 0);
+}
+
+void UinputTouchScreen::sendUp() {
+    sendTrackingId(0xffffffff);
+    injectEvent(EV_KEY, BTN_TOUCH, 0);
+    injectEvent(EV_SYN, SYN_REPORT, 0);
+}
+
+void UinputTouchScreen::sendToolType(int32_t toolType) {
+    injectEvent(EV_ABS, ABS_MT_TOOL_TYPE, toolType);
+    injectEvent(EV_SYN, SYN_REPORT, 0);
+}
+
+// Get the center x, y base on the range definition.
+const Point UinputTouchScreen::getCenterPoint() {
+    return Point(mSize.left + mSize.width() / 2, mSize.top + mSize.height() / 2);
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
new file mode 100644
index 0000000..22d1f63
--- /dev/null
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_TEST_INPUT_UINPUT_INJECTOR_H
+#define _UI_TEST_INPUT_UINPUT_INJECTOR_H
+
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+#include <ui/Point.h>
+#include <ui/Rect.h>
+
+#include <memory>
+
+namespace android {
+
+// This is the factory method that must be used to create a UinputDevice.
+template <class D, class... Ts>
+std::unique_ptr<D> createUinputDevice(Ts... args) {
+    // Using `new` to access non-public constructors.
+    std::unique_ptr<D> dev(new D(&args...));
+    EXPECT_NO_FATAL_FAILURE(dev->init());
+    return dev;
+}
+
+// --- UinputDevice ---
+
+class UinputDevice {
+public:
+    virtual ~UinputDevice();
+
+    inline const char* getName() const { return mName; }
+
+    // Subclasses must either provide a public constructor or must be-friend the factory method.
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+protected:
+    const char* mName;
+
+    UinputDevice(const char* name);
+
+    // Signals which types of events this device supports before it is created.
+    // This must be overridden by subclasses.
+    virtual void configureDevice(int fd, uinput_user_dev* device) = 0;
+
+    void injectEvent(uint16_t type, uint16_t code, int32_t value);
+
+private:
+    base::unique_fd mDeviceFd;
+
+    // This is called once by the factory method createUinputDevice().
+    void init();
+};
+
+// --- UinputKeyboard ---
+
+class UinputKeyboard : public UinputDevice {
+public:
+    static constexpr const char* KEYBOARD_NAME = "Test Keyboard Device";
+
+    // Injects key press and sync.
+    void pressKey(int key);
+    // Injects key release and sync.
+    void releaseKey(int key);
+    // Injects 4 events: key press, sync, key release, and sync.
+    void pressAndReleaseKey(int key);
+
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+protected:
+    UinputKeyboard(std::initializer_list<int> keys = {});
+
+private:
+    void configureDevice(int fd, uinput_user_dev* device) override;
+
+    std::set<int> mKeys;
+};
+
+// --- UinputHomeKey---
+
+// A keyboard device that has a single HOME key.
+class UinputHomeKey : public UinputKeyboard {
+public:
+    // Injects 4 events: key press, sync, key release, and sync.
+    void pressAndReleaseHomeKey();
+
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+private:
+    UinputHomeKey();
+};
+
+// A joystick device that sends a BTN_GEAR_DOWN / BTN_WHEEL key.
+class UinputSteamController : public UinputKeyboard {
+public:
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+private:
+    UinputSteamController();
+};
+
+// --- UinputTouchScreen ---
+// A touch screen device with specific size.
+class UinputTouchScreen : public UinputDevice {
+public:
+    static constexpr const char* DEVICE_NAME = "Test Touch Screen";
+    static const int32_t RAW_TOUCH_MIN = 0;
+    static const int32_t RAW_TOUCH_MAX = 31;
+    static const int32_t RAW_ID_MIN = 0;
+    static const int32_t RAW_ID_MAX = 9;
+    static const int32_t RAW_SLOT_MIN = 0;
+    static const int32_t RAW_SLOT_MAX = 9;
+    static const int32_t RAW_PRESSURE_MIN = 0;
+    static const int32_t RAW_PRESSURE_MAX = 255;
+
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+    void sendSlot(int32_t slot);
+    void sendTrackingId(int32_t trackingId);
+    void sendDown(const Point& point);
+    void sendMove(const Point& point);
+    void sendUp();
+    void sendToolType(int32_t toolType);
+
+    const Point getCenterPoint();
+
+protected:
+    UinputTouchScreen(const Rect* size);
+
+private:
+    void configureDevice(int fd, uinput_user_dev* device) override;
+    const Rect mSize;
+};
+
+} // namespace android
+
+#endif // _UI_TEST_INPUT_UINPUT_INJECTOR_H
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 7b3af70..b0d3e3b 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -1,11 +1,25 @@
 cc_library_shared {
     name: "libpowermanager",
 
-    srcs: ["IPowerManager.cpp"],
+    srcs: [
+        "IPowerManager.cpp",
+        "Temperature.cpp",
+        "CoolingDevice.cpp",
+        ":libpowermanager_aidl",
+    ],
+
+    aidl: {
+       local_include_dirs: ["include"],
+       include_dirs: [
+           "frameworks/base/core/java/android/os",
+       ],
+       export_aidl_headers: true
+    },
 
     shared_libs: [
         "libutils",
         "libbinder",
+        "liblog"
     ],
 
     cflags: [
@@ -14,4 +28,28 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
+
+    local_include_dirs: ["include"],
+    export_include_dirs: [
+         "include",
+    ],
+}
+
+cc_test {
+    name: "thermalmanager-test",
+    srcs: ["IThermalManagerTest.cpp",
+          ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "libpowermanager",
+        "libbinder",
+        "libutils",
+    ],
 }
diff --git a/services/powermanager/CoolingDevice.cpp b/services/powermanager/CoolingDevice.cpp
new file mode 100644
index 0000000..ebbc1b4
--- /dev/null
+++ b/services/powermanager/CoolingDevice.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "CoolingDevice"
+
+#include <android/CoolingDevice.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace os {
+
+status_t CoolingDevice::readFromParcel(const android::Parcel *parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    parcel->readFloat(&mValue);
+    parcel->readUint32(&mType);
+    parcel->readString16(&mName);
+
+    return OK;
+}
+
+status_t CoolingDevice::writeToParcel(android::Parcel *parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    parcel->writeFloat(mValue);
+    parcel->writeUint32(mType);
+    parcel->writeString16(mName);
+
+    return OK;
+}
+
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/services/powermanager/IThermalManagerTest.cpp b/services/powermanager/IThermalManagerTest.cpp
new file mode 100644
index 0000000..575b9ee
--- /dev/null
+++ b/services/powermanager/IThermalManagerTest.cpp
@@ -0,0 +1,158 @@
+/*
+ * 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 "ThermalManagerTest"
+//#define LOG_NDEBUG 0
+
+#include <thread>
+
+#include <android/os/BnThermalStatusListener.h>
+#include <android/os/IThermalService.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <condition_variable>
+#include <gtest/gtest.h>
+#include <powermanager/PowerManager.h>
+#include <utils/Log.h>
+
+using namespace android;
+using namespace android::os;
+using namespace std::chrono_literals;
+
+class IThermalServiceTest : public testing::Test,
+                            public BnThermalStatusListener{
+    public:
+        IThermalServiceTest();
+        void setThermalOverride(int level);
+        virtual binder::Status onStatusChange(int status) override;
+        int getStatusFromService();
+        void SetUp() override;
+        void TearDown() override;
+    protected:
+        sp<IThermalService> mThermalSvc;
+        std::condition_variable mCondition;
+        int mListenerStatus;
+        int mServiceStatus;
+        std::mutex mMutex;
+};
+
+IThermalServiceTest::IThermalServiceTest()
+ : mListenerStatus(0),
+   mServiceStatus(0) {
+}
+
+void IThermalServiceTest::setThermalOverride(int level) {
+    std::string cmdStr = "cmd thermalservice override-status " + std::to_string(level);
+    system(cmdStr.c_str());
+}
+
+binder::Status IThermalServiceTest::onStatusChange(int status) {
+    std::unique_lock<std::mutex> lock(mMutex);
+    mListenerStatus = status;
+    ALOGI("IThermalServiceTest::notifyListener %d", mListenerStatus);
+    mCondition.notify_all();
+    return binder::Status::ok();
+}
+
+int IThermalServiceTest::getStatusFromService() {
+    int status;
+    binder::Status ret = mThermalSvc->getCurrentThermalStatus(&status);
+    if (ret.isOk()) {
+        return status;
+    } else {
+        return BAD_VALUE;
+    }
+}
+
+void IThermalServiceTest::SetUp() {
+    setThermalOverride(0);
+    // use checkService() to avoid blocking if thermal service is not up yet
+    sp<IBinder> binder =
+        defaultServiceManager()->checkService(String16("thermalservice"));
+    EXPECT_NE(binder, nullptr);
+    mThermalSvc = interface_cast<IThermalService>(binder);
+    EXPECT_NE(mThermalSvc, nullptr);
+    bool success = false;
+    binder::Status ret = mThermalSvc->registerThermalStatusListener(this, &success);
+    ASSERT_TRUE(success);
+    ASSERT_TRUE(ret.isOk());
+    // Wait for listener called after registration, shouldn't timeout
+    std::unique_lock<std::mutex> lock(mMutex);
+    EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout);
+}
+
+void IThermalServiceTest::TearDown() {
+    bool success = false;
+    binder::Status ret = mThermalSvc->unregisterThermalStatusListener(this, &success);
+    ASSERT_TRUE(success);
+    ASSERT_TRUE(ret.isOk());
+}
+
+class IThermalListenerTest : public IThermalServiceTest, public testing::WithParamInterface<int32_t> {
+  public:
+    static auto PrintParam(const testing::TestParamInfo<ParamType> &info) {
+        return std::to_string(info.param);
+    }
+};
+
+TEST_P(IThermalListenerTest, TestListener) {
+    int level = GetParam();
+    std::unique_lock<std::mutex> lock(mMutex);
+    // Set the override thermal status
+    setThermalOverride(level);
+    // Wait for listener called, shouldn't timeout
+    EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout);
+    // Check the result
+    EXPECT_EQ(level, mListenerStatus);
+    ALOGI("Thermal listener status %d, expecting %d", mListenerStatus, level);
+}
+
+INSTANTIATE_TEST_SUITE_P(TestListenerLevels, IThermalListenerTest, testing::Range(
+        static_cast<int>(ThermalStatus::THERMAL_STATUS_LIGHT),
+        static_cast<int>(ThermalStatus::THERMAL_STATUS_SHUTDOWN)),
+        IThermalListenerTest::PrintParam);
+
+class IThermalLevelTest : public IThermalServiceTest, public testing::WithParamInterface<int32_t> {
+  public:
+    static auto PrintParam(const testing::TestParamInfo<ParamType> &info) {
+        return std::to_string(info.param);
+    }
+};
+
+TEST_P(IThermalLevelTest, TestGetStatusLevel) {
+    int level = GetParam();
+    setThermalOverride(level);
+    mServiceStatus = getStatusFromService();
+    EXPECT_EQ(level, mServiceStatus);
+}
+
+INSTANTIATE_TEST_SUITE_P(TestStatusLevels, IThermalLevelTest, testing::Range(
+        static_cast<int>(ThermalStatus::THERMAL_STATUS_NONE),
+        static_cast<int>(ThermalStatus::THERMAL_STATUS_SHUTDOWN)),
+        IThermalLevelTest::PrintParam);
+
+int main(int argc, char **argv) {
+    std::unique_ptr<std::thread> binderLoop;
+    binderLoop = std::make_unique<std::thread>(
+            [&] { IPCThreadState::self()->joinThreadPool(true); });
+
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    ALOGV("Test result = %d\n", status);
+
+    return status;
+}
\ No newline at end of file
diff --git a/services/powermanager/Temperature.cpp b/services/powermanager/Temperature.cpp
new file mode 100644
index 0000000..8ec0a87
--- /dev/null
+++ b/services/powermanager/Temperature.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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 "Temperature"
+
+#include <android/Temperature.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace os {
+
+status_t Temperature::readFromParcel(const android::Parcel *parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    parcel->readFloat(&mValue);
+    parcel->readInt32(&mType);
+    parcel->readString16(&mName);
+    parcel->readInt32(&mStatus);
+
+    return OK;
+}
+
+status_t Temperature::writeToParcel(android::Parcel *parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    parcel->writeFloat(mValue);
+    parcel->writeInt32(mType);
+    parcel->writeString16(mName);
+    parcel->writeInt32(mStatus);
+
+    return OK;
+}
+
+} // namespace os
+} // namespace android
\ No newline at end of file
diff --git a/services/powermanager/include/android/CoolingDevice.h b/services/powermanager/include/android/CoolingDevice.h
new file mode 100644
index 0000000..2f366be
--- /dev/null
+++ b/services/powermanager/include/android/CoolingDevice.h
@@ -0,0 +1,49 @@
+/*
+ * 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_OS_COOLINGDEVICE_H
+#define ANDROID_OS_COOLINGDEVICE_H
+
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace os {
+
+/**
+ * CoolingDevice is a structure to encapsulate cooling device status.
+ */
+struct CoolingDevice : public android::Parcelable {
+    /** Current throttle state of the cooling device.  */
+    float mValue;
+    /** A cooling device type from ThermalHAL */
+    uint32_t mType;
+    /** Name of this cooling device */
+    String16 mName;
+
+    CoolingDevice()
+        : mValue(0.0f),
+          mType(0),
+          mName("") {
+    }
+    virtual status_t readFromParcel(const android::Parcel* parcel) override;
+    virtual status_t writeToParcel(android::Parcel* parcel) const override;
+};
+
+} // namespace os
+} // namespace android
+
+#endif /* ANDROID_OS_COOLINGDEVICE_H */
diff --git a/services/powermanager/include/android/Temperature.h b/services/powermanager/include/android/Temperature.h
new file mode 100644
index 0000000..2e68ec4
--- /dev/null
+++ b/services/powermanager/include/android/Temperature.h
@@ -0,0 +1,53 @@
+/*
+ * 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_OS_TEMPERATURE_H
+#define ANDROID_OS_TEMPERATURE_H
+
+#include <binder/Parcelable.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace os {
+
+/**
+ * Temperature is a structure to encapsulate temperature status.
+ */
+struct Temperature : public android::Parcelable {
+    /** Temperature value */
+    float mValue;
+    /** A Temperature type from ThermalHAL */
+    int32_t mType;
+    /** Name of this Temperature */
+    String16 mName;
+    /** The level of the sensor is currently in throttling */
+    int32_t mStatus;
+
+    Temperature()
+        : mValue(0.0f),
+          mType(0),
+          mName(""),
+          mStatus(0) {
+    }
+
+    virtual status_t readFromParcel(const android::Parcel* parcel) override;
+    virtual status_t writeToParcel(android::Parcel* parcel) const override;
+};
+
+} // namespace os
+} // namespace android
+
+#endif /* ANDROID_OS_TEMPERATURE_H */
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 1c9a4af..532a2e5 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -33,6 +33,10 @@
         "-fvisibility=hidden"
     ],
 
+    header_libs: [
+        "android.hardware.sensors@2.X-shared-utils",
+    ],
+
     shared_libs: [
         "libcutils",
         "libhardware",
@@ -42,15 +46,21 @@
         "libbinder",
         "libsensor",
         "libsensorprivacy",
+        "libprotoutil",
         "libcrypto",
         "libbase",
         "libhidlbase",
         "libfmq",
         "android.hardware.sensors@1.0",
         "android.hardware.sensors@2.0",
+        "android.hardware.sensors@2.1",
     ],
 
-    static_libs: ["android.hardware.sensors@1.0-convert"],
+    static_libs: [
+        "android.hardware.sensors@1.0-convert",
+    ],
+
+    generated_headers: ["framework-cppstream-protos"],
 
     // our public headers depend on libsensor and libsensorprivacy
     export_shared_lib_headers: ["libsensor", "libsensorprivacy"],
diff --git a/services/sensorservice/RecentEventLogger.cpp b/services/sensorservice/RecentEventLogger.cpp
index 207b097..d7ca6e1 100644
--- a/services/sensorservice/RecentEventLogger.cpp
+++ b/services/sensorservice/RecentEventLogger.cpp
@@ -17,6 +17,8 @@
 #include "RecentEventLogger.h"
 #include "SensorServiceUtils.h"
 
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <utils/Timers.h>
 
 #include <inttypes.h>
@@ -84,6 +86,40 @@
     return std::string(buffer.string());
 }
 
+/**
+ * Dump debugging information as android.service.SensorEventsProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void RecentEventLogger::dump(util::ProtoOutputStream* proto) const {
+    using namespace service::SensorEventsProto;
+    std::lock_guard<std::mutex> lk(mLock);
+
+    proto->write(RecentEventsLog::RECENT_EVENTS_COUNT, int(mRecentEvents.size()));
+    for (int i = mRecentEvents.size() - 1; i >= 0; --i) {
+        const auto& ev = mRecentEvents[i];
+        const uint64_t token = proto->start(RecentEventsLog::EVENTS);
+        proto->write(Event::TIMESTAMP_SEC, float(ev.mEvent.timestamp) / 1e9f);
+        proto->write(Event::WALL_TIMESTAMP_MS, ev.mWallTime.tv_sec * 1000LL
+                + ns2ms(ev.mWallTime.tv_nsec));
+
+        if (mMaskData) {
+            proto->write(Event::MASKED, true);
+        } else {
+            if (mSensorType == SENSOR_TYPE_STEP_COUNTER) {
+                proto->write(Event::INT64_DATA, int64_t(ev.mEvent.u64.step_counter));
+            } else {
+                for (size_t k = 0; k < mEventSize; ++k) {
+                    proto->write(Event::FLOAT_ARRAY, ev.mEvent.data[k]);
+                }
+            }
+        }
+        proto->end(token);
+    }
+}
+
 void RecentEventLogger::setFormat(std::string format) {
     if (format == "mask_data" ) {
         mMaskData = true;
diff --git a/services/sensorservice/RecentEventLogger.h b/services/sensorservice/RecentEventLogger.h
index 67378b7..3a2ae77 100644
--- a/services/sensorservice/RecentEventLogger.h
+++ b/services/sensorservice/RecentEventLogger.h
@@ -48,6 +48,7 @@
 
     // Dumpable interface
     virtual std::string dump() const override;
+    virtual void dump(util::ProtoOutputStream* proto) const override;
     virtual void setFormat(std::string format) override;
 
 protected:
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 717f317..8a282e2 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -16,16 +16,20 @@
 
 #include "SensorDevice.h"
 
-#include "android/hardware/sensors/2.0/ISensorsCallback.h"
 #include "android/hardware/sensors/2.0/types.h"
-#include "SensorService.h"
+#include "android/hardware/sensors/2.1/ISensorsCallback.h"
+#include "android/hardware/sensors/2.1/types.h"
+#include "convertV2_1.h"
 
 #include <android-base/logging.h>
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <sensors/convert.h>
 #include <cutils/atomic.h>
 #include <utils/Errors.h>
 #include <utils/Singleton.h>
 
+#include <cstddef>
 #include <chrono>
 #include <cinttypes>
 #include <thread>
@@ -33,12 +37,19 @@
 using namespace android::hardware::sensors;
 using namespace android::hardware::sensors::V1_0;
 using namespace android::hardware::sensors::V1_0::implementation;
-using android::hardware::sensors::V2_0::ISensorsCallback;
 using android::hardware::sensors::V2_0::EventQueueFlagBits;
 using android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
+using android::hardware::sensors::V2_1::ISensorsCallback;
+using android::hardware::sensors::V2_1::implementation::convertToOldSensorInfo;
+using android::hardware::sensors::V2_1::implementation::convertToNewSensorInfos;
+using android::hardware::sensors::V2_1::implementation::convertToNewEvents;
+using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV1_0;
+using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_0;
+using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_1;
 using android::hardware::hidl_vec;
 using android::hardware::Return;
 using android::SensorDeviceUtils::HidlServiceRegistrationWaiter;
+using android::util::ProtoOutputStream;
 
 namespace android {
 // ---------------------------------------------------------------------------
@@ -84,11 +95,19 @@
 
 struct SensorsCallback : public ISensorsCallback {
     using Result = ::android::hardware::sensors::V1_0::Result;
-    Return<void> onDynamicSensorsConnected(
+    using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
+
+    Return<void> onDynamicSensorsConnected_2_1(
             const hidl_vec<SensorInfo> &dynamicSensorsAdded) override {
         return SensorDevice::getInstance().onDynamicSensorsConnected(dynamicSensorsAdded);
     }
 
+    Return<void> onDynamicSensorsConnected(
+            const hidl_vec<V1_0::SensorInfo> &dynamicSensorsAdded) override {
+        return SensorDevice::getInstance().onDynamicSensorsConnected(
+                convertToNewSensorInfos(dynamicSensorsAdded));
+    }
+
     Return<void> onDynamicSensorsDisconnected(
             const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) override {
         return SensorDevice::getInstance().onDynamicSensorsDisconnected(
@@ -123,7 +142,26 @@
                 Info model;
                 for (size_t i=0 ; i < count; i++) {
                     sensor_t sensor;
-                    convertToSensor(list[i], &sensor);
+                    convertToSensor(convertToOldSensorInfo(list[i]), &sensor);
+
+                    if (sensor.type < static_cast<int>(SensorType::DEVICE_PRIVATE_BASE)) {
+                        if(sensor.resolution == 0) {
+                            // Don't crash here or the device will go into a crashloop.
+                            ALOGW("%s must have a non-zero resolution", sensor.name);
+                            // For simple algos, map their resolution to 1 if it's not specified
+                            sensor.resolution =
+                                    SensorDeviceUtils::defaultResolutionForType(sensor.type);
+                        }
+
+                        double promotedResolution = sensor.resolution;
+                        double promotedMaxRange = sensor.maxRange;
+                        if (fmod(promotedMaxRange, promotedResolution) != 0) {
+                            ALOGW("%s's max range %f is not a multiple of the resolution %f",
+                                    sensor.name, sensor.maxRange, sensor.resolution);
+                            SensorDeviceUtils::quantizeValue(&sensor.maxRange, promotedResolution);
+                        }
+                    }
+
                     // Sanity check and clamp power if it is 0 (or close)
                     if (sensor.power < minPowerMa) {
                         ALOGI("Reported power %f not deemed sane, clamping to %f",
@@ -134,7 +172,12 @@
 
                     mActivationCount.add(list[i].sensorHandle, model);
 
-                    checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */));
+                    // Only disable all sensors on HAL 1.0 since HAL 2.0
+                    // handles this in its initialize method
+                    if (!mSensors->supportsMessageQueues()) {
+                        checkReturn(mSensors->activate(list[i].sensorHandle,
+                                    0 /* enabled */));
+                    }
                 }
             }));
 }
@@ -152,7 +195,11 @@
 }
 
 bool SensorDevice::connectHidlService() {
-    HalConnectionStatus status = connectHidlServiceV2_0();
+    HalConnectionStatus status = connectHidlServiceV2_1();
+    if (status == HalConnectionStatus::DOES_NOT_EXIST) {
+        status = connectHidlServiceV2_0();
+    }
+
     if (status == HalConnectionStatus::DOES_NOT_EXIST) {
         status = connectHidlServiceV1_0();
     }
@@ -172,7 +219,7 @@
             break;
         }
 
-        mSensors = new SensorServiceUtil::SensorsWrapperV1_0(sensors);
+        mSensors = new ISensorsWrapperV1_0(sensors);
         mRestartWaiter->reset();
         // Poke ISensor service. If it has lingering connection from previous generation of
         // system server, it will kill itself. There is no intention to handle the poll result,
@@ -200,40 +247,55 @@
     if (sensors == nullptr) {
         connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
     } else {
-        mSensors = new SensorServiceUtil::SensorsWrapperV2_0(sensors);
+        mSensors = new ISensorsWrapperV2_0(sensors);
+        connectionStatus = initializeHidlServiceV2_X();
+    }
 
-        mEventQueue = std::make_unique<EventMessageQueue>(
-                SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT,
-                true /* configureEventFlagWord */);
+    return connectionStatus;
+}
 
-        mWakeLockQueue = std::make_unique<WakeLockQueue>(
-                SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT,
-                true /* configureEventFlagWord */);
+SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_1() {
+    HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
+    sp<V2_1::ISensors> sensors = V2_1::ISensors::getService();
 
-        hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
-        hardware::EventFlag::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag);
+    if (sensors == nullptr) {
+        connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
+    } else {
+        mSensors = new ISensorsWrapperV2_1(sensors);
+        connectionStatus = initializeHidlServiceV2_X();
+    }
 
-        hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
-        hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(),
-                                             &mWakeLockQueueFlag);
+    return connectionStatus;
+}
 
-        CHECK(mSensors != nullptr && mEventQueue != nullptr &&
-                mWakeLockQueue != nullptr && mEventQueueFlag != nullptr &&
-                mWakeLockQueueFlag != nullptr);
+SensorDevice::HalConnectionStatus SensorDevice::initializeHidlServiceV2_X() {
+    HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
 
-        status_t status = checkReturnAndGetStatus(mSensors->initialize(
-                *mEventQueue->getDesc(),
-                *mWakeLockQueue->getDesc(),
-                new SensorsCallback()));
+    mWakeLockQueue = std::make_unique<WakeLockQueue>(
+            SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT,
+            true /* configureEventFlagWord */);
 
-        if (status != NO_ERROR) {
-            connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
-            ALOGE("Failed to initialize Sensors HAL (%s)", strerror(-status));
-        } else {
-            connectionStatus = HalConnectionStatus::CONNECTED;
-            mSensorsHalDeathReceiver = new SensorsHalDeathReceivier();
-            sensors->linkToDeath(mSensorsHalDeathReceiver, 0 /* cookie */);
-        }
+    hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
+    hardware::EventFlag::createEventFlag(mSensors->getEventQueue()->getEventFlagWord(), &mEventQueueFlag);
+
+    hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
+    hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(),
+                                            &mWakeLockQueueFlag);
+
+    CHECK(mSensors != nullptr && mWakeLockQueue != nullptr &&
+            mEventQueueFlag != nullptr && mWakeLockQueueFlag != nullptr);
+
+    status_t status = checkReturnAndGetStatus(mSensors->initialize(
+            *mWakeLockQueue->getDesc(),
+            new SensorsCallback()));
+
+    if (status != NO_ERROR) {
+        connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
+        ALOGE("Failed to initialize Sensors HAL (%s)", strerror(-status));
+    } else {
+        connectionStatus = HalConnectionStatus::CONNECTED;
+        mSensorsHalDeathReceiver = new SensorsHalDeathReceivier();
+        mSensors->linkToDeath(mSensorsHalDeathReceiver, 0 /* cookie */);
     }
 
     return connectionStatus;
@@ -360,8 +422,8 @@
     if (mSensors == nullptr) return "HAL not initialized\n";
 
     String8 result;
-    result.appendFormat("Total %zu h/w sensors, %zu running:\n",
-                        mSensorList.size(), mActivationCount.size());
+    result.appendFormat("Total %zu h/w sensors, %zu running %zu disabled clients:\n",
+                        mSensorList.size(), mActivationCount.size(), mDisabledClients.size());
 
     Mutex::Autolock _l(mLock);
     for (const auto & s : mSensorList) {
@@ -374,16 +436,18 @@
         result.append("sampling_period(ms) = {");
         for (size_t j = 0; j < info.batchParams.size(); j++) {
             const BatchParams& params = info.batchParams[j];
-            result.appendFormat("%.1f%s", params.mTSample / 1e6f,
-                j < info.batchParams.size() - 1 ? ", " : "");
+            result.appendFormat("%.1f%s%s", params.mTSample / 1e6f,
+                isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" : "",
+                (j < info.batchParams.size() - 1) ? ", " : "");
         }
         result.appendFormat("}, selected = %.2f ms; ", info.bestBatchParams.mTSample / 1e6f);
 
         result.append("batching_period(ms) = {");
         for (size_t j = 0; j < info.batchParams.size(); j++) {
             const BatchParams& params = info.batchParams[j];
-            result.appendFormat("%.1f%s", params.mTBatch / 1e6f,
-                    j < info.batchParams.size() - 1 ? ", " : "");
+            result.appendFormat("%.1f%s%s", params.mTBatch / 1e6f,
+                    isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" : "",
+                    (j < info.batchParams.size() - 1) ? ", " : "");
         }
         result.appendFormat("}, selected = %.2f ms\n", info.bestBatchParams.mTBatch / 1e6f);
     }
@@ -391,6 +455,43 @@
     return result.string();
 }
 
+/**
+ * Dump debugging information as android.service.SensorDeviceProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorDevice::dump(ProtoOutputStream* proto) const {
+    using namespace service::SensorDeviceProto;
+    if (mSensors == nullptr) {
+        proto->write(INITIALIZED , false);
+        return;
+    }
+    proto->write(INITIALIZED , true);
+    proto->write(TOTAL_SENSORS , int(mSensorList.size()));
+    proto->write(ACTIVE_SENSORS , int(mActivationCount.size()));
+
+    Mutex::Autolock _l(mLock);
+    for (const auto & s : mSensorList) {
+        int32_t handle = s.handle;
+        const Info& info = mActivationCount.valueFor(handle);
+        if (info.numActiveClients() == 0) continue;
+
+        uint64_t token = proto->start(SENSORS);
+        proto->write(SensorProto::HANDLE , handle);
+        proto->write(SensorProto::ACTIVE_COUNT , int(info.batchParams.size()));
+        for (size_t j = 0; j < info.batchParams.size(); j++) {
+            const BatchParams& params = info.batchParams[j];
+            proto->write(SensorProto::SAMPLING_PERIOD_MS , params.mTSample / 1e6f);
+            proto->write(SensorProto::BATCHING_PERIOD_MS , params.mTBatch / 1e6f);
+        }
+        proto->write(SensorProto::SAMPLING_PERIOD_SELECTED , info.bestBatchParams.mTSample / 1e6f);
+        proto->write(SensorProto::BATCHING_PERIOD_SELECTED , info.bestBatchParams.mTBatch / 1e6f);
+        proto->end(token);
+    }
+}
+
 ssize_t SensorDevice::getSensorList(sensor_t const** list) {
     *list = &mSensorList[0];
 
@@ -428,7 +529,8 @@
                     const auto &events,
                     const auto &dynamicSensorsAdded) {
                     if (result == Result::OK) {
-                        convertToSensorEvents(events, dynamicSensorsAdded, buffer);
+                        convertToSensorEventsAndQuantize(convertToNewEvents(events),
+                                convertToNewSensorInfos(dynamicSensorsAdded), buffer);
                         err = (ssize_t)events.size();
                     } else {
                         err = statusFromResult(result);
@@ -462,7 +564,7 @@
 
 ssize_t SensorDevice::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead) {
     ssize_t eventsRead = 0;
-    size_t availableEvents = mEventQueue->availableToRead();
+    size_t availableEvents = mSensors->getEventQueue()->availableToRead();
 
     if (availableEvents == 0) {
         uint32_t eventFlagState = 0;
@@ -473,7 +575,7 @@
         // additional latency in delivering events to applications.
         mEventQueueFlag->wait(asBaseType(EventQueueFlagBits::READ_AND_PROCESS) |
                               asBaseType(INTERNAL_WAKE), &eventFlagState);
-        availableEvents = mEventQueue->availableToRead();
+        availableEvents = mSensors->getEventQueue()->availableToRead();
 
         if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) {
             ALOGD("Event FMQ internal wake, returning from poll with no events");
@@ -483,13 +585,15 @@
 
     size_t eventsToRead = std::min({availableEvents, maxNumEventsToRead, mEventBuffer.size()});
     if (eventsToRead > 0) {
-        if (mEventQueue->read(mEventBuffer.data(), eventsToRead)) {
+        if (mSensors->getEventQueue()->read(mEventBuffer.data(), eventsToRead)) {
             // Notify the Sensors HAL that sensor events have been read. This is required to support
             // the use of writeBlocking by the Sensors HAL.
             mEventQueueFlag->wake(asBaseType(EventQueueFlagBits::EVENTS_READ));
 
             for (size_t i = 0; i < eventsToRead; i++) {
                 convertToSensorEvent(mEventBuffer[i], &buffer[i]);
+                android::SensorDeviceUtils::quantizeSensorEventValues(&buffer[i],
+                        getResolutionForSensor(buffer[i].sensor));
             }
             eventsRead = eventsToRead;
         } else {
@@ -512,7 +616,7 @@
         CHECK(it == mConnectedDynamicSensors.end());
 
         sensor_t *sensor = new sensor_t();
-        convertToSensor(info, sensor);
+        convertToSensor(convertToOldSensorInfo(info), sensor);
 
         mConnectedDynamicSensors.insert(
                 std::make_pair(sensor->handle, sensor));
@@ -560,7 +664,7 @@
 }
 
 status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) {
-    bool actuateHardware = false;
+    bool activateHardware = false;
 
     status_t err(NO_ERROR);
 
@@ -586,7 +690,7 @@
 
         if (info.batchParams.indexOfKey(ident) >= 0) {
             if (info.numActiveClients() > 0 && !info.isActive) {
-                actuateHardware = true;
+                activateHardware = true;
             }
         } else {
             // Log error. Every activate call should be preceded by a batch() call.
@@ -606,7 +710,7 @@
         if (info.removeBatchParamsForIdent(ident) >= 0) {
             if (info.numActiveClients() == 0) {
                 // This is the last connection, we need to de-activate the underlying h/w sensor.
-                actuateHardware = true;
+                activateHardware = true;
             } else {
                 // Call batch for this sensor with the previously calculated best effort
                 // batch_rate and timeout. One of the apps has unregistered for sensor
@@ -626,12 +730,8 @@
         }
     }
 
-    if (actuateHardware) {
-        ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w activate handle=%d enabled=%d", handle,
-                 enabled);
-        err = checkReturnAndGetStatus(mSensors->activate(handle, enabled));
-        ALOGE_IF(err, "Error %s sensor %d (%s)", enabled ? "activating" : "disabling", handle,
-                 strerror(-err));
+    if (activateHardware) {
+        err = doActivateHardwareLocked(handle, enabled);
 
         if (err != NO_ERROR && enabled) {
             // Failure when enabling the sensor. Clean up on failure.
@@ -647,6 +747,15 @@
     return err;
 }
 
+status_t SensorDevice::doActivateHardwareLocked(int handle, bool enabled) {
+    ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w activate handle=%d enabled=%d", handle,
+             enabled);
+    status_t err = checkReturnAndGetStatus(mSensors->activate(handle, enabled));
+    ALOGE_IF(err, "Error %s sensor %d (%s)", enabled ? "activating" : "disabling", handle,
+             strerror(-err));
+    return err;
+}
+
 status_t SensorDevice::batch(
         void* ident,
         int handle,
@@ -687,6 +796,18 @@
         info.setBatchParamsForIdent(ident, flags, samplingPeriodNs, maxBatchReportLatencyNs);
     }
 
+    status_t err =  updateBatchParamsLocked(handle, info);
+    if (err != NO_ERROR) {
+        ALOGE("sensor batch failed %p 0x%08x %" PRId64 " %" PRId64 " err=%s",
+              mSensors.get(), handle, info.bestBatchParams.mTSample,
+              info.bestBatchParams.mTBatch, strerror(-err));
+        info.removeBatchParamsForIdent(ident);
+    }
+
+    return err;
+}
+
+status_t SensorDevice::updateBatchParamsLocked(int handle, Info &info) {
     BatchParams prevBestBatchParams = info.bestBatchParams;
     // Find the minimum of all timeouts and batch_rates for this sensor.
     info.selectBatchParams();
@@ -699,18 +820,13 @@
 
     status_t err(NO_ERROR);
     // If the min period or min timeout has changed since the last batch call, call batch.
-    if (prevBestBatchParams != info.bestBatchParams) {
+    if (prevBestBatchParams != info.bestBatchParams && info.numActiveClients() > 0) {
         ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH 0x%08x %" PRId64 " %" PRId64, handle,
                  info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
         err = checkReturnAndGetStatus(mSensors->batch(
                 handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch));
-        if (err != NO_ERROR) {
-            ALOGE("sensor batch failed %p 0x%08x %" PRId64 " %" PRId64 " err=%s",
-                  mSensors.get(), handle, info.bestBatchParams.mTSample,
-                  info.bestBatchParams.mTBatch, strerror(-err));
-            info.removeBatchParamsForIdent(ident);
-        }
     }
+
     return err;
 }
 
@@ -730,13 +846,60 @@
     return checkReturnAndGetStatus(mSensors->flush(handle));
 }
 
-bool SensorDevice::isClientDisabled(void* ident) {
+bool SensorDevice::isClientDisabled(void* ident) const {
     Mutex::Autolock _l(mLock);
     return isClientDisabledLocked(ident);
 }
 
-bool SensorDevice::isClientDisabledLocked(void* ident) {
-    return mDisabledClients.indexOf(ident) >= 0;
+bool SensorDevice::isClientDisabledLocked(void* ident) const {
+    return mDisabledClients.count(ident) > 0;
+}
+
+std::vector<void *> SensorDevice::getDisabledClientsLocked() const {
+    std::vector<void *> vec;
+    for (const auto& it : mDisabledClients) {
+        vec.push_back(it.first);
+    }
+
+    return vec;
+}
+
+void SensorDevice::addDisabledReasonForIdentLocked(void* ident, DisabledReason reason) {
+    mDisabledClients[ident] |= 1 << reason;
+}
+
+void SensorDevice::removeDisabledReasonForIdentLocked(void* ident, DisabledReason reason) {
+    if (isClientDisabledLocked(ident)) {
+        mDisabledClients[ident] &= ~(1 << reason);
+        if (mDisabledClients[ident] == 0) {
+            mDisabledClients.erase(ident);
+        }
+    }
+}
+
+void SensorDevice::setUidStateForConnection(void* ident, SensorService::UidState state) {
+    Mutex::Autolock _l(mLock);
+    if (state == SensorService::UID_STATE_ACTIVE) {
+        removeDisabledReasonForIdentLocked(ident, DisabledReason::DISABLED_REASON_UID_IDLE);
+    } else {
+        addDisabledReasonForIdentLocked(ident, DisabledReason::DISABLED_REASON_UID_IDLE);
+    }
+
+    for (size_t i = 0; i< mActivationCount.size(); ++i) {
+        int handle = mActivationCount.keyAt(i);
+        Info& info = mActivationCount.editValueAt(i);
+
+        if (info.hasBatchParamsForIdent(ident)) {
+            updateBatchParamsLocked(handle, info);
+            bool disable = info.numActiveClients() == 0 && info.isActive;
+            bool enable = info.numActiveClients() > 0 && !info.isActive;
+
+            if ((enable || disable) &&
+                doActivateHardwareLocked(handle, enable) == NO_ERROR) {
+                info.isActive = enable;
+            }
+        }
+    }
 }
 
 bool SensorDevice::isSensorActive(int handle) const {
@@ -751,8 +914,12 @@
 void SensorDevice::enableAllSensors() {
     if (mSensors == nullptr) return;
     Mutex::Autolock _l(mLock);
-    mDisabledClients.clear();
-    ALOGI("cleared mDisabledClients");
+
+    for (void *client : getDisabledClientsLocked()) {
+        removeDisabledReasonForIdentLocked(
+            client, DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
+    }
+
     for (size_t i = 0; i< mActivationCount.size(); ++i) {
         Info& info = mActivationCount.editValueAt(i);
         if (info.batchParams.isEmpty()) continue;
@@ -792,7 +959,8 @@
            // Add all the connections that were registered for this sensor to the disabled
            // clients list.
            for (size_t j = 0; j < info.batchParams.size(); ++j) {
-               mDisabledClients.add(info.batchParams.keyAt(j));
+               addDisabledReasonForIdentLocked(
+                   info.batchParams.keyAt(j), DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
                ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
            }
 
@@ -813,7 +981,7 @@
             injected_sensor_event->data[5]);
 
     Event ev;
-    convertFromSensorEvent(*injected_sensor_event, &ev);
+    V2_1::implementation::convertFromSensorEvent(*injected_sensor_event, &ev);
 
     return checkReturnAndGetStatus(mSensors->injectSensorData(ev));
 }
@@ -967,7 +1135,7 @@
 
 void SensorDevice::notifyConnectionDestroyed(void* ident) {
     Mutex::Autolock _l(mLock);
-    mDisabledClients.remove(ident);
+    mDisabledClients.erase(ident);
 }
 
 bool SensorDevice::isDirectReportSupported() const {
@@ -976,10 +1144,9 @@
 
 void SensorDevice::convertToSensorEvent(
         const Event &src, sensors_event_t *dst) {
-    ::android::hardware::sensors::V1_0::implementation::convertToSensorEvent(
-            src, dst);
+    V2_1::implementation::convertToSensorEvent(src, dst);
 
-    if (src.sensorType == SensorType::DYNAMIC_SENSOR_META) {
+    if (src.sensorType == V2_1::SensorType::DYNAMIC_SENSOR_META) {
         const DynamicSensorInfo &dyn = src.u.dynamic;
 
         dst->dynamic_sensor_meta.connected = dyn.connected;
@@ -997,7 +1164,7 @@
     }
 }
 
-void SensorDevice::convertToSensorEvents(
+void SensorDevice::convertToSensorEventsAndQuantize(
         const hidl_vec<Event> &src,
         const hidl_vec<SensorInfo> &dynamicSensorsAdded,
         sensors_event_t *dst) {
@@ -1007,10 +1174,27 @@
     }
 
     for (size_t i = 0; i < src.size(); ++i) {
-        convertToSensorEvent(src[i], &dst[i]);
+        V2_1::implementation::convertToSensorEvent(src[i], &dst[i]);
+        android::SensorDeviceUtils::quantizeSensorEventValues(&dst[i],
+                getResolutionForSensor(dst[i].sensor));
     }
 }
 
+float SensorDevice::getResolutionForSensor(int sensorHandle) {
+    for (size_t i = 0; i < mSensorList.size(); i++) {
+      if (sensorHandle == mSensorList[i].handle) {
+        return mSensorList[i].resolution;
+      }
+    }
+
+    auto it = mConnectedDynamicSensors.find(sensorHandle);
+    if (it != mConnectedDynamicSensors.end()) {
+      return it->second->resolution;
+    }
+
+    return 0;
+}
+
 void SensorDevice::handleHidlDeath(const std::string & detail) {
     if (!mSensors->supportsMessageQueues()) {
         // restart is the only option at present.
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index d2c6994..5e7d3da 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -18,8 +18,9 @@
 #define ANDROID_SENSOR_DEVICE_H
 
 #include "SensorDeviceUtils.h"
+#include "SensorService.h"
 #include "SensorServiceUtils.h"
-#include "SensorsWrapper.h"
+#include "ISensorsWrapper.h"
 
 #include <fmq/MessageQueue.h>
 #include <sensor/SensorEventQueue.h>
@@ -112,10 +113,12 @@
 
     using Result = ::android::hardware::sensors::V1_0::Result;
     hardware::Return<void> onDynamicSensorsConnected(
-            const hardware::hidl_vec<hardware::sensors::V1_0::SensorInfo> &dynamicSensorsAdded);
+            const hardware::hidl_vec<hardware::sensors::V2_1::SensorInfo> &dynamicSensorsAdded);
     hardware::Return<void> onDynamicSensorsDisconnected(
             const hardware::hidl_vec<int32_t> &dynamicSensorHandlesRemoved);
 
+    void setUidStateForConnection(void* ident, SensorService::UidState state);
+
     bool isReconnecting() const {
         return mReconnecting;
     }
@@ -123,11 +126,12 @@
     bool isSensorActive(int handle) const;
 
     // Dumpable
-    virtual std::string dump() const;
+    virtual std::string dump() const override;
+    virtual void dump(util::ProtoOutputStream* proto) const override;
 private:
     friend class Singleton<SensorDevice>;
 
-    sp<SensorServiceUtil::ISensorsWrapper> mSensors;
+    sp<::android::hardware::sensors::V2_1::implementation::ISensorsWrapperBase> mSensors;
     Vector<sensor_t> mSensorList;
     std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors;
 
@@ -178,6 +182,13 @@
         // the removed ident. If index >=0, ident is present and successfully removed.
         ssize_t removeBatchParamsForIdent(void* ident);
 
+        bool hasBatchParamsForIdent(void* ident) const {
+            return batchParams.indexOfKey(ident) >= 0;
+        }
+
+        /**
+         * @return The number of active clients of this sensor.
+         */
         int numActiveClients() const;
     };
     DefaultKeyedVector<int, Info> mActivationCount;
@@ -186,8 +197,26 @@
     SensorServiceUtil::RingBuffer<HidlTransportErrorLog> mHidlTransportErrors;
     int mTotalHidlTransportErrors;
 
-    // Use this vector to determine which client is activated or deactivated.
-    SortedVector<void *> mDisabledClients;
+    /**
+     * Enums describing the reason why a client was disabled.
+     */
+    enum DisabledReason : uint8_t {
+        // UID becomes idle (e.g. app goes to background).
+        DISABLED_REASON_UID_IDLE = 0,
+
+        // Sensors are restricted for all clients.
+        DISABLED_REASON_SERVICE_RESTRICTED,
+        DISABLED_REASON_MAX,
+    };
+
+    static_assert(DisabledReason::DISABLED_REASON_MAX < sizeof(uint8_t) * CHAR_BIT);
+
+    // Use this map to determine which client is activated or deactivated.
+    std::unordered_map<void *, uint8_t> mDisabledClients;
+
+    void addDisabledReasonForIdentLocked(void* ident, DisabledReason reason);
+    void removeDisabledReasonForIdentLocked(void* ident, DisabledReason reason);
+
     SensorDevice();
     bool connectHidlService();
     void initializeSensorList();
@@ -204,6 +233,8 @@
     };
     HalConnectionStatus connectHidlServiceV1_0();
     HalConnectionStatus connectHidlServiceV2_0();
+    HalConnectionStatus connectHidlServiceV2_1();
+    HalConnectionStatus initializeHidlServiceV2_X();
 
     ssize_t pollHal(sensors_event_t* buffer, size_t count);
     ssize_t pollFmq(sensors_event_t* buffer, size_t count);
@@ -211,6 +242,9 @@
     status_t batchLocked(void* ident, int handle, int flags, int64_t samplingPeriodNs,
                          int64_t maxBatchReportLatencyNs);
 
+    status_t updateBatchParamsLocked(int handle, Info& info);
+    status_t doActivateHardwareLocked(int handle, bool enable);
+
     void handleHidlDeath(const std::string &detail);
     template<typename T>
     void checkReturn(const Return<T>& ret) {
@@ -222,24 +256,27 @@
     //TODO(b/67425500): remove waiter after bug is resolved.
     sp<SensorDeviceUtils::HidlServiceRegistrationWaiter> mRestartWaiter;
 
-    bool isClientDisabled(void* ident);
-    bool isClientDisabledLocked(void* ident);
+    bool isClientDisabled(void* ident) const;
+    bool isClientDisabledLocked(void* ident) const;
+    std::vector<void *> getDisabledClientsLocked() const;
 
-    using Event = hardware::sensors::V1_0::Event;
-    using SensorInfo = hardware::sensors::V1_0::SensorInfo;
+    bool clientHasNoAccessLocked(void* ident) const;
+
+    using Event = hardware::sensors::V2_1::Event;
+    using SensorInfo = hardware::sensors::V2_1::SensorInfo;
 
     void convertToSensorEvent(const Event &src, sensors_event_t *dst);
 
-    void convertToSensorEvents(
+    void convertToSensorEventsAndQuantize(
             const hardware::hidl_vec<Event> &src,
             const hardware::hidl_vec<SensorInfo> &dynamicSensorsAdded,
             sensors_event_t *dst);
 
+    float getResolutionForSensor(int sensorHandle);
+
     bool mIsDirectReportSupported;
 
-    typedef hardware::MessageQueue<Event, hardware::kSynchronizedReadWrite> EventMessageQueue;
     typedef hardware::MessageQueue<uint32_t, hardware::kSynchronizedReadWrite> WakeLockQueue;
-    std::unique_ptr<EventMessageQueue> mEventQueue;
     std::unique_ptr<WakeLockQueue> mWakeLockQueue;
 
     hardware::EventFlag* mEventQueueFlag;
diff --git a/services/sensorservice/SensorDeviceUtils.cpp b/services/sensorservice/SensorDeviceUtils.cpp
index dbafffe..52213cf 100644
--- a/services/sensorservice/SensorDeviceUtils.cpp
+++ b/services/sensorservice/SensorDeviceUtils.cpp
@@ -17,17 +17,88 @@
 #include "SensorDeviceUtils.h"
 
 #include <android/hardware/sensors/1.0/ISensors.h>
+#include <android/hardware/sensors/2.1/ISensors.h>
 #include <utils/Log.h>
 
 #include <chrono>
 #include <thread>
 
 using ::android::hardware::Void;
+using SensorTypeV2_1 = android::hardware::sensors::V2_1::SensorType;
 using namespace android::hardware::sensors::V1_0;
 
 namespace android {
 namespace SensorDeviceUtils {
 
+void quantizeSensorEventValues(sensors_event_t *event, float resolution) {
+    LOG_FATAL_IF(resolution == 0, "Resolution must be specified for all sensors!");
+    if (resolution == 0) {
+        return;
+    }
+
+    size_t axes = 0;
+    switch ((SensorTypeV2_1)event->type) {
+        case SensorTypeV2_1::ACCELEROMETER:
+        case SensorTypeV2_1::MAGNETIC_FIELD:
+        case SensorTypeV2_1::GYROSCOPE:
+        case SensorTypeV2_1::MAGNETIC_FIELD_UNCALIBRATED:
+        case SensorTypeV2_1::GYROSCOPE_UNCALIBRATED:
+        case SensorTypeV2_1::ACCELEROMETER_UNCALIBRATED:
+            axes = 3;
+            break;
+        case SensorTypeV2_1::DEVICE_ORIENTATION:
+        case SensorTypeV2_1::LIGHT:
+        case SensorTypeV2_1::PRESSURE:
+        case SensorTypeV2_1::TEMPERATURE:
+        case SensorTypeV2_1::PROXIMITY:
+        case SensorTypeV2_1::RELATIVE_HUMIDITY:
+        case SensorTypeV2_1::AMBIENT_TEMPERATURE:
+        case SensorTypeV2_1::SIGNIFICANT_MOTION:
+        case SensorTypeV2_1::STEP_DETECTOR:
+        case SensorTypeV2_1::TILT_DETECTOR:
+        case SensorTypeV2_1::WAKE_GESTURE:
+        case SensorTypeV2_1::GLANCE_GESTURE:
+        case SensorTypeV2_1::PICK_UP_GESTURE:
+        case SensorTypeV2_1::WRIST_TILT_GESTURE:
+        case SensorTypeV2_1::STATIONARY_DETECT:
+        case SensorTypeV2_1::MOTION_DETECT:
+        case SensorTypeV2_1::HEART_BEAT:
+        case SensorTypeV2_1::LOW_LATENCY_OFFBODY_DETECT:
+        case SensorTypeV2_1::HINGE_ANGLE:
+            axes = 1;
+            break;
+        default:
+            // No other sensors have data that needs to be quantized.
+            break;
+    }
+
+    // sensor_event_t is a union so we're able to perform the same quanitization action for most
+    // sensors by only knowing the number of axes their output data has.
+    for (size_t i = 0; i < axes; i++) {
+        quantizeValue(&event->data[i], resolution);
+    }
+}
+
+float defaultResolutionForType(int type) {
+    switch ((SensorTypeV2_1)type) {
+        case SensorTypeV2_1::SIGNIFICANT_MOTION:
+        case SensorTypeV2_1::STEP_DETECTOR:
+        case SensorTypeV2_1::STEP_COUNTER:
+        case SensorTypeV2_1::TILT_DETECTOR:
+        case SensorTypeV2_1::WAKE_GESTURE:
+        case SensorTypeV2_1::GLANCE_GESTURE:
+        case SensorTypeV2_1::PICK_UP_GESTURE:
+        case SensorTypeV2_1::WRIST_TILT_GESTURE:
+        case SensorTypeV2_1::STATIONARY_DETECT:
+        case SensorTypeV2_1::MOTION_DETECT:
+            return 1.0f;
+        default:
+            // fall through and return 0 for all other types
+            break;
+    }
+    return 0.0f;
+}
+
 HidlServiceRegistrationWaiter::HidlServiceRegistrationWaiter() {
 }
 
diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h
index e2eb606..c232f0b 100644
--- a/services/sensorservice/SensorDeviceUtils.h
+++ b/services/sensorservice/SensorDeviceUtils.h
@@ -18,7 +18,9 @@
 #define ANDROID_SENSOR_DEVICE_UTIL
 
 #include <android/hidl/manager/1.0/IServiceNotification.h>
+#include <hardware/sensors.h>
 
+#include <cmath>
 #include <condition_variable>
 #include <thread>
 
@@ -29,6 +31,21 @@
 namespace android {
 namespace SensorDeviceUtils {
 
+// Quantizes a single value using a sensor's resolution.
+inline void quantizeValue(float *value, double resolution) {
+    // Increase the value of the sensor's nominal resolution to ensure that
+    // sensor accuracy improvements, like runtime calibration, are not masked
+    // during requantization.
+    double incRes = 0.125 * resolution;
+    *value = round(static_cast<double>(*value) / incRes) * incRes;
+}
+
+// Ensures a sensor event doesn't provide values finer grained than its sensor resolution allows.
+void quantizeSensorEventValues(sensors_event_t *event, float resolution);
+
+// Provides a default resolution for simple sensor types if one wasn't provided by the HAL.
+float defaultResolutionForType(int type);
+
 class HidlServiceRegistrationWaiter : public IServiceNotification {
 public:
 
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index cd0ea5d..e4c33da 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -16,12 +16,16 @@
 
 #include "SensorDevice.h"
 #include "SensorDirectConnection.h"
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <hardware/sensors.h>
 
 #define UNUSED(x) (void)(x)
 
 namespace android {
 
+using util::ProtoOutputStream;
+
 SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service,
         uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle,
         const String16& opPackageName)
@@ -64,10 +68,43 @@
     }
 }
 
+/**
+ * Dump debugging information as android.service.SensorDirectConnectionProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorService::SensorDirectConnection::dump(ProtoOutputStream* proto) const {
+    using namespace service::SensorDirectConnectionProto;
+    Mutex::Autolock _l(mConnectionLock);
+    proto->write(PACKAGE_NAME, std::string(String8(mOpPackageName).string()));
+    proto->write(HAL_CHANNEL_HANDLE, getHalChannelHandle());
+    proto->write(NUM_SENSOR_ACTIVATED, int(mActivated.size()));
+    for (auto &i : mActivated) {
+        uint64_t token = proto->start(SENSORS);
+        proto->write(SensorProto::SENSOR, i.first);
+        proto->write(SensorProto::RATE, i.second);
+        proto->end(token);
+    }
+}
+
 sp<BitTube> SensorService::SensorDirectConnection::getSensorChannel() const {
     return nullptr;
 }
 
+void SensorService::SensorDirectConnection::onSensorAccessChanged(bool hasAccess) {
+    if (!hasAccess) {
+        stopAll(true /* backupRecord */);
+    } else {
+        recoverAll();
+    }
+}
+
+bool SensorService::SensorDirectConnection::hasSensorAccess() const {
+    return mService->hasSensorAccess(mUid, mOpPackageName);
+}
+
 status_t SensorService::SensorDirectConnection::enableDisable(
         int handle, bool enabled, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs,
         int reservedFlags) {
@@ -100,7 +137,7 @@
         return NO_ERROR;
     }
 
-    if (!mService->isOperationPermitted(mOpPackageName)) {
+    if (!hasSensorAccess()) {
         return PERMISSION_DENIED;
     }
 
@@ -144,12 +181,15 @@
 }
 
 void SensorService::SensorDirectConnection::stopAll(bool backupRecord) {
+    Mutex::Autolock _l(mConnectionLock);
+    stopAllLocked(backupRecord);
+}
 
+void SensorService::SensorDirectConnection::stopAllLocked(bool backupRecord) {
     struct sensors_direct_cfg_t config = {
         .rate_level = SENSOR_DIRECT_RATE_STOP
     };
 
-    Mutex::Autolock _l(mConnectionLock);
     SensorDevice& dev(SensorDevice::getInstance());
     for (auto &i : mActivated) {
         dev.configureDirectChannel(i.first, getHalChannelHandle(), &config);
@@ -162,21 +202,25 @@
 }
 
 void SensorService::SensorDirectConnection::recoverAll() {
-    stopAll(false);
-
     Mutex::Autolock _l(mConnectionLock);
-    SensorDevice& dev(SensorDevice::getInstance());
+    if (!mActivatedBackup.empty()) {
+        stopAllLocked(false);
 
-    // recover list of report from backup
-    mActivated = mActivatedBackup;
-    mActivatedBackup.clear();
+        SensorDevice& dev(SensorDevice::getInstance());
 
-    // re-enable them
-    for (auto &i : mActivated) {
-        struct sensors_direct_cfg_t config = {
-            .rate_level = i.second
-        };
-        dev.configureDirectChannel(i.first, getHalChannelHandle(), &config);
+        // recover list of report from backup
+        ALOG_ASSERT(mActivated.empty(),
+                    "mActivated must be empty if mActivatedBackup was non-empty");
+        mActivated = mActivatedBackup;
+        mActivatedBackup.clear();
+
+        // re-enable them
+        for (auto &i : mActivated) {
+            struct sensors_direct_cfg_t config = {
+                .rate_level = i.second
+            };
+            dev.configureDirectChannel(i.first, getHalChannelHandle(), &config);
+        }
     }
 }
 
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index 5c398a8..4181b65 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -40,18 +40,16 @@
             const sensors_direct_mem_t *mem, int32_t halChannelHandle,
             const String16& opPackageName);
     void dump(String8& result) const;
+    void dump(util::ProtoOutputStream* proto) const;
     uid_t getUid() const { return mUid; }
+    const String16& getOpPackageName() const { return mOpPackageName; }
     int32_t getHalChannelHandle() const;
     bool isEquivalent(const sensors_direct_mem_t *mem) const;
 
-    // stop all active sensor report. if backupRecord is set to false,
-    // those report can be recovered by recoverAll
-    // called by SensorService when enter restricted mode
-    void stopAll(bool backupRecord = false);
-
-    // recover sensor reports previously stopped by stopAll(true)
-    // called by SensorService when return to NORMAL mode.
-    void recoverAll();
+    // Invoked when access to sensors for this connection has changed, e.g. lost or
+    // regained due to changes in the sensor restricted/privacy mode or the
+    // app changed to idle/active status.
+    void onSensorAccessChanged(bool hasAccess);
 
 protected:
     virtual ~SensorDirectConnection();
@@ -65,6 +63,25 @@
     virtual int32_t configureChannel(int handle, int rateLevel);
     virtual void destroy();
 private:
+    bool hasSensorAccess() const;
+
+    // Stops all active sensor direct report requests.
+    //
+    // If backupRecord is true, stopped requests can be recovered
+    // by a subsequent recoverAll() call (e.g. when temporarily stopping
+    // sensors for sensor privacy/restrict mode or when an app becomes
+    // idle).
+    void stopAll(bool backupRecord = false);
+    // Same as stopAll() but with mConnectionLock held.
+    void stopAllLocked(bool backupRecord);
+
+    // Recover sensor requests previously stopped by stopAll(true).
+    // This method can be called when a sensor access resumes (e.g.
+    // sensor privacy/restrict mode lifted or app becomes active).
+    //
+    // If no requests are backed up by stopAll(), this method is no-op.
+    void recoverAll();
+
     const sp<SensorService> mService;
     const uid_t mUid;
     const sensors_direct_mem_t mMem;
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 0e40940..b4b5f98 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -17,6 +17,8 @@
 #include <sys/socket.h>
 #include <utils/threads.h>
 
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <sensor/SensorEventQueue.h>
 
 #include "vec.h"
@@ -29,13 +31,13 @@
 
 SensorService::SensorEventConnection::SensorEventConnection(
         const sp<SensorService>& service, uid_t uid, String8 packageName, bool isDataInjectionMode,
-        const String16& opPackageName, bool hasSensorAccess)
+        const String16& opPackageName)
     : mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false),
       mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(nullptr),
       mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0),
-      mPackageName(packageName), mOpPackageName(opPackageName), mDestroyed(false),
-      mHasSensorAccess(hasSensorAccess) {
+      mPackageName(packageName), mOpPackageName(opPackageName), mDestroyed(false) {
     mChannel = new BitTube(mService->mSocketBufferSize);
+    mTargetSdk = SensorService::getTargetSdkVersion(opPackageName);
 #if DEBUG_CONNECTIONS
     mEventsReceived = mEventsSentFromCache = mEventsSent = 0;
     mTotalAcksNeeded = mTotalAcksReceived = 0;
@@ -89,11 +91,11 @@
     result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | "
             "max cache size %d\n", mPackageName.string(), mWakeLockRefCount, mUid, mCacheSize,
             mMaxCacheSize);
-    for (size_t i = 0; i < mSensorInfo.size(); ++i) {
-        const FlushInfo& flushInfo = mSensorInfo.valueAt(i);
+    for (auto& it : mSensorInfo) {
+        const FlushInfo& flushInfo = it.second;
         result.appendFormat("\t %s 0x%08x | status: %s | pending flush events %d \n",
-                            mService->getSensorName(mSensorInfo.keyAt(i)).string(),
-                            mSensorInfo.keyAt(i),
+                            mService->getSensorName(it.first).string(),
+                            it.first,
                             flushInfo.mFirstFlushPending ? "First flush pending" :
                                                            "active",
                             flushInfo.mPendingFlushEventsToSend);
@@ -110,29 +112,83 @@
 #endif
 }
 
+/**
+ * Dump debugging information as android.service.SensorEventConnectionProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorService::SensorEventConnection::dump(util::ProtoOutputStream* proto) const {
+    using namespace service::SensorEventConnectionProto;
+    Mutex::Autolock _l(mConnectionLock);
+
+    if (!mService->isWhiteListedPackage(getPackageName())) {
+        proto->write(OPERATING_MODE, OP_MODE_RESTRICTED);
+    } else if (mDataInjectionMode) {
+        proto->write(OPERATING_MODE, OP_MODE_DATA_INJECTION);
+    } else {
+        proto->write(OPERATING_MODE, OP_MODE_NORMAL);
+    }
+    proto->write(PACKAGE_NAME, std::string(mPackageName.string()));
+    proto->write(WAKE_LOCK_REF_COUNT, int32_t(mWakeLockRefCount));
+    proto->write(UID, int32_t(mUid));
+    proto->write(CACHE_SIZE, int32_t(mCacheSize));
+    proto->write(MAX_CACHE_SIZE, int32_t(mMaxCacheSize));
+    for (auto& it : mSensorInfo) {
+        const FlushInfo& flushInfo = it.second;
+        const uint64_t token = proto->start(FLUSH_INFOS);
+        proto->write(FlushInfoProto::SENSOR_NAME,
+                std::string(mService->getSensorName(it.first)));
+        proto->write(FlushInfoProto::SENSOR_HANDLE, it.first);
+        proto->write(FlushInfoProto::FIRST_FLUSH_PENDING, flushInfo.mFirstFlushPending);
+        proto->write(FlushInfoProto::PENDING_FLUSH_EVENTS_TO_SEND,
+                flushInfo.mPendingFlushEventsToSend);
+        proto->end(token);
+    }
+#if DEBUG_CONNECTIONS
+    proto->write(EVENTS_RECEIVED, mEventsReceived);
+    proto->write(EVENTS_SENT, mEventsSent);
+    proto->write(EVENTS_CACHE, mEventsSentFromCache);
+    proto->write(EVENTS_DROPPED, mEventsReceived - (mEventsSentFromCache + mEventsSent +
+            mCacheSize));
+    proto->write(TOTAL_ACKS_NEEDED, mTotalAcksNeeded);
+    proto->write(TOTAL_ACKS_RECEIVED, mTotalAcksReceived);
+#endif
+}
+
 bool SensorService::SensorEventConnection::addSensor(int32_t handle) {
     Mutex::Autolock _l(mConnectionLock);
     sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
     if (si == nullptr ||
         !canAccessSensor(si->getSensor(), "Tried adding", mOpPackageName) ||
-        mSensorInfo.indexOfKey(handle) >= 0) {
+        mSensorInfo.count(handle) > 0) {
         return false;
     }
-    mSensorInfo.add(handle, FlushInfo());
+    mSensorInfo[handle] = FlushInfo();
     return true;
 }
 
 bool SensorService::SensorEventConnection::removeSensor(int32_t handle) {
     Mutex::Autolock _l(mConnectionLock);
-    if (mSensorInfo.removeItem(handle) >= 0) {
+    if (mSensorInfo.erase(handle) >= 0) {
         return true;
     }
     return false;
 }
 
+std::vector<int32_t> SensorService::SensorEventConnection::getActiveSensorHandles() const {
+    Mutex::Autolock _l(mConnectionLock);
+    std::vector<int32_t> list;
+    for (auto& it : mSensorInfo) {
+        list.push_back(it.first);
+    }
+    return list;
+}
+
 bool SensorService::SensorEventConnection::hasSensor(int32_t handle) const {
     Mutex::Autolock _l(mConnectionLock);
-    return mSensorInfo.indexOfKey(handle) >= 0;
+    return mSensorInfo.count(handle) > 0;
 }
 
 bool SensorService::SensorEventConnection::hasAnySensor() const {
@@ -142,8 +198,8 @@
 
 bool SensorService::SensorEventConnection::hasOneShotSensors() const {
     Mutex::Autolock _l(mConnectionLock);
-    for (size_t i = 0; i < mSensorInfo.size(); ++i) {
-        const int handle = mSensorInfo.keyAt(i);
+    for (auto &it : mSensorInfo) {
+        const int handle = it.first;
         sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
         if (si != nullptr && si->getSensor().getReportingMode() == AREPORTING_MODE_ONE_SHOT) {
             return true;
@@ -159,9 +215,8 @@
 void SensorService::SensorEventConnection::setFirstFlushPending(int32_t handle,
                                 bool value) {
     Mutex::Autolock _l(mConnectionLock);
-    ssize_t index = mSensorInfo.indexOfKey(handle);
-    if (index >= 0) {
-        FlushInfo& flushInfo = mSensorInfo.editValueAt(index);
+    if (mSensorInfo.count(handle) > 0) {
+        FlushInfo& flushInfo = mSensorInfo[handle];
         flushInfo.mFirstFlushPending = value;
     }
 }
@@ -186,8 +241,8 @@
     int looper_flags = 0;
     if (mCacheSize > 0) looper_flags |= ALOOPER_EVENT_OUTPUT;
     if (mDataInjectionMode) looper_flags |= ALOOPER_EVENT_INPUT;
-    for (size_t i = 0; i < mSensorInfo.size(); ++i) {
-        const int handle = mSensorInfo.keyAt(i);
+    for (auto& it : mSensorInfo) {
+        const int handle = it.first;
         sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
         if (si != nullptr && si->getSensor().isWakeUpSensor()) {
             looper_flags |= ALOOPER_EVENT_INPUT;
@@ -217,12 +272,16 @@
     }
 }
 
-void SensorService::SensorEventConnection::incrementPendingFlushCount(int32_t handle) {
-    Mutex::Autolock _l(mConnectionLock);
-    ssize_t index = mSensorInfo.indexOfKey(handle);
-    if (index >= 0) {
-        FlushInfo& flushInfo = mSensorInfo.editValueAt(index);
-        flushInfo.mPendingFlushEventsToSend++;
+bool SensorService::SensorEventConnection::incrementPendingFlushCountIfHasAccess(int32_t handle) {
+    if (hasSensorAccess()) {
+        Mutex::Autolock _l(mConnectionLock);
+        if (mSensorInfo.count(handle) > 0) {
+            FlushInfo& flushInfo = mSensorInfo[handle];
+            flushInfo.mPendingFlushEventsToSend++;
+        }
+        return true;
+    } else {
+        return false;
     }
 }
 
@@ -249,15 +308,14 @@
                 sensor_handle = buffer[i].meta_data.sensor;
             }
 
-            ssize_t index = mSensorInfo.indexOfKey(sensor_handle);
             // Check if this connection has registered for this sensor. If not continue to the
             // next sensor_event.
-            if (index < 0) {
+            if (mSensorInfo.count(sensor_handle) == 0) {
                 ++i;
                 continue;
             }
 
-            FlushInfo& flushInfo = mSensorInfo.editValueAt(index);
+            FlushInfo& flushInfo = mSensorInfo[sensor_handle];
             // Check if there is a pending flush_complete event for this sensor on this connection.
             if (buffer[i].type == SENSOR_TYPE_META_DATA && flushInfo.mFirstFlushPending == true &&
                     mapFlushEventsToConnections[i] == this) {
@@ -378,21 +436,26 @@
     return size < 0 ? status_t(size) : status_t(NO_ERROR);
 }
 
-void SensorService::SensorEventConnection::setSensorAccess(const bool hasAccess) {
-    Mutex::Autolock _l(mConnectionLock);
-    mHasSensorAccess = hasAccess;
-}
-
 bool SensorService::SensorEventConnection::hasSensorAccess() {
-    return mHasSensorAccess && !mService->mSensorPrivacyPolicy->isSensorPrivacyEnabled();
+    return mService->isUidActive(mUid)
+        && !mService->mSensorPrivacyPolicy->isSensorPrivacyEnabled();
 }
 
 bool SensorService::SensorEventConnection::noteOpIfRequired(const sensors_event_t& event) {
     bool success = true;
     const auto iter = mHandleToAppOp.find(event.sensor);
     if (iter != mHandleToAppOp.end()) {
-        int32_t appOpMode = mService->sAppOpsManager.noteOp((*iter).second, mUid, mOpPackageName);
-        success = (appOpMode == AppOpsManager::MODE_ALLOWED);
+        // Special handling for step count/detect backwards compatibility: if the app's target SDK
+        // is pre-Q, still permit delivering events to the app even if permission isn't granted
+        // (since this permission was only introduced in Q)
+        if ((event.type == SENSOR_TYPE_STEP_COUNTER || event.type == SENSOR_TYPE_STEP_DETECTOR) &&
+                mTargetSdk > 0 && mTargetSdk <= __ANDROID_API_P__) {
+            success = true;
+        } else {
+            int32_t appOpMode = mService->sAppOpsManager.noteOp(iter->second, mUid,
+                                                                mOpPackageName);
+            success = (appOpMode == AppOpsManager::MODE_ALLOWED);
+        }
     }
     return success;
 }
@@ -475,14 +538,14 @@
     flushCompleteEvent.type = SENSOR_TYPE_META_DATA;
     // Loop through all the sensors for this connection and check if there are any pending
     // flush complete events to be sent.
-    for (size_t i = 0; i < mSensorInfo.size(); ++i) {
-        const int handle = mSensorInfo.keyAt(i);
+    for (auto& it : mSensorInfo) {
+        const int handle = it.first;
         sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
         if (si == nullptr) {
             continue;
         }
 
-        FlushInfo& flushInfo = mSensorInfo.editValueAt(i);
+        FlushInfo& flushInfo = it.second;
         while (flushInfo.mPendingFlushEventsToSend > 0) {
             flushCompleteEvent.meta_data.sensor = handle;
             bool wakeUpSensor = si->getSensor().isWakeUpSensor();
@@ -568,14 +631,13 @@
     // separately before the next batch of events.
     for (int j = 0; j < numEventsDropped; ++j) {
         if (scratch[j].type == SENSOR_TYPE_META_DATA) {
-            ssize_t index = mSensorInfo.indexOfKey(scratch[j].meta_data.sensor);
-            if (index < 0) {
+            if (mSensorInfo.count(scratch[j].meta_data.sensor) == 0) {
                 ALOGW("%s: sensor 0x%x is not found in connection",
                       __func__, scratch[j].meta_data.sensor);
                 continue;
             }
 
-            FlushInfo& flushInfo = mSensorInfo.editValueAt(index);
+            FlushInfo& flushInfo = mSensorInfo[scratch[j].meta_data.sensor];
             flushInfo.mPendingFlushEventsToSend++;
             ALOGD_IF(DEBUG_CONNECTIONS, "increment pendingFlushCount %d",
                      flushInfo.mPendingFlushEventsToSend);
@@ -717,8 +779,8 @@
 int SensorService::SensorEventConnection::computeMaxCacheSizeLocked() const {
     size_t fifoWakeUpSensors = 0;
     size_t fifoNonWakeUpSensors = 0;
-    for (size_t i = 0; i < mSensorInfo.size(); ++i) {
-        sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(mSensorInfo.keyAt(i));
+    for (auto& it : mSensorInfo) {
+        sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(it.first);
         if (si == nullptr) {
             continue;
         }
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index fd881cb..8f2d5db 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -23,7 +23,6 @@
 
 #include <utils/Vector.h>
 #include <utils/SortedVector.h>
-#include <utils/KeyedVector.h>
 #include <utils/threads.h>
 #include <utils/AndroidThreads.h>
 #include <utils/RefBase.h>
@@ -50,8 +49,7 @@
 
 public:
     SensorEventConnection(const sp<SensorService>& service, uid_t uid, String8 packageName,
-                          bool isDataInjectionMode, const String16& opPackageName,
-                          bool hasSensorAccess);
+                          bool isDataInjectionMode, const String16& opPackageName);
 
     status_t sendEvents(sensors_event_t const* buffer, size_t count, sensors_event_t* scratch,
                         wp<const SensorEventConnection> const * mapFlushEventsToConnections = nullptr);
@@ -60,16 +58,16 @@
     bool hasOneShotSensors() const;
     bool addSensor(int32_t handle);
     bool removeSensor(int32_t handle);
+    std::vector<int32_t> getActiveSensorHandles() const;
     void setFirstFlushPending(int32_t handle, bool value);
     void dump(String8& result);
+    void dump(util::ProtoOutputStream* proto) const;
     bool needsWakeLock();
     void resetWakeLockRefCount();
     String8 getPackageName() const;
 
     uid_t getUid() const { return mUid; }
 
-    void setSensorAccess(const bool hasAccess);
-
 private:
     virtual ~SensorEventConnection();
     virtual void onFirstRef();
@@ -118,8 +116,9 @@
     // for writing send the data from the cache.
     virtual int handleEvent(int fd, int events, void* data);
 
-    // Increment mPendingFlushEventsToSend for the given sensor handle.
-    void incrementPendingFlushCount(int32_t handle);
+    // Increment mPendingFlushEventsToSend for the given handle if the connection has sensor access.
+    // Returns true if this connection does have sensor access.
+    bool incrementPendingFlushCountIfHasAccess(int32_t handle);
 
     // Add or remove the file descriptor associated with the BitTube to the looper. If mDead is set
     // to true or there are no more sensors for this connection, the file descriptor is removed if
@@ -168,8 +167,8 @@
 
         FlushInfo() : mPendingFlushEventsToSend(0), mFirstFlushPending(false) {}
     };
-    // protected by SensorService::mLock. Key for this vector is the sensor handle.
-    KeyedVector<int, FlushInfo> mSensorInfo;
+    // protected by SensorService::mLock. Key for this map is the sensor handle.
+    std::unordered_map<int32_t, FlushInfo> mSensorInfo;
 
     sensors_event_t *mEventCache;
     int mCacheSize, mMaxCacheSize;
@@ -177,6 +176,7 @@
     int mEventsDropped;
     String8 mPackageName;
     const String16 mOpPackageName;
+    int mTargetSdk;
 #if DEBUG_CONNECTIONS
     int mEventsReceived, mEventsSent, mEventsSentFromCache;
     int mTotalAcksNeeded, mTotalAcksReceived;
@@ -184,7 +184,6 @@
 
     mutable Mutex mDestroyLock;
     bool mDestroyed;
-    bool mHasSensorAccess;
 
     // Store a mapping of sensor handles to required AppOp for a sensor. This map only contains a
     // valid mapping for sensors that require a permission in order to reduce the lookup time.
diff --git a/services/sensorservice/SensorFusion.cpp b/services/sensorservice/SensorFusion.cpp
index 414f673..e27b52b 100644
--- a/services/sensorservice/SensorFusion.cpp
+++ b/services/sensorservice/SensorFusion.cpp
@@ -18,6 +18,9 @@
 #include "SensorFusion.h"
 #include "SensorService.h"
 
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
+
 namespace android {
 // ---------------------------------------------------------------------------
 
@@ -183,7 +186,7 @@
     return mAcc.getMinDelay();
 }
 
-void SensorFusion::dump(String8& result) {
+void SensorFusion::dump(String8& result) const {
     const Fusion& fusion_9axis(mFusions[FUSION_9AXIS]);
     result.appendFormat("9-axis fusion %s (%zd clients), gyro-rate=%7.2fHz, "
             "q=< %g, %g, %g, %g > (%g), "
@@ -235,5 +238,42 @@
             fusion_nogyro.getBias().z);
 }
 
+void SensorFusion::dumpFusion(FUSION_MODE mode, util::ProtoOutputStream* proto) const {
+    using namespace service::SensorFusionProto::FusionProto;
+    const Fusion& fusion(mFusions[mode]);
+    proto->write(ENABLED, mEnabled[mode]);
+    proto->write(NUM_CLIENTS, (int)mClients[mode].size());
+    proto->write(ESTIMATED_GYRO_RATE, mEstimatedGyroRate);
+    proto->write(ATTITUDE_X, fusion.getAttitude().x);
+    proto->write(ATTITUDE_Y, fusion.getAttitude().y);
+    proto->write(ATTITUDE_Z, fusion.getAttitude().z);
+    proto->write(ATTITUDE_W, fusion.getAttitude().w);
+    proto->write(ATTITUDE_LENGTH, length(fusion.getAttitude()));
+    proto->write(BIAS_X, fusion.getBias().x);
+    proto->write(BIAS_Y, fusion.getBias().y);
+    proto->write(BIAS_Z, fusion.getBias().z);
+}
+
+/**
+ * Dump debugging information as android.service.SensorFusionProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorFusion::dump(util::ProtoOutputStream* proto) const {
+    uint64_t token = proto->start(service::SensorFusionProto::FUSION_9AXIS);
+    dumpFusion(FUSION_9AXIS, proto);
+    proto->end(token);
+
+    token = proto->start(service::SensorFusionProto::FUSION_NOMAG);
+    dumpFusion(FUSION_NOMAG, proto);
+    proto->end(token);
+
+    token = proto->start(service::SensorFusionProto::FUSION_NOGYRO);
+    dumpFusion(FUSION_NOGYRO, proto);
+    proto->end(token);
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/sensorservice/SensorFusion.h b/services/sensorservice/SensorFusion.h
index 8c0fbf9..66a7290 100644
--- a/services/sensorservice/SensorFusion.h
+++ b/services/sensorservice/SensorFusion.h
@@ -90,7 +90,9 @@
     float getPowerUsage(int mode=FUSION_9AXIS) const;
     int32_t getMinDelay() const;
 
-    void dump(String8& result);
+    void dump(String8& result) const;
+    void dump(util::ProtoOutputStream* proto) const;
+    void dumpFusion(FUSION_MODE mode, util::ProtoOutputStream* proto) const;
 };
 
 
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index aa306d8..0ce32cc 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -16,6 +16,8 @@
 
 #include "SensorList.h"
 
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <hardware/sensors.h>
 #include <utils/String8.h>
 
@@ -203,6 +205,64 @@
     return std::string(result.string());
 }
 
+/**
+ * Dump debugging information as android.service.SensorListProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorList::dump(util::ProtoOutputStream* proto) const {
+    using namespace service::SensorListProto;
+    using namespace service::SensorListProto::SensorProto;
+
+    forEachSensor([&proto] (const Sensor& s) -> bool {
+        const uint64_t token = proto->start(SENSORS);
+        proto->write(HANDLE, s.getHandle());
+        proto->write(NAME, std::string(s.getName().string()));
+        proto->write(VENDOR, std::string(s.getVendor().string()));
+        proto->write(VERSION, s.getVersion());
+        proto->write(STRING_TYPE, std::string(s.getStringType().string()));
+        proto->write(TYPE, s.getType());
+        proto->write(REQUIRED_PERMISSION, std::string(s.getRequiredPermission().size() ?
+                s.getRequiredPermission().string() : ""));
+        proto->write(FLAGS, int(s.getFlags()));
+        switch (s.getReportingMode()) {
+            case AREPORTING_MODE_CONTINUOUS:
+                proto->write(REPORTING_MODE, RM_CONTINUOUS);
+                break;
+            case AREPORTING_MODE_ON_CHANGE:
+                proto->write(REPORTING_MODE, RM_ON_CHANGE);
+                break;
+            case AREPORTING_MODE_ONE_SHOT:
+                proto->write(REPORTING_MODE, RM_ONE_SHOT);
+                break;
+            case AREPORTING_MODE_SPECIAL_TRIGGER:
+                proto->write(REPORTING_MODE, RM_SPECIAL_TRIGGER);
+                break;
+            default:
+                proto->write(REPORTING_MODE, RM_UNKNOWN);
+        }
+        proto->write(MAX_DELAY_US, s.getMaxDelay());
+        proto->write(MIN_DELAY_US, s.getMinDelay());
+        proto->write(FIFO_MAX_EVENT_COUNT, int(s.getFifoMaxEventCount()));
+        proto->write(FIFO_RESERVED_EVENT_COUNT, int(s.getFifoReservedEventCount()));
+        proto->write(IS_WAKEUP, s.isWakeUpSensor());
+        proto->write(DATA_INJECTION_SUPPORTED, s.isDataInjectionSupported());
+        proto->write(IS_DYNAMIC, s.isDynamicSensor());
+        proto->write(HAS_ADDITIONAL_INFO, s.hasAdditionalInfo());
+        proto->write(HIGHEST_RATE_LEVEL, s.getHighestDirectReportRateLevel());
+        proto->write(ASHMEM, s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_ASHMEM));
+        proto->write(GRALLOC, s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_GRALLOC));
+        proto->write(MIN_VALUE, s.getMinValue());
+        proto->write(MAX_VALUE, s.getMaxValue());
+        proto->write(RESOLUTION, s.getResolution());
+        proto->write(POWER_USAGE, s.getPowerUsage());
+        proto->end(token);
+        return true;
+    });
+}
+
 SensorList::~SensorList() {
 }
 
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 6b90ad9..8424b22 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -71,6 +71,7 @@
 
     // Dumpable interface
     virtual std::string dump() const override;
+    virtual void dump(util::ProtoOutputStream* proto) const override;
 
     virtual ~SensorList();
 private:
diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h
index 5411515..a34a65b 100644
--- a/services/sensorservice/SensorRegistrationInfo.h
+++ b/services/sensorservice/SensorRegistrationInfo.h
@@ -17,10 +17,14 @@
 #ifndef ANDROID_SENSOR_REGISTRATION_INFO_H
 #define ANDROID_SENSOR_REGISTRATION_INFO_H
 
-#include "SensorServiceUtils.h"
-#include <utils/Thread.h>
+#include <ctime>
 #include <iomanip>
 #include <sstream>
+#include <utils/Thread.h>
+
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
+#include "SensorServiceUtils.h"
 
 namespace android {
 
@@ -30,7 +34,7 @@
 public:
     SensorRegistrationInfo() : mPackageName() {
         mSensorHandle = mSamplingRateUs = mMaxReportLatencyUs = INT32_MIN;
-        mHour = mMin = mSec = INT8_MIN;
+        mRealtimeSec = 0;
         mActivated = false;
     }
 
@@ -47,25 +51,26 @@
         mPid = (thread != nullptr) ? thread->getCallingPid() : -1;
         mUid = (thread != nullptr) ? thread->getCallingUid() : -1;
 
-        time_t rawtime = time(nullptr);
-        struct tm * timeinfo = localtime(&rawtime);
-        mHour = static_cast<int8_t>(timeinfo->tm_hour);
-        mMin = static_cast<int8_t>(timeinfo->tm_min);
-        mSec = static_cast<int8_t>(timeinfo->tm_sec);
+        timespec curTime;
+        clock_gettime(CLOCK_REALTIME_COARSE, &curTime);
+        mRealtimeSec = curTime.tv_sec;
     }
 
     static bool isSentinel(const SensorRegistrationInfo& info) {
-       return (info.mHour == INT8_MIN &&
-               info.mMin == INT8_MIN &&
-               info.mSec == INT8_MIN);
+       return (info.mSensorHandle == INT32_MIN && info.mRealtimeSec == 0);
     }
 
     // Dumpable interface
     virtual std::string dump() const override {
+        struct tm* timeinfo = localtime(&mRealtimeSec);
+        const int8_t hour = static_cast<int8_t>(timeinfo->tm_hour);
+        const int8_t min = static_cast<int8_t>(timeinfo->tm_min);
+        const int8_t sec = static_cast<int8_t>(timeinfo->tm_sec);
+
         std::ostringstream ss;
-        ss << std::setfill('0') << std::setw(2) << static_cast<int>(mHour) << ":"
-           << std::setw(2) << static_cast<int>(mMin) << ":"
-           << std::setw(2) << static_cast<int>(mSec)
+        ss << std::setfill('0') << std::setw(2) << static_cast<int>(hour) << ":"
+           << std::setw(2) << static_cast<int>(min) << ":"
+           << std::setw(2) << static_cast<int>(sec)
            << (mActivated ? " +" : " -")
            << " 0x" << std::hex << std::setw(8) << mSensorHandle << std::dec
            << std::setfill(' ') << " pid=" << std::setw(5) << mPid
@@ -77,6 +82,25 @@
         return ss.str();
     }
 
+    /**
+     * Dump debugging information as android.service.SensorRegistrationInfoProto protobuf message
+     * using ProtoOutputStream.
+     *
+     * See proto definition and some notes about ProtoOutputStream in
+     * frameworks/base/core/proto/android/service/sensor_service.proto
+     */
+    virtual void dump(util::ProtoOutputStream* proto) const override {
+        using namespace service::SensorRegistrationInfoProto;
+        proto->write(TIMESTAMP_SEC, int64_t(mRealtimeSec));
+        proto->write(SENSOR_HANDLE, mSensorHandle);
+        proto->write(PACKAGE_NAME, std::string(mPackageName.string()));
+        proto->write(PID, int32_t(mPid));
+        proto->write(UID, int32_t(mUid));
+        proto->write(SAMPLING_RATE_US, mSamplingRateUs);
+        proto->write(MAX_REPORT_LATENCY_US, mMaxReportLatencyUs);
+        proto->write(ACTIVATED, mActivated);
+    }
+
 private:
     int32_t mSensorHandle;
     String8 mPackageName;
@@ -85,8 +109,7 @@
     int64_t mSamplingRateUs;
     int64_t mMaxReportLatencyUs;
     bool mActivated;
-    int8_t mHour, mMin, mSec;
-
+    time_t mRealtimeSec;
 };
 
 } // namespace android;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 090a0ce..60f9cd9 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 #include <android/content/pm/IPackageManagerNative.h>
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <binder/ActivityManager.h>
 #include <binder/BinderService.h>
 #include <binder/IServiceManager.h>
@@ -297,13 +299,33 @@
     }
 }
 
-void SensorService::setSensorAccess(uid_t uid, bool hasAccess) {
+void SensorService::onUidStateChanged(uid_t uid, UidState state) {
+    SensorDevice& dev(SensorDevice::getInstance());
+
     ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
     for (const sp<SensorEventConnection>& conn : connLock.getActiveConnections()) {
         if (conn->getUid() == uid) {
-            conn->setSensorAccess(hasAccess);
+            dev.setUidStateForConnection(conn.get(), state);
         }
     }
+
+    for (const sp<SensorDirectConnection>& conn : connLock.getDirectConnections()) {
+        if (conn->getUid() == uid) {
+            // Update sensor subscriptions if needed
+            bool hasAccess = hasSensorAccessLocked(conn->getUid(), conn->getOpPackageName());
+            conn->onSensorAccessChanged(hasAccess);
+        }
+    }
+}
+
+bool SensorService::hasSensorAccess(uid_t uid, const String16& opPackageName) {
+    Mutex::Autolock _l(mLock);
+    return hasSensorAccessLocked(uid, opPackageName);
+}
+
+bool SensorService::hasSensorAccessLocked(uid_t uid, const String16& opPackageName) {
+    return !mSensorPrivacyPolicy->isSensorPrivacyEnabled()
+        && isUidActive(uid) && !isOperationRestrictedLocked(opPackageName);
 }
 
 const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bool isVirtual) {
@@ -403,6 +425,8 @@
                 // Transition to data injection mode supported only from NORMAL mode.
                 return INVALID_OPERATION;
             }
+        } else if (args.size() == 1 && args[0] == String16("--proto")) {
+            return dumpProtoLocked(fd, &connLock);
         } else if (!mSensors.hasAnySensor()) {
             result.append("No Sensors on the device\n");
             result.appendFormat("devInitCheck : %d\n", SensorDevice::getInstance().initCheck());
@@ -505,6 +529,128 @@
     return NO_ERROR;
 }
 
+/**
+ * Dump debugging information as android.service.SensorServiceProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+status_t SensorService::dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock) const {
+    using namespace service::SensorServiceProto;
+    util::ProtoOutputStream proto;
+    proto.write(INIT_STATUS, int(SensorDevice::getInstance().initCheck()));
+    if (!mSensors.hasAnySensor()) {
+        return proto.flush(fd) ? OK : UNKNOWN_ERROR;
+    }
+    const bool privileged = IPCThreadState::self()->getCallingUid() == 0;
+
+    timespec curTime;
+    clock_gettime(CLOCK_REALTIME, &curTime);
+    proto.write(CURRENT_TIME_MS, curTime.tv_sec * 1000 + ns2ms(curTime.tv_nsec));
+
+    // Write SensorDeviceProto
+    uint64_t token = proto.start(SENSOR_DEVICE);
+    SensorDevice::getInstance().dump(&proto);
+    proto.end(token);
+
+    // Write SensorListProto
+    token = proto.start(SENSORS);
+    mSensors.dump(&proto);
+    proto.end(token);
+
+    // Write SensorFusionProto
+    token = proto.start(FUSION_STATE);
+    SensorFusion::getInstance().dump(&proto);
+    proto.end(token);
+
+    // Write SensorEventsProto
+    token = proto.start(SENSOR_EVENTS);
+    for (auto&& i : mRecentEvent) {
+        sp<SensorInterface> s = mSensors.getInterface(i.first);
+        if (!i.second->isEmpty()) {
+            i.second->setFormat(privileged || s->getSensor().getRequiredPermission().isEmpty() ?
+                    "normal" : "mask_data");
+            const uint64_t mToken = proto.start(service::SensorEventsProto::RECENT_EVENTS_LOGS);
+            proto.write(service::SensorEventsProto::RecentEventsLog::NAME,
+                    std::string(s->getSensor().getName().string()));
+            i.second->dump(&proto);
+            proto.end(mToken);
+        }
+    }
+    proto.end(token);
+
+    // Write ActiveSensorProto
+    SensorDevice& dev = SensorDevice::getInstance();
+    for (size_t i=0 ; i<mActiveSensors.size() ; i++) {
+        int handle = mActiveSensors.keyAt(i);
+        if (dev.isSensorActive(handle)) {
+            token = proto.start(ACTIVE_SENSORS);
+            proto.write(service::ActiveSensorProto::NAME,
+                    std::string(getSensorName(handle).string()));
+            proto.write(service::ActiveSensorProto::HANDLE, handle);
+            proto.write(service::ActiveSensorProto::NUM_CONNECTIONS,
+                    int(mActiveSensors.valueAt(i)->getNumConnections()));
+            proto.end(token);
+        }
+    }
+
+    proto.write(SOCKET_BUFFER_SIZE, int(mSocketBufferSize));
+    proto.write(SOCKET_BUFFER_SIZE_IN_EVENTS, int(mSocketBufferSize / sizeof(sensors_event_t)));
+    proto.write(WAKE_LOCK_ACQUIRED, mWakeLockAcquired);
+
+    switch(mCurrentOperatingMode) {
+        case NORMAL:
+            proto.write(OPERATING_MODE, OP_MODE_NORMAL);
+            break;
+        case RESTRICTED:
+            proto.write(OPERATING_MODE, OP_MODE_RESTRICTED);
+            proto.write(WHITELISTED_PACKAGE, std::string(mWhiteListedPackage.string()));
+            break;
+        case DATA_INJECTION:
+            proto.write(OPERATING_MODE, OP_MODE_DATA_INJECTION);
+            proto.write(WHITELISTED_PACKAGE, std::string(mWhiteListedPackage.string()));
+            break;
+        default:
+            proto.write(OPERATING_MODE, OP_MODE_UNKNOWN);
+    }
+    proto.write(SENSOR_PRIVACY, mSensorPrivacyPolicy->isSensorPrivacyEnabled());
+
+    // Write repeated SensorEventConnectionProto
+    const auto& activeConnections = connLock->getActiveConnections();
+    for (size_t i = 0; i < activeConnections.size(); i++) {
+        token = proto.start(ACTIVE_CONNECTIONS);
+        activeConnections[i]->dump(&proto);
+        proto.end(token);
+    }
+
+    // Write repeated SensorDirectConnectionProto
+    const auto& directConnections = connLock->getDirectConnections();
+    for (size_t i = 0 ; i < directConnections.size() ; i++) {
+        token = proto.start(DIRECT_CONNECTIONS);
+        directConnections[i]->dump(&proto);
+        proto.end(token);
+    }
+
+    // Write repeated SensorRegistrationInfoProto
+    const int startIndex = mNextSensorRegIndex;
+    int curr = startIndex;
+    do {
+        const SensorRegistrationInfo& reg_info = mLastNSensorRegistrations[curr];
+        if (SensorRegistrationInfo::isSentinel(reg_info)) {
+            // Ignore sentinel, proceed to next item.
+            curr = (curr + 1 + SENSOR_REGISTRATIONS_BUF_SIZE) % SENSOR_REGISTRATIONS_BUF_SIZE;
+            continue;
+        }
+        token = proto.start(PREVIOUS_REGISTRATIONS);
+        reg_info.dump(&proto);
+        proto.end(token);
+        curr = (curr + 1 + SENSOR_REGISTRATIONS_BUF_SIZE) % SENSOR_REGISTRATIONS_BUF_SIZE;
+    } while (startIndex != curr);
+
+    return proto.flush(fd) ? OK : UNKNOWN_ERROR;
+}
+
 void SensorService::disableAllSensors() {
     ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
     disableAllSensorsLocked(&connLock);
@@ -512,8 +658,9 @@
 
 void SensorService::disableAllSensorsLocked(ConnectionSafeAutolock* connLock) {
     SensorDevice& dev(SensorDevice::getInstance());
-    for (const sp<SensorDirectConnection>& connection : connLock->getDirectConnections()) {
-        connection->stopAll(true /* backupRecord */);
+    for (const sp<SensorDirectConnection>& conn : connLock->getDirectConnections()) {
+        bool hasAccess = hasSensorAccessLocked(conn->getUid(), conn->getOpPackageName());
+        conn->onSensorAccessChanged(hasAccess);
     }
     dev.disableAllSensors();
     // Clear all pending flush connections for all active sensors. If one of the active
@@ -540,8 +687,9 @@
     }
     SensorDevice& dev(SensorDevice::getInstance());
     dev.enableAllSensors();
-    for (const sp<SensorDirectConnection>& connection : connLock->getDirectConnections()) {
-        connection->recoverAll();
+    for (const sp<SensorDirectConnection>& conn : connLock->getDirectConnections()) {
+        bool hasAccess = hasSensorAccessLocked(conn->getUid(), conn->getOpPackageName());
+        conn->onSensorAccessChanged(hasAccess);
     }
 }
 
@@ -1108,9 +1256,8 @@
             (packageName == "") ? String8::format("unknown_package_pid_%d", pid) : packageName;
     String16 connOpPackageName =
             (opPackageName == String16("")) ? String16(connPackageName) : opPackageName;
-    bool hasSensorAccess = mUidPolicy->isUidActive(uid);
     sp<SensorEventConnection> result(new SensorEventConnection(this, uid, connPackageName,
-            requestedMode == DATA_INJECTION, connOpPackageName, hasSensorAccess));
+            requestedMode == DATA_INJECTION, connOpPackageName));
     if (requestedMode == DATA_INJECTION) {
         mConnectionHolder.addEventConnectionIfNotPresent(result);
         // Add the associated file descriptor to the Looper for polling whenever there is data to
@@ -1172,6 +1319,11 @@
                 return nullptr;
             }
             int fd = resource->data[0];
+            if (!ashmem_valid(fd)) {
+                ALOGE("Supplied Ashmem memory region is invalid");
+                return nullptr;
+            }
+
             int size2 = ashmem_get_size_region(fd);
             // check size consistency
             if (size2 < static_cast<int64_t>(size)) {
@@ -1609,8 +1761,7 @@
     status_t err(NO_ERROR);
     Mutex::Autolock _l(mLock);
     // Loop through all sensors for this connection and call flush on each of them.
-    for (size_t i = 0; i < connection->mSensorInfo.size(); ++i) {
-        const int handle = connection->mSensorInfo.keyAt(i);
+    for (int handle : connection->getActiveSensorHandles()) {
         sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
         if (sensor == nullptr) {
             continue;
@@ -1623,7 +1774,10 @@
         if (halVersion <= SENSORS_DEVICE_API_VERSION_1_0 || isVirtualSensor(handle)) {
             // For older devices just increment pending flush count which will send a trivial
             // flush complete event.
-            connection->incrementPendingFlushCount(handle);
+            if (!connection->incrementPendingFlushCountIfHasAccess(handle)) {
+                ALOGE("flush called on an inaccessible sensor");
+                err = INVALID_OPERATION;
+            }
         } else {
             if (!canAccessSensor(sensor->getSensor(), "Tried flushing", opPackageName)) {
                 err = INVALID_OPERATION;
@@ -1651,36 +1805,28 @@
     const int32_t appOpMode = sAppOpsManager.checkOp(opCode,
             IPCThreadState::self()->getCallingUid(), opPackageName);
     bool appOpAllowed = appOpMode == AppOpsManager::MODE_ALLOWED;
+    int targetSdkVersion = getTargetSdkVersion(opPackageName);
 
     bool canAccess = false;
-    if (hasPermissionForSensor(sensor)) {
+    if (targetSdkVersion > 0 && targetSdkVersion <= __ANDROID_API_P__ &&
+            (sensor.getType() == SENSOR_TYPE_STEP_COUNTER ||
+             sensor.getType() == SENSOR_TYPE_STEP_DETECTOR)) {
+        // Allow access to step sensors if the application targets pre-Q, which is before the
+        // requirement to hold the AR permission to access Step Counter and Step Detector events
+        // was introduced.
+        canAccess = true;
+    } else if (hasPermissionForSensor(sensor)) {
         // Ensure that the AppOp is allowed, or that there is no necessary app op for the sensor
         if (opCode < 0 || appOpAllowed) {
             canAccess = true;
         }
-    } else if (sensor.getType() == SENSOR_TYPE_STEP_COUNTER ||
-            sensor.getType() == SENSOR_TYPE_STEP_DETECTOR) {
-        int targetSdkVersion = getTargetSdkVersion(opPackageName);
-        // Allow access to the sensor if the application targets pre-Q, which is before the
-        // requirement to hold the AR permission to access Step Counter and Step Detector events
-        // was introduced, and the user hasn't revoked the app op.
-        //
-        // Verifying the app op is required to ensure that the user hasn't revoked the necessary
-        // permissions to access the Step Detector and Step Counter when the application targets
-        // pre-Q. Without this check, if the user revokes the pre-Q install-time GMS Core AR
-        // permission, the app would still be able to receive Step Counter and Step Detector events.
-        if (appOpAllowed &&
-                targetSdkVersion > 0 &&
-                targetSdkVersion <= __ANDROID_API_P__) {
-            canAccess = true;
-        }
     }
 
     if (canAccess) {
         sAppOpsManager.noteOp(opCode, IPCThreadState::self()->getCallingUid(), opPackageName);
     } else {
-        ALOGE("%s a sensor (%s) without holding its required permission: %s",
-                operation, sensor.getName().string(), sensor.getRequiredPermission().string());
+        ALOGE("%s %s a sensor (%s) without holding %s", String8(opPackageName).string(),
+              operation, sensor.getName().string(), sensor.getRequiredPermission().string());
     }
 
     return canAccess;
@@ -1757,13 +1903,12 @@
     return (packageName.contains(mWhiteListedPackage.string()));
 }
 
-bool SensorService::isOperationPermitted(const String16& opPackageName) {
-    Mutex::Autolock _l(mLock);
+bool SensorService::isOperationRestrictedLocked(const String16& opPackageName) {
     if (mCurrentOperatingMode == RESTRICTED) {
         String8 package(opPackageName);
-        return isWhiteListedPackage(package);
+        return !isWhiteListedPackage(package);
     }
-    return true;
+    return false;
 }
 
 void SensorService::UidPolicy::registerSelf() {
@@ -1791,7 +1936,7 @@
     }
     sp<SensorService> service = mService.promote();
     if (service != nullptr) {
-        service->setSensorAccess(uid, true);
+        service->onUidStateChanged(uid, UID_STATE_ACTIVE);
     }
 }
 
@@ -1806,7 +1951,7 @@
     if (deleted) {
         sp<SensorService> service = mService.promote();
         if (service != nullptr) {
-            service->setSensorAccess(uid, false);
+            service->onUidStateChanged(uid, UID_STATE_IDLE);
         }
     }
 }
@@ -1834,7 +1979,7 @@
     if (wasActive != isActive) {
         sp<SensorService> service = mService.promote();
         if (service != nullptr) {
-            service->setSensorAccess(uid, isActive);
+            service->onUidStateChanged(uid, isActive ? UID_STATE_ACTIVE : UID_STATE_IDLE);
         }
     }
 }
@@ -1860,6 +2005,10 @@
     return mActiveUids.find(uid) != mActiveUids.end();
 }
 
+bool SensorService::isUidActive(uid_t uid) {
+    return mUidPolicy->isUidActive(uid);
+}
+
 void SensorService::SensorPrivacyPolicy::registerSelf() {
     SensorPrivacyManager spm;
     mSensorPrivacyEnabled = spm.isSensorPrivacyEnabled();
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 060b5eb..3bb8421 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -75,6 +75,11 @@
     class SensorDirectConnection;
 
 public:
+    enum UidState {
+      UID_STATE_ACTIVE = 0,
+      UID_STATE_IDLE,
+    };
+
     void cleanupConnection(SensorEventConnection* connection);
     void cleanupConnection(SensorDirectConnection* c);
 
@@ -180,7 +185,7 @@
             void onUidActive(uid_t uid);
             void onUidIdle(uid_t uid, bool disabled);
             void onUidStateChanged(uid_t uid __unused, int32_t procState __unused,
-                                   int64_t procStateSeq __unused) {}
+                                   int64_t procStateSeq __unused, int32_t capability __unused) {}
 
             void addOverrideUid(uid_t uid, bool active);
             void removeOverrideUid(uid_t uid);
@@ -194,6 +199,8 @@
             std::unordered_map<uid_t, bool> mOverrideUids;
     };
 
+    bool isUidActive(uid_t uid);
+
     // Sensor privacy allows a user to disable access to all sensors on the device. When
     // enabled sensor privacy will prevent all apps, including active apps, from accessing
     // sensors, they will not receive trigger nor on-change events, flush event behavior
@@ -286,6 +293,7 @@
     virtual int setOperationParameter(
             int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints);
     virtual status_t dump(int fd, const Vector<String16>& args);
+    status_t dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock) const;
     String8 getSensorName(int handle) const;
     bool isVirtualSensor(int handle) const;
     sp<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
@@ -331,7 +339,11 @@
     // allowed to register for or call flush on sensors. Typically only cts test packages are
     // allowed.
     bool isWhiteListedPackage(const String8& packageName);
-    bool isOperationPermitted(const String16& opPackageName);
+
+    // Returns true if a connection with the specified opPackageName has no access to sensors
+    // in the RESTRICTED mode (i.e. the service is in RESTRICTED mode, and the package is not
+    // whitelisted). mLock must be held to invoke this method.
+    bool isOperationRestrictedLocked(const String16& opPackageName);
 
     // Reset the state of SensorService to NORMAL mode.
     status_t resetToNormalMode();
@@ -348,7 +360,13 @@
     void enableSchedFifoMode();
 
     // Sets whether the given UID can get sensor data
-    void setSensorAccess(uid_t uid, bool hasAccess);
+    void onUidStateChanged(uid_t uid, UidState state);
+
+    // Returns true if a connection with the given uid and opPackageName
+    // currently has access to sensors.
+    bool hasSensorAccess(uid_t uid, const String16& opPackageName);
+    // Same as hasSensorAccess but with mLock held.
+    bool hasSensorAccessLocked(uid_t uid, const String16& opPackageName);
 
     // Overrides the UID state as if it is idle
     status_t handleSetUidState(Vector<String16>& args, int err);
diff --git a/services/sensorservice/SensorServiceUtils.cpp b/services/sensorservice/SensorServiceUtils.cpp
index 34cd8dd..fdd56b3 100644
--- a/services/sensorservice/SensorServiceUtils.cpp
+++ b/services/sensorservice/SensorServiceUtils.cpp
@@ -55,6 +55,7 @@
         case SENSOR_TYPE_MOTION_DETECT:
         case SENSOR_TYPE_HEART_BEAT:
         case SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT:
+        case SENSOR_TYPE_HINGE_ANGLE:
             return 1;
 
         default:
diff --git a/services/sensorservice/SensorServiceUtils.h b/services/sensorservice/SensorServiceUtils.h
index 1558feb..49457cf 100644
--- a/services/sensorservice/SensorServiceUtils.h
+++ b/services/sensorservice/SensorServiceUtils.h
@@ -21,11 +21,17 @@
 #include <string>
 
 namespace android {
+
+namespace util {
+class ProtoOutputStream;
+}
+
 namespace SensorServiceUtil {
 
 class Dumpable {
 public:
     virtual std::string dump() const = 0;
+    virtual void dump(util::ProtoOutputStream*) const {}
     virtual void setFormat(std::string ) {}
     virtual ~Dumpable() {}
 };
diff --git a/services/sensorservice/SensorsWrapper.h b/services/sensorservice/SensorsWrapper.h
deleted file mode 100644
index d1a7234..0000000
--- a/services/sensorservice/SensorsWrapper.h
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SENSORS_WRAPPER_H
-#define ANDROID_SENSORS_WRAPPER_H
-
-#include "android/hardware/sensors/1.0/ISensors.h"
-#include "android/hardware/sensors/2.0/ISensors.h"
-#include "android/hardware/sensors/2.0/ISensorsCallback.h"
-
-#include <utils/LightRefBase.h>
-
-namespace android {
-namespace SensorServiceUtil {
-
-using ::android::hardware::MQDescriptorSync;
-using ::android::hardware::Return;
-using ::android::hardware::sensors::V1_0::Event;
-using ::android::hardware::sensors::V1_0::ISensors;
-using ::android::hardware::sensors::V1_0::OperationMode;
-using ::android::hardware::sensors::V1_0::RateLevel;
-using ::android::hardware::sensors::V1_0::Result;
-using ::android::hardware::sensors::V1_0::SharedMemInfo;
-using ::android::hardware::sensors::V2_0::ISensorsCallback;
-
-/*
- * The ISensorsWrapper interface includes all function from supported Sensors HAL versions. This
- * allows for the SensorDevice to use the ISensorsWrapper interface to interact with the Sensors
- * HAL regardless of the current version of the Sensors HAL that is loaded. Each concrete
- * instantiation of ISensorsWrapper must correspond to a specific Sensors HAL version. This design
- * is beneficial because only the functions that change between Sensors HAL versions must be newly
- * newly implemented, any previously implemented function that does not change may remain the same.
- *
- * Functions that exist across all versions of the Sensors HAL should be implemented as pure
- * virtual functions which forces the concrete instantiations to implement the functions.
- *
- * Functions that do not exist across all versions of the Sensors HAL should include a default
- * implementation that generates an error if called. The default implementation should never
- * be called and must be overridden by Sensors HAL versions that support the function.
- */
-class ISensorsWrapper : public VirtualLightRefBase {
-public:
-    virtual bool supportsPolling() const = 0;
-
-    virtual bool supportsMessageQueues() const = 0;
-
-    virtual Return<void> getSensorsList(ISensors::getSensorsList_cb _hidl_cb) = 0;
-
-    virtual Return<Result> setOperationMode(OperationMode mode) = 0;
-
-    virtual Return<Result> activate(int32_t sensorHandle, bool enabled) = 0;
-
-    virtual Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
-                                 int64_t maxReportLatencyNs) = 0;
-
-    virtual Return<Result> flush(int32_t sensorHandle) = 0;
-
-    virtual Return<Result> injectSensorData(const Event& event) = 0;
-
-    virtual Return<void> registerDirectChannel(const SharedMemInfo& mem,
-                                               ISensors::registerDirectChannel_cb _hidl_cb) = 0;
-
-    virtual Return<Result> unregisterDirectChannel(int32_t channelHandle) = 0;
-
-    virtual Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle,
-                                            RateLevel rate,
-                                            ISensors::configDirectReport_cb _hidl_cb) = 0;
-
-    virtual Return<void> poll(int32_t maxCount, ISensors::poll_cb _hidl_cb) {
-        (void)maxCount;
-        (void)_hidl_cb;
-        // TODO (b/111070257): Generate an assert-level error since this should never be called
-        // directly
-        return Return<void>();
-    }
-
-    virtual Return<Result> initialize(const MQDescriptorSync<Event>& eventQueueDesc,
-                                      const MQDescriptorSync<uint32_t>& wakeLockDesc,
-                                      const ::android::sp<ISensorsCallback>& callback) {
-        (void)eventQueueDesc;
-        (void)wakeLockDesc;
-        (void)callback;
-        // TODO (b/111070257): Generate an assert-level error since this should never be called
-        // directly
-        return Result::INVALID_OPERATION;
-    }
-};
-
-template<typename T>
-class SensorsWrapperBase : public ISensorsWrapper {
-public:
-    SensorsWrapperBase(sp<T> sensors) :
-        mSensors(sensors) { };
-
-    Return<void> getSensorsList(ISensors::getSensorsList_cb _hidl_cb) override {
-        return mSensors->getSensorsList(_hidl_cb);
-    }
-
-    Return<Result> setOperationMode(OperationMode mode) override {
-        return mSensors->setOperationMode(mode);
-    }
-
-    Return<Result> activate(int32_t sensorHandle, bool enabled) override {
-        return mSensors->activate(sensorHandle, enabled);
-    }
-
-    Return<Result> batch(int32_t sensorHandle, int64_t samplingPeriodNs,
-                         int64_t maxReportLatencyNs) override {
-        return mSensors->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs);
-    }
-
-    Return<Result> flush(int32_t sensorHandle) override {
-        return mSensors->flush(sensorHandle);
-    }
-
-    Return<Result> injectSensorData(const Event& event) override {
-        return mSensors->injectSensorData(event);
-    }
-
-    Return<void> registerDirectChannel(const SharedMemInfo& mem,
-                                       ISensors::registerDirectChannel_cb _hidl_cb) override {
-        return mSensors->registerDirectChannel(mem, _hidl_cb);
-    }
-
-    Return<Result> unregisterDirectChannel(int32_t channelHandle) override {
-        return mSensors->unregisterDirectChannel(channelHandle);
-    }
-
-    Return<void> configDirectReport(int32_t sensorHandle, int32_t channelHandle,
-                                    RateLevel rate,
-                                    ISensors::configDirectReport_cb _hidl_cb) override {
-        return mSensors->configDirectReport(sensorHandle, channelHandle, rate, _hidl_cb);
-    }
-
-protected:
-    sp<T> mSensors;
-};
-
-class SensorsWrapperV1_0 : public SensorsWrapperBase<hardware::sensors::V1_0::ISensors> {
-public:
-    SensorsWrapperV1_0(sp<hardware::sensors::V1_0::ISensors> sensors) :
-        SensorsWrapperBase(sensors) { };
-
-    bool supportsPolling() const override {
-        return true;
-    }
-
-    bool supportsMessageQueues() const override {
-        return false;
-    }
-
-    Return<void> poll(int32_t maxCount,
-                      hardware::sensors::V1_0::ISensors::poll_cb _hidl_cb) override {
-        return mSensors->poll(maxCount, _hidl_cb);
-    }
-};
-
-class SensorsWrapperV2_0 : public SensorsWrapperBase<hardware::sensors::V2_0::ISensors> {
-public:
-    SensorsWrapperV2_0(sp<hardware::sensors::V2_0::ISensors> sensors)
-        : SensorsWrapperBase(sensors) { };
-
-    bool supportsPolling() const override {
-        return false;
-    }
-
-    bool supportsMessageQueues() const override {
-        return true;
-    }
-
-    Return<Result> initialize(const MQDescriptorSync<Event>& eventQueueDesc,
-                              const MQDescriptorSync<uint32_t>& wakeLockDesc,
-                              const ::android::sp<ISensorsCallback>& callback) override {
-        return mSensors->initialize(eventQueueDesc, wakeLockDesc, callback);
-    }
-};
-
-}; // namespace SensorServiceUtil
-}; // namespace android
-
-#endif // ANDROID_SENSORS_WRAPPER_H
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index 1cb0489..caf7f03 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -15,19 +15,20 @@
  */
 
 #include <inttypes.h>
+#include <android/hardware_buffer.h>
 #include <android/sensor.h>
 #include <sensor/Sensor.h>
 #include <sensor/SensorManager.h>
 #include <sensor/SensorEventQueue.h>
 #include <utils/Looper.h>
+#include <vndk/hardware_buffer.h>
 
 using namespace android;
 
 static nsecs_t sStartTime = 0;
 
 
-int receiver(__unused int fd, __unused int events, void* data)
-{
+int receiver(__unused int fd, __unused int events, void* data) {
     sp<SensorEventQueue> q((SensorEventQueue*)data);
     ssize_t n;
     ASensorEvent buffer[8];
@@ -59,11 +60,42 @@
     return 1;
 }
 
+void testInvalidSharedMem_NoCrash(SensorManager &mgr) {
+    AHardwareBuffer *hardwareBuffer;
+    char* buffer;
 
-int main()
-{
+    constexpr size_t kEventSize = sizeof(ASensorEvent);
+    constexpr size_t kNEvent = 4096; // enough to contain 1.5 * 800 * 2.2 events
+    constexpr size_t kMemSize = kEventSize * kNEvent;
+    AHardwareBuffer_Desc desc = {
+            .width = static_cast<uint32_t>(kMemSize),
+            .height = 1,
+            .layers = 1,
+            .format = AHARDWAREBUFFER_FORMAT_BLOB,
+            .usage = AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA
+                        | AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+    };
+
+    AHardwareBuffer_allocate(&desc, &hardwareBuffer);
+    AHardwareBuffer_lock(hardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY,
+                         -1, nullptr, reinterpret_cast<void **>(&buffer));
+
+    const native_handle_t *resourceHandle = AHardwareBuffer_getNativeHandle(hardwareBuffer);
+
+    // Pass in AHardwareBuffer, but with the wrong DIRECT_CHANNEL_TYPE to see
+    // if anything in the Sensor framework crashes
+    int ret = mgr.createDirectChannel(
+            kMemSize, ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY, resourceHandle);
+
+    // Should print -22 (BAD_VALUE) and the device runtime shouldn't restart
+    printf("createInvalidDirectChannel=%d\n", ret);
+}
+
+int main() {
     SensorManager& mgr = SensorManager::getInstanceForPackage(String16("Sensor Service Test"));
 
+    testInvalidSharedMem_NoCrash(mgr);
+
     Sensor const* const* list;
     ssize_t count = mgr.getSensorList(&list);
     printf("numSensors=%d\n", int(count));
diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp
index b775431..80c3b65 100644
--- a/services/stats/StatsHal.cpp
+++ b/services/stats/StatsHal.cpp
@@ -112,9 +112,39 @@
 }
 
 hardware::Return<void> StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
-  ALOGW("reportVendorAtom unsupported");
-  std::string reverDomainName = vendorAtom.reverseDomainName;
-  return hardware::Void();
+    std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
+    if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
+        ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
+        return hardware::Void();
+    }
+    if (reverseDomainName.length() > 50) {
+        ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
+        return hardware::Void();
+    }
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, vendorAtom.atomId);
+    AStatsEvent_writeString(event, vendorAtom.reverseDomainName.c_str());
+    for (int i = 0; i < (int)vendorAtom.values.size(); i++) {
+        switch (vendorAtom.values[i].getDiscriminator()) {
+            case VendorAtom::Value::hidl_discriminator::intValue:
+                AStatsEvent_writeInt32(event, vendorAtom.values[i].intValue());
+                break;
+            case VendorAtom::Value::hidl_discriminator::longValue:
+                AStatsEvent_writeInt64(event, vendorAtom.values[i].longValue());
+                break;
+            case VendorAtom::Value::hidl_discriminator::floatValue:
+                AStatsEvent_writeFloat(event, vendorAtom.values[i].floatValue());
+                break;
+            case VendorAtom::Value::hidl_discriminator::stringValue:
+                AStatsEvent_writeString(event, vendorAtom.values[i].stringValue().c_str());
+                break;
+        }
+    }
+    AStatsEvent_build(event);
+    AStatsEvent_write(event);
+    AStatsEvent_release(event);
+
+    return hardware::Void();
 }
 
 }  // namespace implementation
diff --git a/services/stats/include/stats/StatsHal.h b/services/stats/include/stats/StatsHal.h
index ad14263..071e54f 100644
--- a/services/stats/include/stats/StatsHal.h
+++ b/services/stats/include/stats/StatsHal.h
@@ -17,6 +17,8 @@
 #include <android/frameworks/stats/1.0/IStats.h>
 #include <android/frameworks/stats/1.0/types.h>
 
+#include <stats_event.h>
+
 using namespace android::frameworks::stats::V1_0;
 
 namespace android {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index cda982a..a790d0b 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -7,6 +7,7 @@
         "-Wthread-safety",
         "-Wunused",
         "-Wunreachable-code",
+        "-Wconversion",
     ],
 }
 
@@ -19,7 +20,7 @@
         "-DEGL_EGLEXT_PROTOTYPES",
     ],
     shared_libs: [
-        "android.frameworks.vr.composer@1.0",
+        "android.frameworks.vr.composer@2.0",
         "android.hardware.configstore-utils",
         "android.hardware.configstore@1.0",
         "android.hardware.configstore@1.1",
@@ -29,19 +30,19 @@
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
+        "android.hardware.graphics.composer@2.4",
         "android.hardware.power@1.0",
         "android.hardware.power@1.3",
+        "android.hardware.power-cpp",
         "libbase",
         "libbinder",
         "libbufferhubqueue",
         "libcutils",
-        "libdl",
         "libEGL",
         "libfmq",
         "libGLESv1_CM",
         "libGLESv2",
         "libgui",
-        "libhardware",
         "libhidlbase",
         "liblayers_proto",
         "liblog",
@@ -49,25 +50,35 @@
         "libpdx_default_transport",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
+        "libstatslog",
         "libsync",
-        "libtimestats_proto",
+        "libtimestats",
         "libui",
         "libinput",
         "libutils",
         "libSurfaceFlingerProp",
     ],
+    // VrComposer is not used when building surfaceflinger for vendors
+    target: {
+        vendor: {
+            exclude_shared_libs: [
+                "android.frameworks.vr.composer@2.0",
+            ],
+        },
+    },
     static_libs: [
         "libcompositionengine",
+        "libperfetto_client_experimental",
         "librenderengine",
         "libserviceutils",
         "libtrace_proto",
-        "libvr_manager",
         "libvrflinger",
     ],
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
         "android.hardware.graphics.composer@2.2-command-buffer",
         "android.hardware.graphics.composer@2.3-command-buffer",
+        "android.hardware.graphics.composer@2.4-command-buffer",
     ],
     export_static_lib_headers: [
         "libcompositionengine",
@@ -81,9 +92,15 @@
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
+        "android.hardware.graphics.composer@2.4",
         "android.hardware.power@1.3",
         "libhidlbase",
+        "libtimestats",
     ],
+    // TODO (marissaw): this library is not used by surfaceflinger. This is here so
+    // the library compiled in a way that is accessible to system partition when running
+    // IMapper's VTS.
+    required: ["libgralloctypes"]
 }
 
 cc_defaults {
@@ -118,7 +135,7 @@
         "BufferStateLayer.cpp",
         "ClientCache.cpp",
         "Client.cpp",
-        "ColorLayer.cpp",
+        "EffectLayer.cpp",
         "ContainerLayer.cpp",
         "DisplayDevice.cpp",
         "DisplayHardware/ComposerHal.cpp",
@@ -130,11 +147,11 @@
         "DisplayHardware/VirtualDisplaySurface.cpp",
         "Effects/Daltonizer.cpp",
         "EventLog/EventLog.cpp",
+        "FrameTracer/FrameTracer.cpp",
         "FrameTracker.cpp",
         "Layer.cpp",
         "LayerProtoHelper.cpp",
         "LayerRejecter.cpp",
-        "LayerStats.cpp",
         "LayerVector.cpp",
         "MonitoredProducer.cpp",
         "NativeWindowSurface.cpp",
@@ -145,19 +162,26 @@
         "Scheduler/DispSyncSource.cpp",
         "Scheduler/EventControlThread.cpp",
         "Scheduler/EventThread.cpp",
-        "Scheduler/IdleTimer.cpp",
+        "Scheduler/OneShotTimer.cpp",
         "Scheduler/LayerHistory.cpp",
+        "Scheduler/LayerHistoryV2.cpp",
         "Scheduler/LayerInfo.cpp",
+        "Scheduler/LayerInfoV2.cpp",
         "Scheduler/MessageQueue.cpp",
         "Scheduler/PhaseOffsets.cpp",
+        "Scheduler/RefreshRateConfigs.cpp",
         "Scheduler/Scheduler.cpp",
         "Scheduler/SchedulerUtils.cpp",
+        "Scheduler/Timer.cpp",
+        "Scheduler/VSyncDispatchTimerQueue.cpp",
+        "Scheduler/VSyncPredictor.cpp",
         "Scheduler/VSyncModulator.cpp",
+        "Scheduler/VSyncReactor.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
+        "SurfaceFlingerDefaultFactory.cpp",
         "SurfaceInterceptor.cpp",
         "SurfaceTracing.cpp",
-        "TimeStats/TimeStats.cpp",
         "TransactionCompletedThread.cpp",
     ],
 }
@@ -174,6 +198,17 @@
         // can be easily replaced.
         "SurfaceFlingerFactory.cpp",
     ],
+    cflags: [
+        "-DUSE_VR_COMPOSER=1",
+    ],
+    // VrComposer is not used when building surfaceflinger for vendors
+    target: {
+        vendor: {
+            cflags: [
+                "-DUSE_VR_COMPOSER=0",
+            ],
+        },
+    },
     logtags: ["EventLog/EventLogTags.logtags"],
 }
 
@@ -198,7 +233,6 @@
         "liblog",
         "libprocessgroup",
         "libsync",
-        "libtimestats_proto",
         "libutils",
     ],
     static_libs: [
@@ -226,7 +260,6 @@
 
 subdirs = [
     "layerproto",
-    "TimeStats/timestatsproto",
     "tests",
 ]
 
@@ -251,6 +284,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 4517eff..f0b0200 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "BufferLayer"
@@ -22,17 +26,15 @@
 #include "BufferLayer.h"
 
 #include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
-#include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <cutils/compiler.h>
 #include <cutils/native_handle.h>
 #include <cutils/properties.h>
 #include <gui/BufferItem.h>
 #include <gui/BufferQueue.h>
+#include <gui/GLConsumer.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 #include <renderengine/RenderEngine.h>
@@ -50,17 +52,20 @@
 
 #include "Colorizer.h"
 #include "DisplayDevice.h"
+#include "FrameTracer/FrameTracer.h"
 #include "LayerRejecter.h"
 #include "TimeStats/TimeStats.h"
 
 namespace android {
 
+static constexpr float defaultMaxMasteringLuminance = 1000.0;
+static constexpr float defaultMaxContentLuminance = 1000.0;
+
 BufferLayer::BufferLayer(const LayerCreationArgs& args)
       : Layer(args),
-        mTextureName(args.flinger->getNewTexture()),
-        mCompositionLayer{mFlinger->getCompositionEngine().createLayer(
-                compositionengine::LayerCreationArgs{this})} {
-    ALOGV("Creating Layer %s", args.name.string());
+        mTextureName(args.textureName),
+        mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()} {
+    ALOGV("Creating Layer %s", getDebugName());
 
     mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
 
@@ -69,15 +74,23 @@
 }
 
 BufferLayer::~BufferLayer() {
-    mFlinger->deleteTextureAsync(mTextureName);
-    mFlinger->mTimeStats->onDestroy(getSequence());
+    if (!isClone()) {
+        // The original layer and the clone layer share the same texture. Therefore, only one of
+        // the layers, in this case the original layer, needs to handle the deletion. The original
+        // layer and the clone should be removed at the same time so there shouldn't be any issue
+        // with the clone layer trying to use the deleted texture.
+        mFlinger->deleteTextureAsync(mTextureName);
+    }
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->onDestroy(layerId);
+    mFlinger->mFrameTracer->onDestroy(layerId);
 }
 
 void BufferLayer::useSurfaceDamage() {
     if (mFlinger->mForceFullDamage) {
         surfaceDamageRegion = Region::INVALID_REGION;
     } else {
-        surfaceDamageRegion = getDrawingSurfaceDamage();
+        surfaceDamageRegion = mBufferInfo.mSurfaceDamage;
     }
 }
 
@@ -88,7 +101,7 @@
 bool BufferLayer::isOpaque(const Layer::State& s) const {
     // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
     // layer's opaque flag.
-    if ((mSidebandStream == nullptr) && (mActiveBuffer == nullptr)) {
+    if ((mSidebandStream == nullptr) && (mBufferInfo.mBuffer == nullptr)) {
         return false;
     }
 
@@ -98,11 +111,8 @@
 }
 
 bool BufferLayer::isVisible() const {
-    bool visible = !(isHiddenByPolicy()) && getAlpha() > 0.0f &&
-            (mActiveBuffer != nullptr || mSidebandStream != nullptr);
-    mFlinger->mScheduler->setLayerVisibility(mSchedulerLayerHandle, visible);
-
-    return visible;
+    return !isHiddenByPolicy() && getAlpha() > 0.0f &&
+            (mBufferInfo.mBuffer != nullptr || mSidebandStream != nullptr);
 }
 
 bool BufferLayer::isFixedSize() const {
@@ -131,14 +141,17 @@
     return inverse(tr);
 }
 
-bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                                     bool useIdentityTransform, Region& clearRegion,
-                                     const bool supportProtectedContent,
-                                     renderengine::LayerSettings& layer) {
+std::optional<compositionengine::LayerFE::LayerSettings> BufferLayer::prepareClientComposition(
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
     ATRACE_CALL();
-    Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion,
-                              supportProtectedContent, layer);
-    if (CC_UNLIKELY(mActiveBuffer == 0)) {
+
+    std::optional<compositionengine::LayerFE::LayerSettings> result =
+            Layer::prepareClientComposition(targetSettings);
+    if (!result) {
+        return result;
+    }
+
+    if (CC_UNLIKELY(mBufferInfo.mBuffer == 0)) {
         // the texture has not been created yet, this Layer has
         // in fact never been drawn into. This happens frequently with
         // SurfaceView because the WindowManager can't know when the client
@@ -155,199 +168,150 @@
                 finished = true;
                 return;
             }
-            under.orSelf(layer->visibleRegion);
+
+            under.orSelf(layer->getScreenBounds());
         });
         // if not everything below us is covered, we plug the holes!
-        Region holes(clip.subtract(under));
+        Region holes(targetSettings.clip.subtract(under));
         if (!holes.isEmpty()) {
-            clearRegion.orSelf(holes);
+            targetSettings.clearRegion.orSelf(holes);
         }
-        return false;
+        return std::nullopt;
     }
-    bool blackOutLayer =
-            (isProtected() && !supportProtectedContent) || (isSecure() && !renderArea.isSecure());
+    bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
+            (isSecure() && !targetSettings.isSecure);
+    compositionengine::LayerFE::LayerSettings& layer = *result;
+    if (blackOutLayer) {
+        prepareClearClientComposition(layer, true /* blackout */);
+        return layer;
+    }
+
     const State& s(getDrawingState());
-    if (!blackOutLayer) {
-        layer.source.buffer.buffer = mActiveBuffer;
-        layer.source.buffer.isOpaque = isOpaque(s);
-        layer.source.buffer.fence = mActiveBufferFence;
-        layer.source.buffer.textureName = mTextureName;
-        layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
-        layer.source.buffer.isY410BT2020 = isHdrY410();
-        // TODO: we could be more subtle with isFixedSize()
-        const bool useFiltering = needsFiltering(renderArea.getDisplayDevice()) ||
-                renderArea.needsFiltering() || isFixedSize();
+    layer.source.buffer.buffer = mBufferInfo.mBuffer;
+    layer.source.buffer.isOpaque = isOpaque(s);
+    layer.source.buffer.fence = mBufferInfo.mFence;
+    layer.source.buffer.textureName = mTextureName;
+    layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
+    layer.source.buffer.isY410BT2020 = isHdrY410();
+    bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
+    bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
+    layer.source.buffer.maxMasteringLuminance = hasSmpte2086
+            ? mBufferInfo.mHdrMetadata.smpte2086.maxLuminance
+            : defaultMaxMasteringLuminance;
+    layer.source.buffer.maxContentLuminance = hasCta861_3
+            ? mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel
+            : defaultMaxContentLuminance;
+    layer.frameNumber = mCurrentFrameNumber;
+    layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
 
-        // Query the texture matrix given our current filtering mode.
-        float textureMatrix[16];
-        setFilteringEnabled(useFiltering);
-        getDrawingTransformMatrix(textureMatrix);
+    // TODO: we could be more subtle with isFixedSize()
+    const bool useFiltering = targetSettings.needsFiltering || mNeedsFiltering || isFixedSize();
 
-        if (getTransformToDisplayInverse()) {
-            /*
-             * the code below applies the primary display's inverse transform to
-             * the texture transform
-             */
-            uint32_t transform = DisplayDevice::getPrimaryDisplayOrientationTransform();
-            mat4 tr = inverseOrientation(transform);
+    // Query the texture matrix given our current filtering mode.
+    float textureMatrix[16];
+    getDrawingTransformMatrix(useFiltering, textureMatrix);
 
-            /**
-             * TODO(b/36727915): This is basically a hack.
-             *
-             * Ensure that regardless of the parent transformation,
-             * this buffer is always transformed from native display
-             * orientation to display orientation. For example, in the case
-             * of a camera where the buffer remains in native orientation,
-             * we want the pixels to always be upright.
-             */
-            sp<Layer> p = mDrawingParent.promote();
-            if (p != nullptr) {
-                const auto parentTransform = p->getTransform();
-                tr = tr * inverseOrientation(parentTransform.getOrientation());
-            }
+    if (getTransformToDisplayInverse()) {
+        /*
+         * the code below applies the primary display's inverse transform to
+         * the texture transform
+         */
+        uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
+        mat4 tr = inverseOrientation(transform);
 
-            // and finally apply it to the original texture matrix
-            const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
-            memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
+        /**
+         * TODO(b/36727915): This is basically a hack.
+         *
+         * Ensure that regardless of the parent transformation,
+         * this buffer is always transformed from native display
+         * orientation to display orientation. For example, in the case
+         * of a camera where the buffer remains in native orientation,
+         * we want the pixels to always be upright.
+         */
+        sp<Layer> p = mDrawingParent.promote();
+        if (p != nullptr) {
+            const auto parentTransform = p->getTransform();
+            tr = tr * inverseOrientation(parentTransform.getOrientation());
         }
 
-        const Rect win{getBounds()};
-        float bufferWidth = getBufferSize(s).getWidth();
-        float bufferHeight = getBufferSize(s).getHeight();
-
-        // BufferStateLayers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
-        // been set and there is no parent layer bounds. In that case, the scale is meaningless so
-        // ignore them.
-        if (!getBufferSize(s).isValid()) {
-            bufferWidth = float(win.right) - float(win.left);
-            bufferHeight = float(win.bottom) - float(win.top);
-        }
-
-        const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
-        const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
-        const float translateY = float(win.top) / bufferHeight;
-        const float translateX = float(win.left) / bufferWidth;
-
-        // Flip y-coordinates because GLConsumer expects OpenGL convention.
-        mat4 tr = mat4::translate(vec4(.5, .5, 0, 1)) * mat4::scale(vec4(1, -1, 1, 1)) *
-                mat4::translate(vec4(-.5, -.5, 0, 1)) *
-                mat4::translate(vec4(translateX, translateY, 0, 1)) *
-                mat4::scale(vec4(scaleWidth, scaleHeight, 1.0, 1.0));
-
-        layer.source.buffer.useTextureFiltering = useFiltering;
-        layer.source.buffer.textureTransform = mat4(static_cast<const float*>(textureMatrix)) * tr;
-    } else {
-        // If layer is blacked out, force alpha to 1 so that we draw a black color
-        // layer.
-        layer.source.buffer.buffer = nullptr;
-        layer.alpha = 1.0;
+        // and finally apply it to the original texture matrix
+        const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
+        memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
     }
 
-    return true;
+    const Rect win{getBounds()};
+    float bufferWidth = getBufferSize(s).getWidth();
+    float bufferHeight = getBufferSize(s).getHeight();
+
+    // BufferStateLayers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
+    // been set and there is no parent layer bounds. In that case, the scale is meaningless so
+    // ignore them.
+    if (!getBufferSize(s).isValid()) {
+        bufferWidth = float(win.right) - float(win.left);
+        bufferHeight = float(win.bottom) - float(win.top);
+    }
+
+    const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight;
+    const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth;
+    const float translateY = float(win.top) / bufferHeight;
+    const float translateX = float(win.left) / bufferWidth;
+
+    // Flip y-coordinates because GLConsumer expects OpenGL convention.
+    mat4 tr = mat4::translate(vec4(.5, .5, 0, 1)) * mat4::scale(vec4(1, -1, 1, 1)) *
+            mat4::translate(vec4(-.5, -.5, 0, 1)) *
+            mat4::translate(vec4(translateX, translateY, 0, 1)) *
+            mat4::scale(vec4(scaleWidth, scaleHeight, 1.0, 1.0));
+
+    layer.source.buffer.useTextureFiltering = useFiltering;
+    layer.source.buffer.textureTransform = mat4(static_cast<const float*>(textureMatrix)) * tr;
+
+    return layer;
 }
 
 bool BufferLayer::isHdrY410() const {
     // pixel format is HDR Y410 masquerading as RGBA_1010102
-    return (mCurrentDataSpace == ui::Dataspace::BT2020_ITU_PQ &&
-            getDrawingApi() == NATIVE_WINDOW_API_MEDIA &&
-            getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102);
+    return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ &&
+            mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA &&
+            mBufferInfo.mPixelFormat == HAL_PIXEL_FORMAT_RGBA_1010102);
 }
 
-PixelFormat BufferLayer::getPixelFormat() const {
-    if (!mActiveBuffer) {
-        return PIXEL_FORMAT_NONE;
-    }
-    return mActiveBuffer->format;
+sp<compositionengine::LayerFE> BufferLayer::getCompositionEngineLayerFE() const {
+    return asLayerFE();
 }
 
-void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice,
-                                  const ui::Transform& transform, const Rect& viewport,
-                                  int32_t supportedPerFrameMetadata,
-                                  const ui::Dataspace targetDataspace) {
-    RETURN_IF_NO_HWC_LAYER(displayDevice);
+compositionengine::LayerFECompositionState* BufferLayer::editCompositionState() {
+    return mCompositionState.get();
+}
 
-    // Apply this display's projection's viewport to the visible region
-    // before giving it to the HWC HAL.
-    Region visible = transform.transform(visibleRegion.intersect(viewport));
+const compositionengine::LayerFECompositionState* BufferLayer::getCompositionState() const {
+    return mCompositionState.get();
+}
 
-    const auto outputLayer = findOutputLayerForDisplay(displayDevice);
-    LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
-
-    auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
-    auto error = hwcLayer->setVisibleRegion(visible);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-        visible.dump(LOG_TAG);
-    }
-    outputLayer->editState().visibleRegion = visible;
-
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-
-    error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-        surfaceDamageRegion.dump(LOG_TAG);
-    }
-    layerCompositionState.surfaceDamage = surfaceDamageRegion;
+void BufferLayer::preparePerFrameCompositionState() {
+    Layer::preparePerFrameCompositionState();
 
     // Sideband layers
-    if (layerCompositionState.sidebandStream.get()) {
-        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::SIDEBAND);
-        ALOGV("[%s] Requesting Sideband composition", mName.string());
-        error = hwcLayer->setSidebandStream(layerCompositionState.sidebandStream->handle());
-        if (error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mName.string(),
-                  layerCompositionState.sidebandStream->handle(), to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-        layerCompositionState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
+    auto* compositionState = editCompositionState();
+    if (compositionState->sidebandStream.get()) {
+        compositionState->compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
         return;
-    }
-
-    // Device or Cursor layers
-    if (mPotentialCursor) {
-        ALOGV("[%s] Requesting Cursor composition", mName.string());
-        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::CURSOR);
     } else {
-        ALOGV("[%s] Requesting Device composition", mName.string());
-        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::DEVICE);
+        // Normal buffer layers
+        compositionState->hdrMetadata = mBufferInfo.mHdrMetadata;
+        compositionState->compositionType = mPotentialCursor
+                ? Hwc2::IComposerClient::Composition::CURSOR
+                : Hwc2::IComposerClient::Composition::DEVICE;
     }
 
-    ui::Dataspace dataspace = isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN
-            ? targetDataspace
-            : mCurrentDataSpace;
-    error = hwcLayer->setDataspace(dataspace);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace,
-              to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-
-    const HdrMetadata& metadata = getDrawingHdrMetadata();
-    error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata, metadata);
-    if (error != HWC2::Error::None && error != HWC2::Error::Unsupported) {
-        ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mName.string(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-
-    error = hwcLayer->setColorTransform(getColorTransform());
-    if (error == HWC2::Error::Unsupported) {
-        // If per layer color transform is not supported, we use GPU composition.
-        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::CLIENT);
-    } else if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(),
-                to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-    layerCompositionState.dataspace = mCurrentDataSpace;
-    layerCompositionState.colorTransform = getColorTransform();
-    layerCompositionState.hdrMetadata = metadata;
-
-    setHwcLayerBuffer(displayDevice);
+    compositionState->buffer = mBufferInfo.mBuffer;
+    compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
+            ? 0
+            : mBufferInfo.mBufferSlot;
+    compositionState->acquireFence = mBufferInfo.mFence;
 }
 
 bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) {
-    if (mBufferLatched) {
+    if (mBufferInfo.mBuffer != nullptr) {
         Mutex::Autolock lock(mFrameEventHistoryMutex);
         mFrameEventHistory.addPreComposition(mCurrentFrameNumber, refreshStartTime);
     }
@@ -355,29 +319,38 @@
     return hasReadyFrame();
 }
 
-bool BufferLayer::onPostComposition(const std::optional<DisplayId>& displayId,
+bool BufferLayer::onPostComposition(const DisplayDevice* display,
                                     const std::shared_ptr<FenceTime>& glDoneFence,
                                     const std::shared_ptr<FenceTime>& presentFence,
                                     const CompositorTiming& compositorTiming) {
     // mFrameLatencyNeeded is true when a new frame was latched for the
     // composition.
-    if (!mFrameLatencyNeeded) return false;
+    if (!mBufferInfo.mFrameLatencyNeeded) return false;
 
     // Update mFrameEventHistory.
     {
         Mutex::Autolock lock(mFrameEventHistoryMutex);
         mFrameEventHistory.addPostComposition(mCurrentFrameNumber, glDoneFence, presentFence,
                                               compositorTiming);
+        finalizeFrameEventHistory(glDoneFence, compositorTiming);
     }
 
     // Update mFrameTracker.
-    nsecs_t desiredPresentTime = getDesiredPresentTime();
+    nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
     mFrameTracker.setDesiredPresentTime(desiredPresentTime);
 
-    const int32_t layerID = getSequence();
-    mFlinger->mTimeStats->setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
 
-    std::shared_ptr<FenceTime> frameReadyFence = getCurrentFenceTime();
+    const auto outputLayer = findOutputLayerForDisplay(display);
+    if (outputLayer && outputLayer->requiresClientComposition()) {
+        nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
+        mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+                                               clientCompositionTimestamp,
+                                               FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
+    }
+
+    std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
     if (frameReadyFence->isValid()) {
         mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
     } else {
@@ -387,22 +360,37 @@
     }
 
     if (presentFence->isValid()) {
-        mFlinger->mTimeStats->setPresentFence(layerID, mCurrentFrameNumber, presentFence);
+        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);
-        mFlinger->mTimeStats->setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
+        mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime);
+        mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+                                               actualPresentTime,
+                                               FrameTracer::FrameEvent::PRESENT_FENCE);
         mFrameTracker.setActualPresentTime(actualPresentTime);
     }
 
     mFrameTracker.advanceFrame();
-    mFrameLatencyNeeded = false;
+    mBufferInfo.mFrameLatencyNeeded = false;
     return true;
 }
 
-bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
+void BufferLayer::gatherBufferInfo() {
+    mBufferInfo.mPixelFormat =
+            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->format;
+    mBufferInfo.mFrameLatencyNeeded = true;
+}
+
+bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                              nsecs_t expectedPresentTime) {
     ATRACE_CALL();
 
     bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
@@ -435,14 +423,15 @@
     // Capture the old state of the layer for comparisons later
     const State& s(getDrawingState());
     const bool oldOpacity = isOpaque(s);
-    sp<GraphicBuffer> oldBuffer = mActiveBuffer;
 
-    if (!allTransactionsSignaled()) {
+    BufferInfo oldBufferInfo = mBufferInfo;
+
+    if (!allTransactionsSignaled(expectedPresentTime)) {
         mFlinger->setTransactionFlags(eTraversalNeeded);
         return false;
     }
 
-    status_t err = updateTexImage(recomputeVisibleRegions, latchTime);
+    status_t err = updateTexImage(recomputeVisibleRegions, latchTime, expectedPresentTime);
     if (err != NO_ERROR) {
         return false;
     }
@@ -452,65 +441,32 @@
         return false;
     }
 
-    mBufferLatched = true;
-
     err = updateFrameNumber(latchTime);
     if (err != NO_ERROR) {
         return false;
     }
 
+    gatherBufferInfo();
+
     mRefreshPending = true;
-    mFrameLatencyNeeded = true;
-    if (oldBuffer == nullptr) {
+    if (oldBufferInfo.mBuffer == nullptr) {
         // the first time we receive a buffer, we need to trigger a
         // geometry invalidation.
         recomputeVisibleRegions = true;
     }
 
-    ui::Dataspace dataSpace = getDrawingDataSpace();
-    // translate legacy dataspaces to modern dataspaces
-    switch (dataSpace) {
-        case ui::Dataspace::SRGB:
-            dataSpace = ui::Dataspace::V0_SRGB;
-            break;
-        case ui::Dataspace::SRGB_LINEAR:
-            dataSpace = ui::Dataspace::V0_SRGB_LINEAR;
-            break;
-        case ui::Dataspace::JFIF:
-            dataSpace = ui::Dataspace::V0_JFIF;
-            break;
-        case ui::Dataspace::BT601_625:
-            dataSpace = ui::Dataspace::V0_BT601_625;
-            break;
-        case ui::Dataspace::BT601_525:
-            dataSpace = ui::Dataspace::V0_BT601_525;
-            break;
-        case ui::Dataspace::BT709:
-            dataSpace = ui::Dataspace::V0_BT709;
-            break;
-        default:
-            break;
-    }
-    mCurrentDataSpace = dataSpace;
-
-    Rect crop(getDrawingCrop());
-    const uint32_t transform(getDrawingTransform());
-    const uint32_t scalingMode(getDrawingScalingMode());
-    const bool transformToDisplayInverse(getTransformToDisplayInverse());
-    if ((crop != mCurrentCrop) || (transform != mCurrentTransform) ||
-        (scalingMode != mCurrentScalingMode) ||
-        (transformToDisplayInverse != mTransformToDisplayInverse)) {
-        mCurrentCrop = crop;
-        mCurrentTransform = transform;
-        mCurrentScalingMode = scalingMode;
-        mTransformToDisplayInverse = transformToDisplayInverse;
+    if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) ||
+        (mBufferInfo.mTransform != oldBufferInfo.mTransform) ||
+        (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) ||
+        (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) {
         recomputeVisibleRegions = true;
     }
 
-    if (oldBuffer != nullptr) {
-        uint32_t bufWidth = mActiveBuffer->getWidth();
-        uint32_t bufHeight = mActiveBuffer->getHeight();
-        if (bufWidth != uint32_t(oldBuffer->width) || bufHeight != uint32_t(oldBuffer->height)) {
+    if (oldBufferInfo.mBuffer != nullptr) {
+        uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+        uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+        if (bufWidth != uint32_t(oldBufferInfo.mBuffer->width) ||
+            bufHeight != uint32_t(oldBufferInfo.mBuffer->height)) {
             recomputeVisibleRegions = true;
         }
     }
@@ -547,10 +503,10 @@
 }
 
 // transaction
-void BufferLayer::notifyAvailableFrames() {
-    const auto headFrameNumber = getHeadFrameNumber();
+void BufferLayer::notifyAvailableFrames(nsecs_t expectedPresentTime) {
+    const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime);
     const bool headFenceSignaled = fenceHasSignaled();
-    const bool presentTimeIsCurrent = framePresentTimeIsCurrent();
+    const bool presentTimeIsCurrent = framePresentTimeIsCurrent(expectedPresentTime);
     Mutex::Autolock lock(mLocalSyncPointMutex);
     for (auto& point : mLocalSyncPoints) {
         if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled &&
@@ -575,11 +531,11 @@
         return mOverrideScalingMode;
     }
 
-    return mCurrentScalingMode;
+    return mBufferInfo.mScaleMode;
 }
 
 bool BufferLayer::isProtected() const {
-    const sp<GraphicBuffer>& buffer(mActiveBuffer);
+    const sp<GraphicBuffer>& buffer(mBufferInfo.mBuffer);
     return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
 }
 
@@ -598,8 +554,8 @@
 }
 
 // h/w composer set-up
-bool BufferLayer::allTransactionsSignaled() {
-    auto headFrameNumber = getHeadFrameNumber();
+bool BufferLayer::allTransactionsSignaled(nsecs_t expectedPresentTime) {
+    const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime);
     bool matchingFramesFound = false;
     bool allTransactionsApplied = true;
     Mutex::Autolock lock(mLocalSyncPointMutex);
@@ -646,28 +602,51 @@
     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, we
-    // only return mNeedsFiltering
-    if (displayDevice == nullptr) {
-        return mNeedsFiltering;
-    }
-
-    const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+bool BufferLayer::needsFiltering(const DisplayDevice* display) const {
+    const auto outputLayer = findOutputLayerForDisplay(display);
     if (outputLayer == nullptr) {
-        return mNeedsFiltering;
+        return false;
     }
 
+    // We need filtering if the sourceCrop rectangle size does not match the
+    // displayframe rectangle size (not a 1:1 render)
     const auto& compositionState = outputLayer->getState();
     const auto displayFrame = compositionState.displayFrame;
     const auto sourceCrop = compositionState.sourceCrop;
-    return mNeedsFiltering || sourceCrop.getHeight() != displayFrame.getHeight() ||
+    return sourceCrop.getHeight() != displayFrame.getHeight() ||
             sourceCrop.getWidth() != displayFrame.getWidth();
 }
 
-uint64_t BufferLayer::getHeadFrameNumber() const {
+bool BufferLayer::needsFilteringForScreenshots(const DisplayDevice* display,
+                                               const ui::Transform& inverseParentTransform) const {
+    const auto outputLayer = findOutputLayerForDisplay(display);
+    if (outputLayer == nullptr) {
+        return false;
+    }
+
+    // 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 = display->getTransform();
+    const ui::Transform inverseTransform = inverseParentTransform * displayTransform.inverse();
+    // Undo the transformation of the displayFrame so that we're back into
+    // layer-stack space.
+    const Rect frame = inverseTransform.transform(compositionState.displayFrame);
+    const FloatRect sourceCrop = compositionState.sourceCrop;
+
+    int32_t frameHeight = frame.getHeight();
+    int32_t frameWidth = frame.getWidth();
+    // If the display transform had a rotational component then undo the
+    // rotation so that the orientation matches the source crop.
+    if (displayTransform.getOrientation() & ui::Transform::ROT_90) {
+        std::swap(frameHeight, frameWidth);
+    }
+    return sourceCrop.getHeight() != frameHeight || sourceCrop.getWidth() != frameWidth;
+}
+
+uint64_t BufferLayer::getHeadFrameNumber(nsecs_t expectedPresentTime) const {
     if (hasFrameUpdate()) {
-        return getFrameNumber();
+        return getFrameNumber(expectedPresentTime);
     } else {
         return mCurrentFrameNumber;
     }
@@ -681,20 +660,20 @@
         return Rect(getActiveWidth(s), getActiveHeight(s));
     }
 
-    if (mActiveBuffer == nullptr) {
+    if (mBufferInfo.mBuffer == nullptr) {
         return Rect::INVALID_RECT;
     }
 
-    uint32_t bufWidth = mActiveBuffer->getWidth();
-    uint32_t bufHeight = mActiveBuffer->getHeight();
+    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
 
     // Undo any transformations on the buffer and return the result.
-    if (mCurrentTransform & ui::Transform::ROT_90) {
+    if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
         std::swap(bufWidth, bufHeight);
     }
 
     if (getTransformToDisplayInverse()) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
         if (invTransform & ui::Transform::ROT_90) {
             std::swap(bufWidth, bufHeight);
         }
@@ -703,10 +682,6 @@
     return Rect(bufWidth, bufHeight);
 }
 
-std::shared_ptr<compositionengine::Layer> BufferLayer::getCompositionLayer() const {
-    return mCompositionLayer;
-}
-
 FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const {
     const State& s(getDrawingState());
 
@@ -717,20 +692,20 @@
         return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
     }
 
-    if (mActiveBuffer == nullptr) {
+    if (mBufferInfo.mBuffer == nullptr) {
         return parentBounds;
     }
 
-    uint32_t bufWidth = mActiveBuffer->getWidth();
-    uint32_t bufHeight = mActiveBuffer->getHeight();
+    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
 
     // Undo any transformations on the buffer and return the result.
-    if (mCurrentTransform & ui::Transform::ROT_90) {
+    if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
         std::swap(bufWidth, bufHeight);
     }
 
     if (getTransformToDisplayInverse()) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
         if (invTransform & ui::Transform::ROT_90) {
             std::swap(bufWidth, bufHeight);
         }
@@ -739,6 +714,132 @@
     return FloatRect(0, 0, bufWidth, bufHeight);
 }
 
+void BufferLayer::latchAndReleaseBuffer() {
+    mRefreshPending = false;
+    if (hasReadyFrame()) {
+        bool ignored = false;
+        latchBuffer(ignored, systemTime(), 0 /* expectedPresentTime */);
+    }
+    releasePendingBuffer(systemTime());
+}
+
+PixelFormat BufferLayer::getPixelFormat() const {
+    return mBufferInfo.mPixelFormat;
+}
+
+bool BufferLayer::getTransformToDisplayInverse() const {
+    return mBufferInfo.mTransformToDisplayInverse;
+}
+
+Rect BufferLayer::getBufferCrop() const {
+    // this is the crop rectangle that applies to the buffer
+    // itself (as opposed to the window)
+    if (!mBufferInfo.mCrop.isEmpty()) {
+        // if the buffer crop is defined, we use that
+        return mBufferInfo.mCrop;
+    } else if (mBufferInfo.mBuffer != nullptr) {
+        // otherwise we use the whole buffer
+        return mBufferInfo.mBuffer->getBounds();
+    } else {
+        // if we don't have a buffer yet, we use an empty/invalid crop
+        return Rect();
+    }
+}
+
+uint32_t BufferLayer::getBufferTransform() const {
+    return mBufferInfo.mTransform;
+}
+
+ui::Dataspace BufferLayer::getDataSpace() const {
+    return mBufferInfo.mDataspace;
+}
+
+ui::Dataspace BufferLayer::translateDataspace(ui::Dataspace dataspace) {
+    ui::Dataspace updatedDataspace = dataspace;
+    // translate legacy dataspaces to modern dataspaces
+    switch (dataspace) {
+        case ui::Dataspace::SRGB:
+            updatedDataspace = ui::Dataspace::V0_SRGB;
+            break;
+        case ui::Dataspace::SRGB_LINEAR:
+            updatedDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+            break;
+        case ui::Dataspace::JFIF:
+            updatedDataspace = ui::Dataspace::V0_JFIF;
+            break;
+        case ui::Dataspace::BT601_625:
+            updatedDataspace = ui::Dataspace::V0_BT601_625;
+            break;
+        case ui::Dataspace::BT601_525:
+            updatedDataspace = ui::Dataspace::V0_BT601_525;
+            break;
+        case ui::Dataspace::BT709:
+            updatedDataspace = ui::Dataspace::V0_BT709;
+            break;
+        default:
+            break;
+    }
+
+    return updatedDataspace;
+}
+
+sp<GraphicBuffer> BufferLayer::getBuffer() const {
+    return mBufferInfo.mBuffer;
+}
+
+void BufferLayer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) {
+    GLConsumer::computeTransformMatrix(outMatrix, mBufferInfo.mBuffer, mBufferInfo.mCrop,
+                                       mBufferInfo.mTransform, filteringEnabled);
+}
+
+void BufferLayer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
+    Layer::setInitialValuesForClone(clonedFrom);
+
+    sp<BufferLayer> bufferClonedFrom = static_cast<BufferLayer*>(clonedFrom.get());
+    mPremultipliedAlpha = bufferClonedFrom->mPremultipliedAlpha;
+    mPotentialCursor = bufferClonedFrom->mPotentialCursor;
+    mProtectedByApp = bufferClonedFrom->mProtectedByApp;
+
+    updateCloneBufferInfo();
+}
+
+void BufferLayer::updateCloneBufferInfo() {
+    if (!isClone() || !isClonedFromAlive()) {
+        return;
+    }
+
+    sp<BufferLayer> clonedFrom = static_cast<BufferLayer*>(getClonedFrom().get());
+    mBufferInfo = clonedFrom->mBufferInfo;
+    mSidebandStream = clonedFrom->mSidebandStream;
+    surfaceDamageRegion = clonedFrom->surfaceDamageRegion;
+    mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load();
+    mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber;
+
+    // After buffer info is updated, the drawingState from the real layer needs to be copied into
+    // the cloned. This is because some properties of drawingState can change when latchBuffer is
+    // called. However, copying the drawingState would also overwrite the cloned layer's relatives
+    // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in
+    // the cloned drawingState again.
+    wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
+    SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
+    wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
+    InputWindowInfo tmpInputInfo = mDrawingState.inputInfo;
+
+    mDrawingState = clonedFrom->mDrawingState;
+
+    mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
+    mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
+    mDrawingState.zOrderRelatives = tmpZOrderRelatives;
+    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_)
@@ -748,3 +849,6 @@
 #if defined(__gl2_h_)
 #error "don't include gl2/gl2.h in this file"
 #endif
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 6828938..26bfb49 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -54,7 +54,8 @@
     // Overriden from Layer
     // -----------------------------------------------------------------------
 public:
-    std::shared_ptr<compositionengine::Layer> getCompositionLayer() const override;
+    sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
+    compositionengine::LayerFECompositionState* editCompositionState() override;
 
     // If we have received a new buffer this frame, we will pass its surface
     // damage down to hardware composer. Otherwise, we must send a region with
@@ -62,10 +63,6 @@
     void useSurfaceDamage() override;
     void useEmptyDamage() override;
 
-    // getTypeId - Provide unique string for each class type in the Layer
-    // hierarchy
-    const char* getTypeId() const override { return "BufferLayer"; }
-
     bool isOpaque(const Layer::State& s) const override;
 
     // isVisible - true if this layer is visible, false otherwise
@@ -82,53 +79,59 @@
 
     bool isHdrY410() const override;
 
-    void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
-                         const Rect& viewport, int32_t supportedPerFrameMetadata,
-                         const ui::Dataspace targetDataspace) override;
-
-    bool onPreComposition(nsecs_t refreshStartTime) override;
-    bool onPostComposition(const std::optional<DisplayId>& displayId,
-                           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
     // operation, so this should be set only if needed). Typically this is used
     // to figure out if the content or size of a surface has changed.
-    bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
+    bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                     nsecs_t expectedPresentTime) override;
 
     bool isBufferLatched() const override { return mRefreshPending; }
 
-    void notifyAvailableFrames() override;
+    void notifyAvailableFrames(nsecs_t expectedPresentTime) override;
 
     bool hasReadyFrame() const override;
 
     // Returns the current scaling mode, unless mOverrideScalingMode
     // is set, in which case, it returns mOverrideScalingMode
     uint32_t getEffectiveScalingMode() const override;
-    // -----------------------------------------------------------------------
+
+    // Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
+    // This is used if the buffer is just latched and releases to free up the buffer
+    // and will not be shown on screen.
+    // Should only be called on the main thread.
+    void latchAndReleaseBuffer() override;
+
+    bool getTransformToDisplayInverse() const override;
+
+    Rect getBufferCrop() const override;
+
+    uint32_t getBufferTransform() const override;
+
+    ui::Dataspace getDataSpace() const override;
+
+    sp<GraphicBuffer> getBuffer() const override;
+
+    ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; }
 
     // -----------------------------------------------------------------------
     // Functions that must be implemented by derived classes
     // -----------------------------------------------------------------------
 private:
     virtual bool fenceHasSignaled() const = 0;
-    virtual bool framePresentTimeIsCurrent() const = 0;
+    virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
 
-    virtual nsecs_t getDesiredPresentTime() = 0;
-    virtual std::shared_ptr<FenceTime> getCurrentFenceTime() const = 0;
+    PixelFormat getPixelFormat() const;
 
-    virtual void getDrawingTransformMatrix(float *matrix) = 0;
-    virtual uint32_t getDrawingTransform() const = 0;
-    virtual ui::Dataspace getDrawingDataSpace() const = 0;
-    virtual Rect getDrawingCrop() const = 0;
-    virtual uint32_t getDrawingScalingMode() const = 0;
-    virtual Region getDrawingSurfaceDamage() const = 0;
-    virtual const HdrMetadata& getDrawingHdrMetadata() const = 0;
-    virtual int getDrawingApi() const = 0;
+    // Computes the transform matrix using the setFilteringEnabled to determine whether the
+    // transform matrix should be computed for use with bilinear filtering.
+    void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]);
 
-    virtual uint64_t getFrameNumber() const = 0;
+    virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
 
     virtual bool getAutoRefresh() const = 0;
     virtual bool getSidebandStreamChanged() const = 0;
@@ -138,60 +141,93 @@
 
     virtual bool hasFrameUpdate() const = 0;
 
-    virtual void setFilteringEnabled(bool enabled) = 0;
-
     virtual status_t bindTextureImage() = 0;
-    virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) = 0;
+    virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                                    nsecs_t expectedPresentTime) = 0;
 
     virtual status_t updateActiveBuffer() = 0;
     virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
 
-    virtual void setHwcLayerBuffer(const sp<const DisplayDevice>& displayDevice) = 0;
+    // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they
+    // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion
+    // detection.
+    bool needsInputInfo() const override { return !mPotentialCursor; }
 
 protected:
+    struct BufferInfo {
+        nsecs_t mDesiredPresentTime;
+        std::shared_ptr<FenceTime> mFenceTime;
+        sp<Fence> mFence;
+        uint32_t mTransform{0};
+        ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
+        Rect mCrop;
+        uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
+        Region mSurfaceDamage;
+        HdrMetadata mHdrMetadata;
+        int mApi;
+        PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
+        bool mTransformToDisplayInverse{false};
+
+        sp<GraphicBuffer> mBuffer;
+        int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
+
+        bool mFrameLatencyNeeded{false};
+    };
+
+    BufferInfo mBufferInfo;
+    virtual void gatherBufferInfo() = 0;
+
+    std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
+            compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
+
+    /*
+     * compositionengine::LayerFE overrides
+     */
+    const compositionengine::LayerFECompositionState* getCompositionState() const override;
+    bool onPreComposition(nsecs_t) override;
+    void preparePerFrameCompositionState() override;
+
     // Loads the corresponding system property once per process
     static bool latchUnsignaledBuffers();
 
     // Check all of the local sync points to ensure that all transactions
     // which need to have been applied prior to the frame which is about to
     // be latched have signaled
-    bool allTransactionsSignaled();
+    bool allTransactionsSignaled(nsecs_t expectedPresentTime);
 
     static bool getOpacityForFormat(uint32_t format);
 
-    // from GLES
+    // from graphics API
     const uint32_t mTextureName;
 
     bool mRefreshPending{false};
 
-    // prepareClientLayer - constructs a RenderEngine layer for GPU composition.
-    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                            bool useIdentityTransform, Region& clearRegion,
-                            const bool supportProtectedContent,
-                            renderengine::LayerSettings& layer) override;
+    ui::Dataspace translateDataspace(ui::Dataspace dataspace);
+    void setInitialValuesForClone(const sp<Layer>& clonedFrom);
+    void updateCloneBufferInfo() override;
+    uint64_t mPreviousFrameNumber = 0;
+
+    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;
-
-    uint64_t getHeadFrameNumber() const;
-
-    uint32_t mCurrentScalingMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
-
-    bool mTransformToDisplayInverse{false};
-
-    // main thread.
-    bool mBufferLatched{false}; // TODO: Use mActiveBuffer?
+    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
     // and its parent layer is not bounded
     Rect getBufferSize(const State& s) const override;
 
-    std::shared_ptr<compositionengine::Layer> mCompositionLayer;
+    std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
 
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
-
-    PixelFormat getPixelFormat() const;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 414814a..8722952 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "BufferLayerConsumer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -403,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;
 }
 
@@ -428,30 +441,6 @@
     return mCurrentFenceTime;
 }
 
-status_t BufferLayerConsumer::doFenceWaitLocked() const {
-    if (mCurrentFence->isValid()) {
-        if (mRE.useWaitSync()) {
-            base::unique_fd fenceFd(mCurrentFence->dup());
-            if (fenceFd == -1) {
-                BLC_LOGE("doFenceWait: error dup'ing fence fd: %d", errno);
-                return -errno;
-            }
-            if (!mRE.waitFence(std::move(fenceFd))) {
-                BLC_LOGE("doFenceWait: failed to wait on fence fd");
-                return UNKNOWN_ERROR;
-            }
-        } else {
-            status_t err = mCurrentFence->waitForever("BufferLayerConsumer::doFenceWaitLocked");
-            if (err != NO_ERROR) {
-                BLC_LOGE("doFenceWait: error waiting for fence: %d", err);
-                return err;
-            }
-        }
-    }
-
-    return NO_ERROR;
-}
-
 void BufferLayerConsumer::freeBufferLocked(int slotIndex) {
     BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
     std::lock_guard<std::mutex> lock(mImagesMutex);
@@ -463,10 +452,14 @@
 }
 
 void BufferLayerConsumer::onDisconnect() {
-    sp<Layer> l = mLayer.promote();
-    if (l.get()) {
-        l->onDisconnect();
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        // Nothing to do if we're already abandoned.
+        return;
     }
+
+    mLayer->onDisconnect();
 }
 
 void BufferLayerConsumer::onSidebandStreamChanged() {
@@ -500,10 +493,14 @@
 
 void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
                                                    FrameEventHistoryDelta* outDelta) {
-    sp<Layer> l = mLayer.promote();
-    if (l.get()) {
-        l->addAndGetFrameTimestamps(newTimestamps, outDelta);
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        // Nothing to do if we're already abandoned.
+        return;
     }
+
+    mLayer->addAndGetFrameTimestamps(newTimestamps, outDelta);
 }
 
 void BufferLayerConsumer::abandonLocked() {
@@ -543,3 +540,6 @@
     }
 }
 }; // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index 617b1c2..5e3044f 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -254,11 +254,6 @@
     // mCurrentTextureImage must not be nullptr.
     void computeCurrentTransformMatrixLocked();
 
-    // doFenceWaitLocked inserts a wait command into the RenderEngine command
-    // stream to ensure that it is safe for future RenderEngine commands to
-    // access the current texture buffer.
-    status_t doFenceWaitLocked() const;
-
     // getCurrentCropLocked returns the cropping rectangle of the current buffer.
     Rect getCurrentCropLocked() const;
 
@@ -336,8 +331,8 @@
     // construction time.
     const uint32_t mTexName;
 
-    // The layer for this BufferLayerConsumer
-    const wp<Layer> mLayer;
+    // The layer for this BufferLayerConsumer. Always check mAbandoned before accessing.
+    Layer* mLayer GUARDED_BY(mMutex);
 
     wp<ContentsChangedListener> mContentsChangedListener;
 
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 1de7afb..6e4235e 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -14,21 +14,24 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "BufferQueueLayer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
-#include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include "BufferQueueLayer.h"
+
+#include <compositionengine/LayerFECompositionState.h>
 #include <gui/BufferQueueConsumer.h>
 #include <system/window.h>
 
-#include "BufferQueueLayer.h"
 #include "LayerRejecter.h"
 #include "SurfaceInterceptor.h"
 
+#include "FrameTracer/FrameTracer.h"
+#include "Scheduler/LayerHistory.h"
 #include "TimeStats/TimeStats.h"
 
 namespace android {
@@ -36,6 +39,7 @@
 BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
 
 BufferQueueLayer::~BufferQueueLayer() {
+    mContentsChangedListener->abandon();
     mConsumer->abandon();
 }
 
@@ -45,26 +49,31 @@
 
 void BufferQueueLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
     mConsumer->setReleaseFence(releaseFence);
+
+    // Prevent tracing the same release multiple times.
+    if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
+        mFlinger->mFrameTracer->traceFence(getSequence(), mPreviousBufferId, mPreviousFrameNumber,
+                                           std::make_shared<FenceTime>(releaseFence),
+                                           FrameTracer::FrameEvent::RELEASE_FENCE);
+        mPreviousReleasedFrameNumber = mPreviousFrameNumber;
+    }
 }
 
-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) {
     std::vector<OccupancyTracker::Segment> history;
     status_t result = mConsumer->getOccupancyHistory(forceFlush, &history);
     if (result != NO_ERROR) {
-        ALOGW("[%s] Failed to obtain occupancy history (%d)", mName.string(), result);
+        ALOGW("[%s] Failed to obtain occupancy history (%d)", getDebugName(), result);
         return {};
     }
     return history;
 }
 
-bool BufferQueueLayer::getTransformToDisplayInverse() const {
-    return mConsumer->getTransformToDisplayInverse();
-}
-
 void BufferQueueLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
     if (!mConsumer->releasePendingBuffer()) {
         return;
@@ -107,7 +116,11 @@
     ALOGW_IF(!isPlausible,
              "[%s] Timestamp %" PRId64 " seems implausible "
              "relative to expectedPresent %" PRId64,
-             mName.string(), addedTime, expectedPresentTime);
+             getDebugName(), addedTime, expectedPresentTime);
+
+    if (!isPlausible) {
+        mFlinger->mTimeStats->incrementBadDesiredPresent(getSequence());
+    }
 
     const bool isDue = addedTime < expectedPresentTime;
     return isDue || !isPlausible;
@@ -134,71 +147,30 @@
         // able to be latched. To avoid this, grab this buffer anyway.
         return true;
     }
-    return mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+    const bool fenceSignaled =
+            mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+    if (!fenceSignaled) {
+        mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
+                                                    TimeStats::LatchSkipReason::LateAcquire);
+    }
+
+    return fenceSignaled;
 }
 
-bool BufferQueueLayer::framePresentTimeIsCurrent() const {
+bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
     if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
         return true;
     }
 
     Mutex::Autolock lock(mQueueItemLock);
-    return mQueueItems[0].mTimestamp <= mFlinger->getExpectedPresentTime();
+    return mQueueItems[0].mTimestamp <= expectedPresentTime;
 }
 
-nsecs_t BufferQueueLayer::getDesiredPresentTime() {
-    return mConsumer->getTimestamp();
-}
-
-std::shared_ptr<FenceTime> BufferQueueLayer::getCurrentFenceTime() const {
-    return mConsumer->getCurrentFenceTime();
-}
-
-void BufferQueueLayer::getDrawingTransformMatrix(float *matrix) {
-    return mConsumer->getTransformMatrix(matrix);
-}
-
-// NOTE: SurfaceFlinger's definitions of "Current" and "Drawing" do not neatly map to BufferQueue's
-// These functions get the fields for the frame that is currently in SurfaceFlinger's Drawing state
-// so the functions start with "getDrawing". The data is retrieved from the BufferQueueConsumer's
-// current buffer so the consumer functions start with "getCurrent".
-//
-// This results in the rather confusing functions below.
-uint32_t BufferQueueLayer::getDrawingTransform() const {
-    return mConsumer->getCurrentTransform();
-}
-
-ui::Dataspace BufferQueueLayer::getDrawingDataSpace() const {
-    return mConsumer->getCurrentDataSpace();
-}
-
-Rect BufferQueueLayer::getDrawingCrop() const {
-    return mConsumer->getCurrentCrop();
-}
-
-uint32_t BufferQueueLayer::getDrawingScalingMode() const {
-    return mConsumer->getCurrentScalingMode();
-}
-
-Region BufferQueueLayer::getDrawingSurfaceDamage() const {
-    return mConsumer->getSurfaceDamage();
-}
-
-const HdrMetadata& BufferQueueLayer::getDrawingHdrMetadata() const {
-    return mConsumer->getCurrentHdrMetadata();
-}
-
-int BufferQueueLayer::getDrawingApi() const {
-    return mConsumer->getCurrentApi();
-}
-
-uint64_t BufferQueueLayer::getFrameNumber() const {
+uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const {
     Mutex::Autolock lock(mQueueItemLock);
     uint64_t frameNumber = mQueueItems[0].mFrameNumber;
 
     // The head of the queue will be dropped if there are signaled and timely frames behind it
-    nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime();
-
     if (isRemovedFromCurrentState()) {
         expectedPresentTime = 0;
     }
@@ -240,9 +212,9 @@
     if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
         // mSidebandStreamChanged was changed to false
         mSidebandStream = mConsumer->getSidebandStream();
-        auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-        layerCompositionState.sidebandStream = mSidebandStream;
-        if (layerCompositionState.sidebandStream != nullptr) {
+        auto* layerCompositionState = editCompositionState();
+        layerCompositionState->sidebandStream = mSidebandStream;
+        if (layerCompositionState->sidebandStream != nullptr) {
             setTransactionFlags(eTransactionNeeded);
             mFlinger->setTransactionFlags(eTraversalNeeded);
         }
@@ -257,26 +229,21 @@
     return mQueuedFrames > 0;
 }
 
-void BufferQueueLayer::setFilteringEnabled(bool enabled) {
-    return mConsumer->setFilteringEnabled(enabled);
-}
-
 status_t BufferQueueLayer::bindTextureImage() {
     return mConsumer->bindTextureImage();
 }
 
-status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) {
+status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                                          nsecs_t expectedPresentTime) {
     // This boolean is used to make sure that SurfaceFlinger's shadow copy
     // of the buffer queue isn't modified when the buffer queue is returning
     // BufferItem's that weren't actually queued. This can happen in shared
     // buffer mode.
     bool queuedBuffer = false;
-    const int32_t layerID = getSequence();
+    const int32_t layerId = getSequence();
     LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
-                    getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode,
-                    getTransformToDisplayInverse(), mFreezeGeometryUpdates);
-
-    nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime();
+                    getProducerStickyTransform() != 0, mName, mOverrideScalingMode,
+                    getTransformToDisplayInverse());
 
     if (isRemovedFromCurrentState()) {
         expectedPresentTime = 0;
@@ -314,7 +281,7 @@
         if (queuedBuffer) {
             Mutex::Autolock lock(mQueueItemLock);
             mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
-            mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
             mQueueItems.removeAt(0);
             mQueuedFrames--;
         }
@@ -328,7 +295,8 @@
             Mutex::Autolock lock(mQueueItemLock);
             mQueueItems.clear();
             mQueuedFrames = 0;
-            mFlinger->mTimeStats->onDestroy(layerID);
+            mFlinger->mTimeStats->onDestroy(layerId);
+            mFlinger->mFrameTracer->onDestroy(layerId);
         }
 
         // Once we have hit this state, the shadow queue may no longer
@@ -350,14 +318,15 @@
         // updateTexImage
         while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
             mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
-            mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
             mQueueItems.removeAt(0);
             mQueuedFrames--;
         }
 
-        mFlinger->mTimeStats->setAcquireFence(layerID, currentFrameNumber,
-                                              mQueueItems[0].mFenceTime);
-        mFlinger->mTimeStats->setLatchTime(layerID, currentFrameNumber, latchTime);
+        uint64_t bufferID = mQueueItems[0].mGraphicBuffer->getId();
+        mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime);
+        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime,
+                                               FrameTracer::FrameEvent::LATCH);
 
         mQueueItems.removeAt(0);
     }
@@ -373,12 +342,11 @@
 
 status_t BufferQueueLayer::updateActiveBuffer() {
     // update the active buffer
-    mActiveBuffer = mConsumer->getCurrentBuffer(&mActiveBufferSlot, &mActiveBufferFence);
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-    layerCompositionState.buffer = mActiveBuffer;
-    layerCompositionState.bufferSlot = mActiveBufferSlot;
+    mPreviousBufferId = getCurrentBufferId();
+    mBufferInfo.mBuffer =
+            mConsumer->getCurrentBuffer(&mBufferInfo.mBufferSlot, &mBufferInfo.mFence);
 
-    if (mActiveBuffer == nullptr) {
+    if (mBufferInfo.mBuffer == nullptr) {
         // this can only happen if the very first buffer was rejected.
         return BAD_VALUE;
     }
@@ -396,55 +364,45 @@
     return NO_ERROR;
 }
 
-void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    LOG_FATAL_IF(!outputLayer->getState.hwc);
-    auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
-
-    uint32_t hwcSlot = 0;
-    sp<GraphicBuffer> hwcBuffer;
-
-    // INVALID_BUFFER_SLOT is used to identify BufferStateLayers.  Default to 0
-    // for BufferQueueLayers
-    int slot = (mActiveBufferSlot == BufferQueue::INVALID_BUFFER_SLOT) ? 0 : mActiveBufferSlot;
-    (*outputLayer->editState().hwc)
-            .hwcBufferCache.getHwcBuffer(slot, mActiveBuffer, &hwcSlot, &hwcBuffer);
-
-    auto acquireFence = mConsumer->getCurrentFence();
-    auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(), mActiveBuffer->handle,
-              to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-    layerCompositionState.bufferSlot = mActiveBufferSlot;
-    layerCompositionState.buffer = mActiveBuffer;
-    layerCompositionState.acquireFence = acquireFence;
-}
-
 // -----------------------------------------------------------------------
 // Interface implementation for BufferLayerConsumer::ContentsChangedListener
 // -----------------------------------------------------------------------
 
-void BufferQueueLayer::fakeVsync() {
-    mRefreshPending = false;
-    bool ignored = false;
-    latchBuffer(ignored, systemTime());
-    usleep(16000);
-    releasePendingBuffer(systemTime());
+void BufferQueueLayer::onFrameDequeued(const uint64_t bufferId) {
+    const int32_t layerId = getSequence();
+    mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+                                           systemTime(), FrameTracer::FrameEvent::DEQUEUE);
+}
+
+void BufferQueueLayer::onFrameDetached(const uint64_t bufferId) {
+    const int32_t layerId = getSequence();
+    mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+                                           systemTime(), FrameTracer::FrameEvent::DETACH);
+}
+
+void BufferQueueLayer::onFrameCancelled(const uint64_t bufferId) {
+    const int32_t layerId = getSequence();
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+                                           systemTime(), FrameTracer::FrameEvent::CANCEL);
 }
 
 void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
+    const int32_t layerId = getSequence();
+    const uint64_t bufferId = item.mGraphicBuffer->getId();
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, item.mFrameNumber, systemTime(),
+                                           FrameTracer::FrameEvent::QUEUE);
+    mFlinger->mFrameTracer->traceFence(layerId, bufferId, item.mFrameNumber,
+                                       std::make_shared<FenceTime>(item.mFence),
+                                       FrameTracer::FrameEvent::ACQUIRE_FENCE);
+
     ATRACE_CALL();
     // Add this buffer from our internal queue tracker
     { // Autolock scope
-        if (mFlinger->mUseSmart90ForVideo) {
-            const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
-            mFlinger->mScheduler->addLayerPresentTimeAndHDR(mSchedulerLayerHandle, presentTime,
-                                                            item.mHdrMetadata.validTypes != 0);
-        }
+        const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
+        mFlinger->mScheduler->recordLayerHistory(this, presentTime,
+                                                 LayerHistory::LayerUpdateType::Buffer);
 
         Mutex::Autolock lock(mQueueItemLock);
         // Reset the frame number tracker when we receive the first buffer after
@@ -457,7 +415,7 @@
         while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
             status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
             if (result != NO_ERROR) {
-                ALOGE("[%s] Timed out waiting on callback", mName.string());
+                ALOGE("[%s] Timed out waiting on callback", getDebugName());
                 break;
             }
         }
@@ -470,16 +428,10 @@
         mQueueItemCondition.broadcast();
     }
 
-    mFlinger->mInterceptor->saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
+    mFlinger->mInterceptor->saveBufferUpdate(layerId, item.mGraphicBuffer->getWidth(),
                                              item.mGraphicBuffer->getHeight(), item.mFrameNumber);
 
-    // If this layer is orphaned, then we run a fake vsync pulse so that
-    // dequeueBuffer doesn't block indefinitely.
-    if (isRemovedFromCurrentState()) {
-        fakeVsync();
-    } else {
-        mFlinger->signalLayerUpdate();
-    }
+    mFlinger->signalLayerUpdate();
     mConsumer->onBufferAvailable(item);
 }
 
@@ -492,7 +444,7 @@
         while (item.mFrameNumber != mLastFrameNumberReceived + 1) {
             status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
             if (result != NO_ERROR) {
-                ALOGE("[%s] Timed out waiting on callback", mName.string());
+                ALOGE("[%s] Timed out waiting on callback", getDebugName());
                 break;
             }
         }
@@ -507,6 +459,14 @@
         mLastFrameNumberReceived = item.mFrameNumber;
         mQueueItemCondition.broadcast();
     }
+
+    const int32_t layerId = getSequence();
+    const uint64_t bufferId = item.mGraphicBuffer->getId();
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, item.mFrameNumber, systemTime(),
+                                           FrameTracer::FrameEvent::QUEUE);
+    mFlinger->mFrameTracer->traceFence(layerId, bufferId, item.mFrameNumber,
+                                       std::make_shared<FenceTime>(item.mFence),
+                                       FrameTracer::FrameEvent::ACQUIRE_FENCE);
     mConsumer->onBufferAvailable(item);
 }
 
@@ -526,26 +486,21 @@
     // Creates a custom BufferQueue for SurfaceFlingerConsumer to use
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
-    BufferQueue::createBufferQueue(&producer, &consumer, true);
-    mProducer = new MonitoredProducer(producer, mFlinger, this);
-    {
-        // Grab the SF state lock during this since it's the only safe way to access RenderEngine
-        Mutex::Autolock lock(mFlinger->mStateLock);
-        mConsumer =
-                new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, this);
-    }
+    mFlinger->getFactory().createBufferQueue(&producer, &consumer, true);
+    mProducer = mFlinger->getFactory().createMonitoredProducer(producer, mFlinger, this);
+    mConsumer =
+            mFlinger->getFactory().createBufferLayerConsumer(consumer, mFlinger->getRenderEngine(),
+                                                             mTextureName, this);
     mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
-    mConsumer->setContentsChangedListener(this);
-    mConsumer->setName(mName);
+
+    mContentsChangedListener = new ContentsChangedListener(this);
+    mConsumer->setContentsChangedListener(mContentsChangedListener);
+    mConsumer->setName(String8(mName.data(), mName.size()));
 
     // BufferQueueCore::mMaxDequeuedBufferCount is default to 1
     if (!mFlinger->isLayerTripleBufferingDisabled()) {
         mProducer->setMaxDequeuedBufferCount(2);
     }
-
-    if (const auto display = mFlinger->getDefaultDisplayDevice()) {
-        updateTransformHint(display);
-    }
 }
 
 status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format) {
@@ -581,4 +536,85 @@
     return static_cast<uint32_t>(producerStickyTransform);
 }
 
+void BufferQueueLayer::gatherBufferInfo() {
+    BufferLayer::gatherBufferInfo();
+
+    mBufferInfo.mDesiredPresentTime = mConsumer->getTimestamp();
+    mBufferInfo.mFenceTime = mConsumer->getCurrentFenceTime();
+    mBufferInfo.mFence = mConsumer->getCurrentFence();
+    mBufferInfo.mTransform = mConsumer->getCurrentTransform();
+    mBufferInfo.mDataspace = translateDataspace(mConsumer->getCurrentDataSpace());
+    mBufferInfo.mCrop = mConsumer->getCurrentCrop();
+    mBufferInfo.mScaleMode = mConsumer->getCurrentScalingMode();
+    mBufferInfo.mSurfaceDamage = mConsumer->getSurfaceDamage();
+    mBufferInfo.mHdrMetadata = mConsumer->getCurrentHdrMetadata();
+    mBufferInfo.mApi = mConsumer->getCurrentApi();
+    mBufferInfo.mTransformToDisplayInverse = mConsumer->getTransformToDisplayInverse();
+}
+
+sp<Layer> BufferQueueLayer::createClone() {
+    LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, LayerMetadata());
+    args.textureName = mTextureName;
+    sp<BufferQueueLayer> layer = mFlinger->getFactory().createBufferQueueLayer(args);
+    layer->setInitialValuesForClone(this);
+
+    return layer;
+}
+
+// -----------------------------------------------------------------------
+// Interface implementation for BufferLayerConsumer::ContentsChangedListener
+// -----------------------------------------------------------------------
+
+void BufferQueueLayer::ContentsChangedListener::onFrameAvailable(const BufferItem& item) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameAvailable(item);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameReplaced(const BufferItem& item) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameReplaced(item);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onSidebandStreamChanged() {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onSidebandStreamChanged();
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameDequeued(const uint64_t bufferId) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameDequeued(bufferId);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameDetached(const uint64_t bufferId) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameDetached(bufferId);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameCancelled(const uint64_t bufferId) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameCancelled(bufferId);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::abandon() {
+    Mutex::Autolock lock(mMutex);
+    mBufferQueueLayer = nullptr;
+}
+
+// -----------------------------------------------------------------------
+
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 2675643..5ebc22d 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -29,8 +29,9 @@
  * This also implements onFrameAvailable(), which notifies SurfaceFlinger
  * that new data has arrived.
  */
-class BufferQueueLayer : public BufferLayer, public BufferLayerConsumer::ContentsChangedListener {
+class BufferQueueLayer : public BufferLayer {
 public:
+    // Only call while mStateLock is held
     explicit BufferQueueLayer(const LayerCreationArgs&);
     ~BufferQueueLayer() override;
 
@@ -38,14 +39,12 @@
     // Interface implementation for Layer
     // -----------------------------------------------------------------------
 public:
+    const char* getType() const override { return "BufferQueueLayer"; }
+
     void onLayerDisplayed(const sp<Fence>& releaseFence) override;
 
-    void setTransformHint(uint32_t orientation) const override;
-
     std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override;
 
-    bool getTransformToDisplayInverse() const override;
-
     // If a buffer was replaced this frame, release the former buffer
     void releasePendingBuffer(nsecs_t dequeueReadyTime) override;
 
@@ -54,6 +53,7 @@
     int32_t getQueuedFrameCount() const override;
 
     bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
+
     // -----------------------------------------------------------------------
 
     // -----------------------------------------------------------------------
@@ -61,47 +61,59 @@
     // -----------------------------------------------------------------------
 public:
     bool fenceHasSignaled() const override;
-    bool framePresentTimeIsCurrent() const override;
+    bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
 
 private:
-    nsecs_t getDesiredPresentTime() override;
-    std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
-
-    void getDrawingTransformMatrix(float *matrix) override;
-    uint32_t getDrawingTransform() const override;
-    ui::Dataspace getDrawingDataSpace() const override;
-    Rect getDrawingCrop() const override;
-    uint32_t getDrawingScalingMode() const override;
-    Region getDrawingSurfaceDamage() const override;
-    const HdrMetadata& getDrawingHdrMetadata() const override;
-    int getDrawingApi() const override;
-
-    uint64_t getFrameNumber() const override;
+    uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
 
     bool getAutoRefresh() const override;
     bool getSidebandStreamChanged() const override;
 
     bool latchSidebandStream(bool& recomputeVisibleRegions) override;
+    void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override;
 
     bool hasFrameUpdate() const override;
 
-    void setFilteringEnabled(bool enabled) override;
-
     status_t bindTextureImage() override;
-    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
+    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                            nsecs_t expectedPresentTime) override;
 
     status_t updateActiveBuffer() override;
     status_t updateFrameNumber(nsecs_t latchTime) override;
 
-    void setHwcLayerBuffer(const sp<const DisplayDevice>& displayDevice) override;
+    sp<Layer> createClone() override;
+
+    void onFrameAvailable(const BufferItem& item);
+    void onFrameReplaced(const BufferItem& item);
+    void onSidebandStreamChanged();
+    void onFrameDequeued(const uint64_t bufferId);
+    void onFrameDetached(const uint64_t bufferId);
+    void onFrameCancelled(const uint64_t bufferId);
+
+protected:
+    void gatherBufferInfo() override;
 
     // -----------------------------------------------------------------------
     // Interface implementation for BufferLayerConsumer::ContentsChangedListener
     // -----------------------------------------------------------------------
-protected:
-    void onFrameAvailable(const BufferItem& item) override;
-    void onFrameReplaced(const BufferItem& item) override;
-    void onSidebandStreamChanged() override;
+    class ContentsChangedListener : public BufferLayerConsumer::ContentsChangedListener {
+    public:
+        ContentsChangedListener(BufferQueueLayer* bufferQueueLayer)
+              : mBufferQueueLayer(bufferQueueLayer) {}
+        void abandon();
+
+    protected:
+        void onFrameAvailable(const BufferItem& item) override;
+        void onFrameReplaced(const BufferItem& item) override;
+        void onSidebandStreamChanged() override;
+        void onFrameDequeued(const uint64_t bufferId) override;
+        void onFrameDetached(const uint64_t bufferId) override;
+        void onFrameCancelled(const uint64_t bufferId) override;
+
+    private:
+        BufferQueueLayer* mBufferQueueLayer = nullptr;
+        Mutex mMutex;
+    };
     // -----------------------------------------------------------------------
 
 public:
@@ -118,10 +130,11 @@
     sp<BufferLayerConsumer> mConsumer;
     sp<IGraphicBufferProducer> mProducer;
 
-    // Only accessed on the main thread.
-    uint64_t mPreviousFrameNumber{0};
     bool mUpdateTexImageFailed{false};
 
+    uint64_t mPreviousBufferId = 0;
+    uint64_t mPreviousReleasedFrameNumber = 0;
+
     // Local copy of the queued contents of the incoming BufferQueue
     mutable Mutex mQueueItemLock;
     Condition mQueueItemCondition;
@@ -129,13 +142,12 @@
     std::atomic<uint64_t> mLastFrameNumberReceived{0};
 
     bool mAutoRefresh{false};
-    int mActiveBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
 
     // thread-safe
     std::atomic<int32_t> mQueuedFrames{0};
     std::atomic<bool> mSidebandStreamChanged{false};
 
-    void fakeVsync();
+    sp<ContentsChangedListener> mContentsChangedListener;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index de5d622..790f2ec 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -14,24 +14,25 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "BufferStateLayer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include "BufferStateLayer.h"
+
 #include <limits>
 
-#include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
-#include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <gui/BufferQueue.h>
 #include <private/gui/SyncFeatures.h>
 #include <renderengine/Image.h>
 
-#include "BufferStateLayer.h"
-#include "ColorLayer.h"
+#include "EffectLayer.h"
 #include "TimeStats/TimeStats.h"
 
 namespace android {
@@ -52,12 +53,16 @@
 }
 
 BufferStateLayer::~BufferStateLayer() {
-    if (mActiveBuffer != nullptr) {
-        // Ensure that mActiveBuffer is uncached from RenderEngine here, as
+    // The original layer and the clone layer share the same texture and buffer. Therefore, only
+    // one of the layers, in this case the original layer, needs to handle the deletion. The
+    // original layer and the clone should be removed at the same time so there shouldn't be any
+    // issue with the clone layer trying to use the texture.
+    if (mBufferInfo.mBuffer != nullptr && !isClone()) {
+        // Ensure that mBuffer is uncached from RenderEngine here, as
         // RenderEngine may have been using the buffer as an external texture
         // after the client uncached the buffer.
         auto& engine(mFlinger->getRenderEngine());
-        engine.unbindExternalTextureBuffer(mActiveBuffer->getId());
+        engine.unbindExternalTextureBuffer(mBufferInfo.mBuffer->getId());
     }
 }
 
@@ -87,18 +92,43 @@
             break;
         }
     }
+
+    mPreviousReleaseFence = releaseFence;
+
+    // Prevent tracing the same release multiple times.
+    if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
+        mPreviousReleasedFrameNumber = mPreviousFrameNumber;
+    }
 }
 
-void BufferStateLayer::setTransformHint(uint32_t /*orientation*/) const {
-    // TODO(marissaw): send the transform hint to buffer owner
-    return;
-}
+void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
+    for (const auto& handle : mDrawingState.callbackHandles) {
+        handle->transformHint = mTransformHint;
+        handle->dequeueReadyTime = dequeueReadyTime;
+    }
 
-void BufferStateLayer::releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) {
-    mFlinger->getTransactionCompletedThread().addPresentedCallbackHandles(
+    mFlinger->getTransactionCompletedThread().finalizePendingCallbackHandles(
             mDrawingState.callbackHandles);
 
     mDrawingState.callbackHandles = {};
+
+    const sp<Fence>& releaseFence(mPreviousReleaseFence);
+    std::shared_ptr<FenceTime> releaseFenceTime = std::make_shared<FenceTime>(releaseFence);
+    {
+        Mutex::Autolock lock(mFrameEventHistoryMutex);
+        if (mPreviousFrameNumber != 0) {
+            mFrameEventHistory.addRelease(mPreviousFrameNumber, dequeueReadyTime,
+                                          std::move(releaseFenceTime));
+        }
+    }
+}
+
+void BufferStateLayer::finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
+                                                 const CompositorTiming& compositorTiming) {
+    for (const auto& handle : mDrawingState.callbackHandles) {
+        handle->gpuCompositionDoneFence = glDoneFence;
+        handle->compositorTiming = compositorTiming;
+    }
 }
 
 bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const {
@@ -111,29 +141,27 @@
 
 bool BufferStateLayer::willPresentCurrentTransaction() const {
     // Returns true if the most recent Transaction applied to CurrentState will be presented.
-    return getSidebandStreamChanged() || getAutoRefresh() ||
+    return (getSidebandStreamChanged() || getAutoRefresh() ||
             (mCurrentState.modified &&
-             (mCurrentState.buffer != nullptr || mCurrentState.bgColorLayer != nullptr));
+             (mCurrentState.buffer != nullptr || mCurrentState.bgColorLayer != nullptr))) &&
+        !mLayerDetached;
 }
 
-bool BufferStateLayer::getTransformToDisplayInverse() const {
-    return mCurrentState.transformToDisplayInverse;
-}
-
+/* TODO: vhau uncomment once deferred transaction migration complete in
+ * WindowManager
 void BufferStateLayer::pushPendingState() {
     if (!mCurrentState.modified) {
         return;
     }
     mPendingStates.push_back(mCurrentState);
-    ATRACE_INT(mTransactionName.string(), mPendingStates.size());
+    ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
 }
+*/
 
 bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) {
-    const bool stateUpdateAvailable = !mPendingStates.empty();
-    while (!mPendingStates.empty()) {
-        popPendingState(stateToCommit);
-    }
-    mCurrentStateModified = stateUpdateAvailable && mCurrentState.modified;
+    mCurrentStateModified = mCurrentState.modified;
+    bool stateUpdateAvailable = Layer::applyPendingStates(stateToCommit);
+    mCurrentStateModified = stateUpdateAvailable && mCurrentStateModified;
     mCurrentState.modified = false;
     return stateUpdateAvailable;
 }
@@ -215,26 +243,43 @@
     return true;
 }
 
-bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime,
-                                 nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) {
+bool BufferStateLayer::addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
+                                     nsecs_t desiredPresentTime) {
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    mAcquireTimeline.updateSignalTimes();
+    std::shared_ptr<FenceTime> acquireFenceTime =
+            std::make_shared<FenceTime>((acquireFence ? acquireFence : Fence::NO_FENCE));
+    NewFrameEventsEntry newTimestamps = {mCurrentState.frameNumber, postedTime, desiredPresentTime,
+                                         acquireFenceTime};
+    mFrameEventHistory.setProducerWantsEvents();
+    mFrameEventHistory.addQueue(newTimestamps);
+    return true;
+}
+
+bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
+                                 nsecs_t postTime, nsecs_t desiredPresentTime,
+                                 const client_cache_t& clientCacheId) {
     if (mCurrentState.buffer) {
         mReleasePreviousBuffer = true;
     }
 
+    mCurrentState.frameNumber++;
+
     mCurrentState.buffer = buffer;
     mCurrentState.clientCacheId = clientCacheId;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
 
-    mFlinger->mTimeStats->setPostTime(getSequence(), getFrameNumber(), getName().c_str(), postTime);
-    mDesiredPresentTime = desiredPresentTime;
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
+                                      postTime);
+    desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
+    mCurrentState.desiredPresentTime = desiredPresentTime;
 
-    if (mFlinger->mUseSmart90ForVideo) {
-        const nsecs_t presentTime = (mDesiredPresentTime == -1) ? 0 : mDesiredPresentTime;
-        mFlinger->mScheduler->addLayerPresentTimeAndHDR(mSchedulerLayerHandle, presentTime,
-                                                        mCurrentState.hdrMetadata.validTypes != 0);
-    }
+    mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime,
+                                             LayerHistory::LayerUpdateType::Buffer);
 
+    addFrameEvent(acquireFence, postTime, desiredPresentTime);
     return true;
 }
 
@@ -320,7 +365,7 @@
 
         } else { // If this layer will NOT need to be relatched and presented this frame
             // Notify the transaction completed thread this handle is done
-            mFlinger->getTransactionCompletedThread().addUnpresentedCallbackHandle(handle);
+            mFlinger->getTransactionCompletedThread().registerUnpresentedCallbackHandle(handle);
         }
     }
 
@@ -330,6 +375,11 @@
     return willPresent;
 }
 
+void BufferStateLayer::forceSendCallbacks() {
+    mFlinger->getTransactionCompletedThread().finalizePendingCallbackHandles(
+            mCurrentState.callbackHandles);
+}
+
 bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) {
     mCurrentState.transparentRegionHint = transparent;
     mCurrentState.modified = true;
@@ -376,81 +426,59 @@
         return true;
     }
 
-    return getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+    const bool fenceSignaled =
+            getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+    if (!fenceSignaled) {
+        mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
+                                                    TimeStats::LatchSkipReason::LateAcquire);
+    }
+
+    return fenceSignaled;
 }
 
-bool BufferStateLayer::framePresentTimeIsCurrent() const {
+bool BufferStateLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
     if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
         return true;
     }
 
-    return mDesiredPresentTime <= mFlinger->getExpectedPresentTime();
+    return mCurrentState.desiredPresentTime <= expectedPresentTime;
 }
 
-nsecs_t BufferStateLayer::getDesiredPresentTime() {
-    return mDesiredPresentTime;
-}
-
-std::shared_ptr<FenceTime> BufferStateLayer::getCurrentFenceTime() const {
-    return std::make_shared<FenceTime>(getDrawingState().acquireFence);
-}
-
-void BufferStateLayer::getDrawingTransformMatrix(float *matrix) {
-    std::copy(std::begin(mTransformMatrix), std::end(mTransformMatrix), matrix);
-}
-
-uint32_t BufferStateLayer::getDrawingTransform() const {
-    return getDrawingState().transform;
-}
-
-ui::Dataspace BufferStateLayer::getDrawingDataSpace() const {
-    return getDrawingState().dataspace;
-}
-
-// Crop that applies to the buffer
-Rect BufferStateLayer::getDrawingCrop() const {
-    const State& s(getDrawingState());
-
-    if (s.crop.isEmpty() && s.buffer) {
-        return s.buffer->getBounds();
-    } else if (s.buffer) {
-        Rect crop = s.crop;
-        crop.left = std::max(crop.left, 0);
-        crop.top = std::max(crop.top, 0);
-        uint32_t bufferWidth = s.buffer->getWidth();
-        uint32_t bufferHeight = s.buffer->getHeight();
-        if (bufferHeight <= std::numeric_limits<int32_t>::max() &&
-            bufferWidth <= std::numeric_limits<int32_t>::max()) {
-            crop.right = std::min(crop.right, static_cast<int32_t>(bufferWidth));
-            crop.bottom = std::min(crop.bottom, static_cast<int32_t>(bufferHeight));
-        }
-        if (!crop.isValid()) {
-            // Crop rect is out of bounds, return whole buffer
-            return s.buffer->getBounds();
-        }
-        return crop;
+bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) {
+    for (const auto& handle : mDrawingState.callbackHandles) {
+        handle->refreshStartTime = refreshStartTime;
     }
-    return s.crop;
+    return BufferLayer::onPreComposition(refreshStartTime);
 }
 
-uint32_t BufferStateLayer::getDrawingScalingMode() const {
-    return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+uint64_t BufferStateLayer::getFrameNumber(nsecs_t /*expectedPresentTime*/) const {
+    return mDrawingState.frameNumber;
 }
 
-Region BufferStateLayer::getDrawingSurfaceDamage() const {
-    return getDrawingState().surfaceDamageRegion;
-}
-
-const HdrMetadata& BufferStateLayer::getDrawingHdrMetadata() const {
-    return getDrawingState().hdrMetadata;
-}
-
-int BufferStateLayer::getDrawingApi() const {
-    return getDrawingState().api;
-}
-
-uint64_t BufferStateLayer::getFrameNumber() const {
-    return mFrameNumber;
+/**
+ * This is the frameNumber used for deferred transaction signalling. We need to use this because
+ * of cases where we defer a transaction for a surface to itself. In the BLAST world this
+ * may not make a huge amount of sense (Why not just merge the Buffer transaction with the
+ * deferred transaction?) but this is an important legacy use case, for example moving
+ * a window at the same time it draws makes use of this kind of technique. So anyway
+ * imagine we have something like this:
+ *
+ * Transaction { // containing
+ *     Buffer -> frameNumber = 2
+ *     DeferTransactionUntil -> frameNumber = 2
+ *     Random other stuff
+ *  }
+ * Now imagine getHeadFrameNumber returned mDrawingState.mFrameNumber (or mCurrentFrameNumber).
+ * Prior to doTransaction SurfaceFlinger will call notifyAvailableFrames, but because we
+ * haven't swapped mCurrentState to mDrawingState yet we will think the sync point
+ * is not ready. So we will return false from applyPendingState and not swap
+ * current state to drawing state. But because we don't swap current state
+ * to drawing state the number will never update and we will be stuck. This way
+ * we can see we need to return the frame number for the buffer we are about
+ * to apply.
+ */
+uint64_t BufferStateLayer::getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const {
+    return mCurrentState.frameNumber;
 }
 
 bool BufferStateLayer::getAutoRefresh() const {
@@ -466,9 +494,8 @@
     if (mSidebandStreamChanged.exchange(false)) {
         const State& s(getDrawingState());
         // mSidebandStreamChanged was true
-        LOG_ALWAYS_FATAL_IF(!getCompositionLayer());
         mSidebandStream = s.sidebandStream;
-        getCompositionLayer()->editState().frontEnd.sidebandStream = mSidebandStream;
+        editCompositionState()->sidebandStream = mSidebandStream;
         if (mSidebandStream != nullptr) {
             setTransactionFlags(eTransactionNeeded);
             mFlinger->setTransactionFlags(eTraversalNeeded);
@@ -485,11 +512,6 @@
     return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr);
 }
 
-void BufferStateLayer::setFilteringEnabled(bool enabled) {
-    GLConsumer::computeTransformMatrix(mTransformMatrix.data(), mActiveBuffer, mCurrentCrop,
-                                       mCurrentTransform, enabled);
-}
-
 status_t BufferStateLayer::bindTextureImage() {
     const State& s(getDrawingState());
     auto& engine(mFlinger->getRenderEngine());
@@ -497,7 +519,8 @@
     return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence);
 }
 
-status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime) {
+status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
+                                          nsecs_t /*expectedPresentTime*/) {
     const State& s(getDrawingState());
 
     if (!s.buffer) {
@@ -509,7 +532,7 @@
         return NO_ERROR;
     }
 
-    const int32_t layerID = getSequence();
+    const int32_t layerId = getSequence();
 
     // Reject if the layer is invalid
     uint32_t bufferWidth = s.buffer->width;
@@ -520,7 +543,7 @@
     }
 
     if (s.transformToDisplayInverse) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
         if (invTransform & ui::Transform::ROT_90) {
             std::swap(bufferWidth, bufferHeight);
         }
@@ -530,13 +553,14 @@
         (s.active.w != bufferWidth || s.active.h != bufferHeight)) {
         ALOGE("[%s] rejecting buffer: "
               "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
-              mName.string(), bufferWidth, bufferHeight, s.active.w, s.active.h);
-        mFlinger->mTimeStats->removeTimeRecord(layerID, getFrameNumber());
+              getDebugName(), bufferWidth, bufferHeight, s.active.w, s.active.h);
+        mFlinger->mTimeStats->removeTimeRecord(layerId, mDrawingState.frameNumber);
         return BAD_VALUE;
     }
 
     for (auto& handle : mDrawingState.callbackHandles) {
         handle->latchTime = latchTime;
+        handle->frameNumber = mDrawingState.frameNumber;
     }
 
     if (!SyncFeatures::getInstance().useNativeFenceSync()) {
@@ -548,13 +572,14 @@
         // a GL-composited layer) not at all.
         status_t err = bindTextureImage();
         if (err != NO_ERROR) {
-            mFlinger->mTimeStats->onDestroy(layerID);
+            mFlinger->mTimeStats->onDestroy(layerId);
             return BAD_VALUE;
         }
     }
 
-    mFlinger->mTimeStats->setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime());
-    mFlinger->mTimeStats->setLatchTime(layerID, getFrameNumber(), latchTime);
+    mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber,
+                                          std::make_shared<FenceTime>(mDrawingState.acquireFence));
+    mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime);
 
     mCurrentStateModified = false;
 
@@ -568,51 +593,24 @@
         return BAD_VALUE;
     }
 
-    mActiveBuffer = s.buffer;
-    mActiveBufferFence = s.acquireFence;
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-    layerCompositionState.buffer = mActiveBuffer;
-    layerCompositionState.bufferSlot = 0;
+    mPreviousBufferId = getCurrentBufferId();
+    mBufferInfo.mBuffer = s.buffer;
+    mBufferInfo.mFence = s.acquireFence;
 
     return NO_ERROR;
 }
 
-status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
+status_t BufferStateLayer::updateFrameNumber(nsecs_t latchTime) {
     // TODO(marissaw): support frame history events
-    mCurrentFrameNumber = mFrameNumber;
+    mPreviousFrameNumber = mCurrentFrameNumber;
+    mCurrentFrameNumber = mDrawingState.frameNumber;
+    {
+        Mutex::Autolock lock(mFrameEventHistoryMutex);
+        mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
+    }
     return NO_ERROR;
 }
 
-void BufferStateLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
-    auto& hwcInfo = *outputLayer->editState().hwc;
-    auto& hwcLayer = hwcInfo.hwcLayer;
-
-    const State& s(getDrawingState());
-
-    uint32_t hwcSlot;
-    sp<GraphicBuffer> buffer;
-    hwcInfo.hwcBufferCache.getHwcBuffer(mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId),
-                                        s.buffer, &hwcSlot, &buffer);
-
-    auto error = hwcLayer->setBuffer(hwcSlot, buffer, s.acquireFence);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(),
-              s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-
-    mFrameNumber++;
-}
-
-void BufferStateLayer::onFirstRef() {
-    BufferLayer::onFirstRef();
-
-    if (const auto display = mFlinger->getDefaultDisplayDevice()) {
-        updateTransformHint(display);
-    }
-}
-
 void BufferStateLayer::HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
     std::lock_guard lock(mMutex);
     if (!clientCacheId.isValid()) {
@@ -685,4 +683,84 @@
     mFreeHwcCacheSlots.push(hwcCacheSlot);
     mCachedBuffers.erase(clientCacheId);
 }
+
+void BufferStateLayer::gatherBufferInfo() {
+    BufferLayer::gatherBufferInfo();
+
+    const State& s(getDrawingState());
+    mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
+    mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
+    mBufferInfo.mFence = s.acquireFence;
+    mBufferInfo.mTransform = s.transform;
+    mBufferInfo.mDataspace = translateDataspace(s.dataspace);
+    mBufferInfo.mCrop = computeCrop(s);
+    mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+    mBufferInfo.mSurfaceDamage = s.surfaceDamageRegion;
+    mBufferInfo.mHdrMetadata = s.hdrMetadata;
+    mBufferInfo.mApi = s.api;
+    mBufferInfo.mTransformToDisplayInverse = s.transformToDisplayInverse;
+    mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
+}
+
+Rect BufferStateLayer::computeCrop(const State& s) {
+    if (s.crop.isEmpty() && s.buffer) {
+        return s.buffer->getBounds();
+    } else if (s.buffer) {
+        Rect crop = s.crop;
+        crop.left = std::max(crop.left, 0);
+        crop.top = std::max(crop.top, 0);
+        uint32_t bufferWidth = s.buffer->getWidth();
+        uint32_t bufferHeight = s.buffer->getHeight();
+        if (bufferHeight <= std::numeric_limits<int32_t>::max() &&
+            bufferWidth <= std::numeric_limits<int32_t>::max()) {
+            crop.right = std::min(crop.right, static_cast<int32_t>(bufferWidth));
+            crop.bottom = std::min(crop.bottom, static_cast<int32_t>(bufferHeight));
+        }
+        if (!crop.isValid()) {
+            // Crop rect is out of bounds, return whole buffer
+            return s.buffer->getBounds();
+        }
+        return crop;
+    }
+    return s.crop;
+}
+
+sp<Layer> BufferStateLayer::createClone() {
+    LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, LayerMetadata());
+    args.textureName = mTextureName;
+    sp<BufferStateLayer> layer = mFlinger->getFactory().createBufferStateLayer(args);
+    layer->mHwcSlotGenerator = mHwcSlotGenerator;
+    layer->setInitialValuesForClone(this);
+    return layer;
+}
+
+Layer::RoundedCornerState BufferStateLayer::getRoundedCornerState() const {
+    const auto& p = mDrawingParent.promote();
+    if (p != nullptr) {
+        RoundedCornerState parentState = p->getRoundedCornerState();
+        if (parentState.radius > 0) {
+            ui::Transform t = getActiveTransform(getDrawingState());
+            t = t.inverse();
+            parentState.cropRect = t.transform(parentState.cropRect);
+            // The rounded corners shader only accepts 1 corner radius for performance reasons,
+            // but a transform matrix can define horizontal and vertical scales.
+            // Let's take the average between both of them and pass into the shader, practically we
+            // never do this type of transformation on windows anyway.
+            parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
+            return parentState;
+        }
+    }
+    const float radius = getDrawingState().cornerRadius;
+    const State& s(getDrawingState());
+    if (radius <= 0 || (getActiveWidth(s) == UINT32_MAX && getActiveHeight(s) == UINT32_MAX))
+        return RoundedCornerState();
+    return RoundedCornerState(FloatRect(static_cast<float>(s.active.transform.tx()),
+                                        static_cast<float>(s.active.transform.ty()),
+                                        static_cast<float>(s.active.transform.tx() + s.active.w),
+                                        static_cast<float>(s.active.transform.ty() + s.active.h)),
+                              radius);
+}
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 2b52102..00fa7f7 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -19,7 +19,6 @@
 #include "BufferLayer.h"
 #include "Layer.h"
 
-#include <gui/GLConsumer.h>
 #include <renderengine/Image.h>
 #include <renderengine/RenderEngine.h>
 #include <system/window.h>
@@ -40,18 +39,22 @@
     // -----------------------------------------------------------------------
     // Interface implementation for Layer
     // -----------------------------------------------------------------------
+    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;
 
-    bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
+    void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
+                                   const CompositorTiming& compositorTiming) override;
 
-    bool getTransformToDisplayInverse() const override;
+    bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
 
     uint32_t doTransactionResize(uint32_t flags, Layer::State* /*stateToCommit*/) override {
         return flags;
     }
-    void pushPendingState() override;
+    /*TODO:vhau return to using BufferStateLayer override once WM
+     * has removed deferred transactions!
+    void pushPendingState() override;*/
     bool applyPendingStates(Layer::State* stateToCommit) override;
 
     uint32_t getActiveWidth(const Layer::State& s) const override { return s.active.w; }
@@ -68,8 +71,8 @@
     bool setTransformToDisplayInverse(bool transformToDisplayInverse) override;
     bool setCrop(const Rect& crop) override;
     bool setFrame(const Rect& frame) override;
-    bool setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime, nsecs_t desiredPresentTime,
-                   const client_cache_t& clientCacheId) override;
+    bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
+                   nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -77,16 +80,19 @@
     bool setApi(int32_t api) override;
     bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
     bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
+    void forceSendCallbacks() override;
+    bool addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
+                       nsecs_t requestedPresentTime) override;
 
     // Override to ignore legacy layer state properties that are not used by BufferStateLayer
     bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; }
-    bool setPosition(float /*x*/, float /*y*/, bool /*immediate*/) override { return false; }
+    bool setPosition(float /*x*/, float /*y*/) override { return false; }
     bool setTransparentRegionHint(const Region& transparent) override;
     bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/,
                    bool /*allowNonRectPreservingTransforms*/) override {
         return false;
     }
-    bool setCrop_legacy(const Rect& /*crop*/, bool /*immediate*/) override { return false; }
+    bool setCrop_legacy(const Rect& /*crop*/) override { return false; }
     bool setOverrideScalingMode(int32_t /*overrideScalingMode*/) override { return false; }
     void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
                                       uint64_t /*frameNumber*/) override {}
@@ -95,6 +101,7 @@
 
     Rect getBufferSize(const State& s) const override;
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
+    Layer::RoundedCornerState getRoundedCornerState() const override;
 
     // -----------------------------------------------------------------------
 
@@ -102,22 +109,18 @@
     // Interface implementation for BufferLayer
     // -----------------------------------------------------------------------
     bool fenceHasSignaled() const override;
-    bool framePresentTimeIsCurrent() const override;
+    bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
+    bool onPreComposition(nsecs_t refreshStartTime) override;
+
+protected:
+    void gatherBufferInfo() override;
+    uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
 
 private:
-    nsecs_t getDesiredPresentTime() override;
-    std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
+    bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
+                                 nsecs_t requestedPresentTime);
 
-    void getDrawingTransformMatrix(float *matrix) override;
-    uint32_t getDrawingTransform() const override;
-    ui::Dataspace getDrawingDataSpace() const override;
-    Rect getDrawingCrop() const override;
-    uint32_t getDrawingScalingMode() const override;
-    Region getDrawingSurfaceDamage() const override;
-    const HdrMetadata& getDrawingHdrMetadata() const override;
-    int getDrawingApi() const override;
-
-    uint64_t getFrameNumber() const override;
+    uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
 
     bool getAutoRefresh() const override;
     bool getSidebandStreamChanged() const override;
@@ -126,39 +129,39 @@
 
     bool hasFrameUpdate() const override;
 
-    void setFilteringEnabled(bool enabled) override;
-
     status_t bindTextureImage() override;
-    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
+    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                            nsecs_t expectedPresentTime) override;
 
     status_t updateActiveBuffer() override;
     status_t updateFrameNumber(nsecs_t latchTime) override;
 
-    void setHwcLayerBuffer(const sp<const DisplayDevice>& display) override;
+    sp<Layer> createClone() override;
+
+    // Crop that applies to the buffer
+    Rect computeCrop(const State& s);
 
 private:
     friend class SlotGenerationTest;
-    void onFirstRef() override;
     bool willPresentCurrentTransaction() const;
 
     static const std::array<float, 16> IDENTITY_MATRIX;
 
     std::unique_ptr<renderengine::Image> mTextureImage;
 
-    std::array<float, 16> mTransformMatrix{IDENTITY_MATRIX};
-
     std::atomic<bool> mSidebandStreamChanged{false};
 
-    uint32_t mFrameNumber{0};
+    mutable uint64_t mFrameNumber{0};
+    uint64_t mFrameCounter{0};
 
     sp<Fence> mPreviousReleaseFence;
+    uint64_t mPreviousBufferId = 0;
+    uint64_t mPreviousReleasedFrameNumber = 0;
 
-    bool mCurrentStateModified = false;
+    mutable bool mCurrentStateModified = false;
     bool mReleasePreviousBuffer = false;
     nsecs_t mCallbackHandleAcquireTime = -1;
 
-    nsecs_t mDesiredPresentTime = -1;
-
     // 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 c526f7f..78bbcba 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -75,17 +79,18 @@
 status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                                uint32_t flags, const sp<IBinder>& parentHandle,
                                LayerMetadata metadata, sp<IBinder>* handle,
-                               sp<IGraphicBufferProducer>* gbp) {
+                               sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) {
     // We rely on createLayer to check permissions.
     return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
-                                 parentHandle);
+                                 parentHandle, nullptr, outTransformHint);
 }
 
 status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
                                          PixelFormat format, uint32_t flags,
                                          const sp<IGraphicBufferProducer>& parent,
                                          LayerMetadata metadata, sp<IBinder>* handle,
-                                         sp<IGraphicBufferProducer>* gbp) {
+                                         sp<IGraphicBufferProducer>* gbp,
+                                         uint32_t* outTransformHint) {
     if (mFlinger->authenticateSurfaceTexture(parent) == false) {
         ALOGE("failed to authenticate surface texture");
         return BAD_VALUE;
@@ -98,7 +103,11 @@
     }
 
     return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
-                                 nullptr, layer);
+                                 nullptr, layer, outTransformHint);
+}
+
+status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) {
+    return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle);
 }
 
 status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
@@ -121,3 +130,6 @@
 
 // ---------------------------------------------------------------------------
 }; // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 74e4818..e9063e5 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -54,13 +54,17 @@
     virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                                    uint32_t flags, const sp<IBinder>& parent,
                                    LayerMetadata metadata, sp<IBinder>* handle,
-                                   sp<IGraphicBufferProducer>* gbp);
+                                   sp<IGraphicBufferProducer>* gbp,
+                                   uint32_t* outTransformHint = nullptr);
 
     virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
                                              PixelFormat format, uint32_t flags,
                                              const sp<IGraphicBufferProducer>& parent,
                                              LayerMetadata metadata, sp<IBinder>* handle,
-                                             sp<IGraphicBufferProducer>* gbp);
+                                             sp<IGraphicBufferProducer>* gbp,
+                                             uint32_t* outTransformHint = nullptr);
+
+    status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle);
 
     virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
 
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 16fe27c..a5be01c 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -42,7 +42,7 @@
         return false;
     }
 
-    auto& processBuffers = it->second;
+    auto& processBuffers = it->second.second;
 
     auto bufItr = processBuffers.find(id);
     if (bufItr == processBuffers.end()) {
@@ -86,12 +86,14 @@
             return false;
         }
         auto [itr, success] =
-                mBuffers.emplace(processToken, std::unordered_map<uint64_t, ClientCacheBuffer>());
+                mBuffers.emplace(processToken,
+                                 std::make_pair(token,
+                                                std::unordered_map<uint64_t, ClientCacheBuffer>()));
         LOG_ALWAYS_FATAL_IF(!success, "failed to insert new process into client cache");
         it = itr;
     }
 
-    auto& processBuffers = it->second;
+    auto& processBuffers = it->second.second;
 
     if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
         ALOGE("failed to cache buffer: cache is full");
@@ -120,7 +122,7 @@
             }
         }
 
-        mBuffers[processToken].erase(id);
+        mBuffers[processToken].second.erase(id);
     }
 
     for (auto& recipient : pendingErase) {
@@ -180,7 +182,7 @@
             return;
         }
 
-        for (auto& [id, clientCacheBuffer] : itr->second) {
+        for (auto& [id, clientCacheBuffer] : itr->second.second) {
             client_cache_t cacheId = {processToken, id};
             for (auto& recipient : clientCacheBuffer.recipients) {
                 sp<ErasedRecipient> erasedRecipient = recipient.promote();
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index aa6c80d..d7af7c0 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -61,7 +61,8 @@
         std::set<wp<ErasedRecipient>> recipients;
     };
     std::map<wp<IBinder> /*caching process*/,
-             std::unordered_map<uint64_t /*cache id*/, ClientCacheBuffer>>
+             std::pair<sp<IBinder> /*strong ref to caching process*/,
+                       std::unordered_map<uint64_t /*cache id*/, ClientCacheBuffer>>>
             mBuffers GUARDED_BY(mMutex);
 
     class CacheDeathRecipient : public IBinder::DeathRecipient {
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
deleted file mode 100644
index fcc2d97..0000000
--- a/services/surfaceflinger/ColorLayer.cpp
+++ /dev/null
@@ -1,176 +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.
- */
-
-// #define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "ColorLayer"
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/types.h>
-
-#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
-#include <compositionengine/LayerCreationArgs.h>
-#include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
-#include <renderengine/RenderEngine.h>
-#include <ui/GraphicBuffer.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
-#include "ColorLayer.h"
-#include "DisplayDevice.h"
-#include "SurfaceFlinger.h"
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-ColorLayer::ColorLayer(const LayerCreationArgs& args)
-      : Layer(args),
-        mCompositionLayer{mFlinger->getCompositionEngine().createLayer(
-                compositionengine::LayerCreationArgs{this})} {}
-
-ColorLayer::~ColorLayer() = default;
-
-bool ColorLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                                    bool useIdentityTransform, Region& clearRegion,
-                                    const bool supportProtectedContent,
-                                    renderengine::LayerSettings& layer) {
-    Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion,
-                              supportProtectedContent, layer);
-    half4 color(getColor());
-    half3 solidColor(color.r, color.g, color.b);
-    layer.source.solidColor = solidColor;
-    return true;
-}
-
-bool ColorLayer::isVisible() const {
-    return !isHiddenByPolicy() && getAlpha() > 0.0f;
-}
-
-bool ColorLayer::setColor(const half3& color) {
-    if (mCurrentState.color.r == color.r && mCurrentState.color.g == color.g &&
-        mCurrentState.color.b == color.b) {
-        return false;
-    }
-
-    mCurrentState.sequence++;
-    mCurrentState.color.r = color.r;
-    mCurrentState.color.g = color.g;
-    mCurrentState.color.b = color.b;
-    mCurrentState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool ColorLayer::setDataspace(ui::Dataspace dataspace) {
-    if (mCurrentState.dataspace == dataspace) {
-        return false;
-    }
-
-    mCurrentState.sequence++;
-    mCurrentState.dataspace = dataspace;
-    mCurrentState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& display,
-                                 const ui::Transform& transform, const Rect& viewport,
-                                 int32_t /* supportedPerFrameMetadata */,
-                                 const ui::Dataspace targetDataspace) {
-    RETURN_IF_NO_HWC_LAYER(display);
-
-    Region visible = transform.transform(visibleRegion.intersect(viewport));
-
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
-
-    auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
-
-    auto error = hwcLayer->setVisibleRegion(visible);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-        visible.dump(LOG_TAG);
-    }
-    outputLayer->editState().visibleRegion = visible;
-
-    setCompositionType(display, Hwc2::IComposerClient::Composition::SOLID_COLOR);
-
-    const ui::Dataspace dataspace =
-            isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN ? targetDataspace
-                                                                                : mCurrentDataSpace;
-    error = hwcLayer->setDataspace(dataspace);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace,
-              to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-    layerCompositionState.dataspace = mCurrentDataSpace;
-
-    half4 color = getColor();
-    error = hwcLayer->setColor({static_cast<uint8_t>(std::round(255.0f * color.r)),
-                                static_cast<uint8_t>(std::round(255.0f * color.g)),
-                                static_cast<uint8_t>(std::round(255.0f * color.b)), 255});
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set color: %s (%d)", mName.string(), to_string(error).c_str(),
-              static_cast<int32_t>(error));
-    }
-    layerCompositionState.color = {static_cast<uint8_t>(std::round(255.0f * color.r)),
-                                   static_cast<uint8_t>(std::round(255.0f * color.g)),
-                                   static_cast<uint8_t>(std::round(255.0f * color.b)), 255};
-
-    // Clear out the transform, because it doesn't make sense absent a source buffer
-    error = hwcLayer->setTransform(HWC2::Transform::None);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to clear transform: %s (%d)", mName.string(), to_string(error).c_str(),
-              static_cast<int32_t>(error));
-    }
-    outputLayer->editState().bufferTransform = static_cast<Hwc2::Transform>(0);
-
-    error = hwcLayer->setColorTransform(getColorTransform());
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(),
-                to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-    layerCompositionState.colorTransform = getColorTransform();
-
-    error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(),
-              to_string(error).c_str(), static_cast<int32_t>(error));
-        surfaceDamageRegion.dump(LOG_TAG);
-    }
-    layerCompositionState.surfaceDamage = surfaceDamageRegion;
-}
-
-void ColorLayer::commitTransaction(const State& stateToCommit) {
-    Layer::commitTransaction(stateToCommit);
-    mCurrentDataSpace = mDrawingState.dataspace;
-}
-
-std::shared_ptr<compositionengine::Layer> ColorLayer::getCompositionLayer() const {
-    return mCompositionLayer;
-}
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h
deleted file mode 100644
index 53d5b5b..0000000
--- a/services/surfaceflinger/ColorLayer.h
+++ /dev/null
@@ -1,58 +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.
- */
-#pragma once
-
-#include <sys/types.h>
-
-#include <cstdint>
-
-#include "Layer.h"
-
-namespace android {
-
-class ColorLayer : public Layer {
-public:
-    explicit ColorLayer(const LayerCreationArgs&);
-    ~ColorLayer() override;
-
-    std::shared_ptr<compositionengine::Layer> getCompositionLayer() const override;
-
-    virtual const char* getTypeId() const { return "ColorLayer"; }
-    bool isVisible() const override;
-
-    bool setColor(const half3& color) override;
-
-    bool setDataspace(ui::Dataspace dataspace) override;
-
-    void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
-                         const Rect& viewport, int32_t supportedPerFrameMetadata,
-                         const ui::Dataspace targetDataspace) override;
-
-    void commitTransaction(const State& stateToCommit) override;
-
-    bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
-
-protected:
-    virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                                    bool useIdentityTransform, Region& clearRegion,
-                                    const bool supportProtectedContent,
-                                    renderengine::LayerSettings& layer);
-
-private:
-    std::shared_ptr<compositionengine::Layer> mCompositionLayer;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 6f076ad..b37ca33 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -3,13 +3,15 @@
     defaults: ["surfaceflinger_defaults"],
     cflags: [
         "-DLOG_TAG=\"CompositionEngine\"",
+        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
     ],
     shared_libs: [
-        "android.frameworks.vr.composer@1.0",
+        "android.frameworks.vr.composer@2.0",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
+        "android.hardware.graphics.composer@2.4",
         "android.hardware.power@1.0",
         "android.hardware.power@1.3",
         "libbase",
@@ -18,8 +20,8 @@
         "liblayers_proto",
         "liblog",
         "libnativewindow",
-        "libsync",
-        "libtimestats_proto",
+        "libprotobuf-cpp-lite",
+        "libtimestats",
         "libui",
         "libutils",
     ],
@@ -32,6 +34,7 @@
         "android.hardware.graphics.composer@2.1-command-buffer",
         "android.hardware.graphics.composer@2.2-command-buffer",
         "android.hardware.graphics.composer@2.3-command-buffer",
+        "android.hardware.graphics.composer@2.4-command-buffer",
         "libsurfaceflinger_headers",
     ],
 }
@@ -40,14 +43,14 @@
     name: "libcompositionengine",
     defaults: ["libcompositionengine_defaults"],
     srcs: [
+        "src/ClientCompositionRequestCache.cpp",
         "src/CompositionEngine.cpp",
         "src/Display.cpp",
         "src/DisplayColorProfile.cpp",
         "src/DisplaySurface.cpp",
         "src/DumpHelpers.cpp",
         "src/HwcBufferCache.cpp",
-        "src/Layer.cpp",
-        "src/LayerCompositionState.cpp",
+        "src/LayerFECompositionState.cpp",
         "src/Output.cpp",
         "src/OutputCompositionState.cpp",
         "src/OutputLayer.cpp",
@@ -66,7 +69,6 @@
         "mock/Display.cpp",
         "mock/DisplayColorProfile.cpp",
         "mock/DisplaySurface.cpp",
-        "mock/Layer.cpp",
         "mock/LayerFE.cpp",
         "mock/NativeWindow.cpp",
         "mock/Output.cpp",
@@ -91,9 +93,9 @@
         "tests/DisplayColorProfileTest.cpp",
         "tests/DisplayTest.cpp",
         "tests/HwcBufferCacheTest.cpp",
-        "tests/LayerTest.cpp",
         "tests/MockHWC2.cpp",
         "tests/MockHWComposer.cpp",
+        "tests/MockPowerAdvisor.cpp",
         "tests/OutputTest.cpp",
         "tests/OutputLayerTest.cpp",
         "tests/RenderSurfaceTest.cpp",
@@ -101,6 +103,7 @@
     static_libs: [
         "libcompositionengine",
         "libcompositionengine_mocks",
+        "libgui_mocks",
         "librenderengine_mocks",
         "libgmock",
         "libgtest",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 896f8aa..3faa068 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -16,6 +16,9 @@
 
 #pragma once
 
+#include <TimeStats/TimeStats.h>
+#include <utils/Timers.h>
+
 #include <memory>
 
 namespace android {
@@ -29,10 +32,11 @@
 namespace compositionengine {
 
 class Display;
-class Layer;
 
+struct CompositionRefreshArgs;
 struct DisplayCreationArgs;
 struct LayerCreationArgs;
+struct LayerFECompositionState;
 
 /**
  * Encapsulates all the interfaces and implementation details for performing
@@ -43,14 +47,33 @@
     virtual ~CompositionEngine();
 
     // Create a composition Display
-    virtual std::shared_ptr<Display> createDisplay(DisplayCreationArgs&&) = 0;
-    virtual std::shared_ptr<Layer> createLayer(LayerCreationArgs&&) = 0;
+    virtual std::shared_ptr<Display> createDisplay(const DisplayCreationArgs&) = 0;
+    virtual std::unique_ptr<compositionengine::LayerFECompositionState>
+    createLayerFECompositionState() = 0;
 
     virtual HWComposer& getHwComposer() const = 0;
     virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0;
 
     virtual renderengine::RenderEngine& getRenderEngine() const = 0;
     virtual void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) = 0;
+
+    virtual TimeStats& getTimeStats() const = 0;
+    virtual void setTimeStats(const std::shared_ptr<TimeStats>&) = 0;
+
+    virtual bool needsAnotherUpdate() const = 0;
+    virtual nsecs_t getLastFrameRefreshTimestamp() const = 0;
+
+    // Presents the indicated outputs
+    virtual void present(CompositionRefreshArgs&) = 0;
+
+    // Updates the cursor position for the indicated outputs.
+    virtual void updateCursorAsync(CompositionRefreshArgs&) = 0;
+
+    // TODO(b/121291683): These will become private/internal
+    virtual void preComposition(CompositionRefreshArgs&) = 0;
+
+    // Debugging
+    virtual void dump(std::string&) const = 0;
 };
 
 } // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
new file mode 100644
index 0000000..a0606b4
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <optional>
+#include <vector>
+
+#include <compositionengine/Display.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/OutputColorSetting.h>
+#include <math/mat4.h>
+#include <ui/Transform.h>
+
+namespace android::compositionengine {
+
+using Layers = std::vector<sp<compositionengine::LayerFE>>;
+using Outputs = std::vector<std::shared_ptr<compositionengine::Output>>;
+
+/**
+ * A parameter object for refreshing a set of outputs
+ */
+struct CompositionRefreshArgs {
+    // All the outputs being refreshed
+    Outputs outputs;
+
+    // All the layers that are potentially visible in the outputs. The order of
+    // the layers is important, and should be in traversal order from back to
+    // front.
+    Layers layers;
+
+    // All the layers that have queued updates.
+    Layers layersWithQueuedFrames;
+
+    // If true, forces the entire display to be considered dirty and repainted
+    bool repaintEverything{false};
+
+    // Controls how the color mode is chosen for an output
+    OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced};
+
+    // If not Dataspace::UNKNOWN, overrides the dataspace on each output
+    ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN};
+
+    // 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};
+
+    // If true, the complete output geometry needs to be recomputed this frame
+    bool updatingOutputGeometryThisFrame{false};
+
+    // If true, there was a geometry update this frame
+    bool updatingGeometryThisFrame{false};
+
+    // The color matrix to use for this
+    // frame. Only set if the color transform is changing this frame.
+    std::optional<mat4> colorTransformMatrix;
+
+    // If true, client composition is always used.
+    bool devOptForceClientComposition{false};
+
+    // If set, causes the dirty regions to flash with the delay
+    std::optional<std::chrono::microseconds> devOptFlashDirtyRegionsDelay;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index dbcd3bd..a38d1f3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -47,10 +47,14 @@
     virtual void disconnect() = 0;
 
     // Creates a render color mode for the display
-    virtual void createDisplayColorProfile(DisplayColorProfileCreationArgs&&) = 0;
+    virtual void createDisplayColorProfile(const DisplayColorProfileCreationArgs&) = 0;
 
     // Creates a render surface for the display
-    virtual void createRenderSurface(RenderSurfaceCreationArgs&&) = 0;
+    virtual void createRenderSurface(const RenderSurfaceCreationArgs&) = 0;
+
+    // Creates a cache to cache duplicate client composition requests and skip
+    // similar requests if needed.
+    virtual void createClientCompositionCache(uint32_t cacheSize) = 0;
 
 protected:
     ~Display() = default;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
index e2a0d42..67e6deb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
@@ -17,9 +17,17 @@
 #pragma once
 
 #include <cstdint>
+#include <string>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 
 #include <ui/GraphicTypes.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android {
 
 class HdrCapabilities;
@@ -75,6 +83,13 @@
     // Gets the supported HDR capabilities for the profile
     virtual const HdrCapabilities& getHdrCapabilities() const = 0;
 
+    // Returns true if HWC for this profile supports the dataspace
+    virtual bool isDataspaceSupported(ui::Dataspace) const = 0;
+
+    // Returns the target dataspace for picked color mode and dataspace
+    virtual ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace,
+                                             ui::Dataspace colorSpaceAgnosticDataspace) const = 0;
+
     // Debugging
     virtual void dump(std::string&) const = 0;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
index ef0f925..7eb8eb1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
@@ -20,7 +20,15 @@
 #include <unordered_map>
 #include <vector>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <ui/GraphicTypes.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 #include <ui/HdrCapabilities.h>
 
 namespace android::compositionengine {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 0b6b4e4..6bc677d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -18,8 +18,14 @@
 
 #include <cstdint>
 #include <optional>
+#include <string>
+
+#include <ui/DisplayInfo.h>
+#include <ui/PixelFormat.h>
+#include <ui/Size.h>
 
 #include "DisplayHardware/DisplayIdentification.h"
+#include "DisplayHardware/PowerAdvisor.h"
 
 namespace android::compositionengine {
 
@@ -29,43 +35,83 @@
  * A parameter object for creating Display instances
  */
 struct DisplayCreationArgs {
-    // True if this display is secure
+    struct Physical {
+        DisplayId id;
+        DisplayConnectionType type;
+    };
+
+    // Required for physical displays. Gives the HWC display id for the existing
+    // display along with the connection type.
+    std::optional<Physical> physical;
+
+    // Size of the display in pixels
+    ui::Size pixels = ui::Size::INVALID;
+
+    // Pixel format of the display
+    ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
+
+    // True if virtual displays should be created with the HWC API if possible
+    bool useHwcVirtualDisplays = false;
+
+    // True if this display should be considered secure
     bool isSecure = false;
 
-    // True if this display is a virtual display
-    bool isVirtual = false;
+    // Gives the initial layer stack id to be used for the display
+    uint32_t layerStackId = ~0u;
 
-    // Identifies the display to the HWC, if composition is supported by it
-    std::optional<DisplayId> displayId;
+    // Optional pointer to the power advisor interface, if one is needed for
+    // this display.
+    Hwc2::PowerAdvisor* powerAdvisor = nullptr;
+
+    // Debugging. Human readable name for the display.
+    std::string name;
 };
 
 /**
  * A helper for setting up a DisplayCreationArgs value in-line.
  * Prefer this builder over raw structure initialization.
- *
- * Instead of:
- *
- *   DisplayCreationArgs{false, false, displayId}
- *
- * Prefer:
- *
- *  DisplayCreationArgsBuilder().setIsSecure(false).setIsVirtual(false)
- *      .setDisplayId(displayId).build();
  */
 class DisplayCreationArgsBuilder {
 public:
     DisplayCreationArgs build() { return std::move(mArgs); }
 
+    DisplayCreationArgsBuilder& setPhysical(DisplayCreationArgs::Physical physical) {
+        mArgs.physical = physical;
+        return *this;
+    }
+
+    DisplayCreationArgsBuilder& setPixels(ui::Size pixels) {
+        mArgs.pixels = pixels;
+        return *this;
+    }
+
+    DisplayCreationArgsBuilder& setPixelFormat(ui::PixelFormat pixelFormat) {
+        mArgs.pixelFormat = pixelFormat;
+        return *this;
+    }
+
+    DisplayCreationArgsBuilder& setUseHwcVirtualDisplays(bool useHwcVirtualDisplays) {
+        mArgs.useHwcVirtualDisplays = useHwcVirtualDisplays;
+        return *this;
+    }
+
     DisplayCreationArgsBuilder& setIsSecure(bool isSecure) {
         mArgs.isSecure = isSecure;
         return *this;
     }
-    DisplayCreationArgsBuilder& setIsVirtual(bool isVirtual) {
-        mArgs.isVirtual = isVirtual;
+
+    DisplayCreationArgsBuilder& setLayerStackId(uint32_t layerStackId) {
+        mArgs.layerStackId = layerStackId;
         return *this;
     }
-    DisplayCreationArgsBuilder& setDisplayId(std::optional<DisplayId> displayId) {
-        mArgs.displayId = displayId;
+
+    DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
+        mArgs.powerAdvisor = powerAdvisor;
+        return *this;
+    }
+
+    DisplayCreationArgsBuilder& setName(std::string name) {
+        mArgs.name = std::move(name);
         return *this;
     }
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index 0e67acf..6559ed8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -45,19 +45,19 @@
     // prepareFrame is called after the composition configuration is known but
     // before composition takes place. The DisplaySurface can use the
     // composition type to decide how to manage the flow of buffers between
-    // GLES and HWC for this frame.
+    // GPU and HWC for this frame.
     enum CompositionType {
         COMPOSITION_UNKNOWN = 0,
-        COMPOSITION_GLES = 1,
+        COMPOSITION_GPU = 1,
         COMPOSITION_HWC = 2,
-        COMPOSITION_MIXED = COMPOSITION_GLES | COMPOSITION_HWC
+        COMPOSITION_MIXED = COMPOSITION_GPU | COMPOSITION_HWC
     };
     virtual status_t prepareFrame(CompositionType compositionType) = 0;
 
-    // Inform the surface that GLES composition is complete for this frame, and
+    // Inform the surface that GPU composition is complete for this frame, and
     // the surface should make sure that HWComposer has the correct buffer for
     // this frame. Some implementations may only push a new buffer to
-    // HWComposer if GLES composition took place, others need to push a new
+    // HWComposer if GPU composition took place, others need to push a new
     // buffer on every frame.
     //
     // advanceFrame must be followed by a call to  onFrameCommitted before
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
deleted file mode 100644
index 8cb9203..0000000
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cstdint>
-#include <string>
-
-#include <utils/StrongPointer.h>
-
-namespace android {
-
-typedef int64_t nsecs_t;
-
-namespace compositionengine {
-
-class Display;
-class LayerFE;
-
-namespace impl {
-struct LayerCompositionState;
-} // namespace impl
-
-/**
- * A layer contains the output-independent composition state for a front-end
- * Layer
- */
-class Layer {
-public:
-    virtual ~Layer();
-
-    // Gets the front-end interface for this layer.  Can return nullptr if the
-    // front-end layer no longer exists.
-    virtual sp<LayerFE> getLayerFE() const = 0;
-
-    using CompositionState = impl::LayerCompositionState;
-
-    // Gets the raw composition state data for the layer
-    // TODO(lpique): Make this protected once it is only internally called.
-    virtual const CompositionState& getState() const = 0;
-
-    // Allows mutable access to the raw composition state data for the layer.
-    // This is meant to be used by the various functions that are part of the
-    // composition process.
-    // TODO(lpique): Make this protected once it is only internally called.
-    virtual CompositionState& editState() = 0;
-
-    // Debugging
-    virtual void dump(std::string& result) const = 0;
-};
-
-} // namespace compositionengine
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 9f635b9..6cc90cb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -16,7 +16,21 @@
 
 #pragma once
 
+#include <optional>
+#include <ostream>
+#include <unordered_set>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <renderengine/LayerSettings.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 #include <utils/RefBase.h>
+#include <utils/Timers.h>
 
 namespace android {
 
@@ -30,9 +44,91 @@
 // of the front-end layer
 class LayerFE : public virtual RefBase {
 public:
-    // Latches the output-independent state. If includeGeometry is false, the
-    // geometry state can be skipped.
-    virtual void latchCompositionState(LayerFECompositionState&, bool includeGeometry) const = 0;
+    // Gets the raw front-end composition state data for the layer
+    virtual const LayerFECompositionState* getCompositionState() const = 0;
+
+    // Called before composition starts. Should return true if this layer has
+    // pending updates which would require an extra display refresh cycle to
+    // process.
+    virtual bool onPreComposition(nsecs_t refreshStartTime) = 0;
+
+    // Used with latchCompositionState()
+    enum class StateSubset {
+        // Gets the basic geometry (bounds, transparent region, visibility,
+        // transforms, alpha) for the layer, for computing visibility and
+        // coverage.
+        BasicGeometry,
+
+        // Gets the full geometry (crops, buffer transforms, metadata) and
+        // content (buffer or color) state for the layer.
+        GeometryAndContent,
+
+        // Gets the per frame content (buffer or color) state for the layer.
+        Content,
+
+        // Gets the cursor state for the layer.
+        Cursor,
+    };
+
+    // Prepares the output-independent composition state for the layer. The
+    // StateSubset argument selects what portion of the state is actually needed
+    // by the CompositionEngine code, since computing everything may be
+    // expensive.
+    virtual void prepareCompositionState(StateSubset) = 0;
+
+    struct ClientCompositionTargetSettings {
+        // The clip region, or visible region that is being rendered to
+        const Region& clip;
+
+        // If true, the layer should use an identity transform for its position
+        // transform. Used only by the captureScreen API call.
+        const bool useIdentityTransform;
+
+        // If set to true, the layer should enable filtering when rendering.
+        const bool needsFiltering;
+
+        // If set to true, the buffer is being sent to a destination that is
+        // expected to treat the buffer contents as secure.
+        const bool isSecure;
+
+        // If set to true, the target buffer has protected content support.
+        const bool supportsProtectedContent;
+
+        // Modified by each call to prepareClientComposition to indicate the
+        // region of the target buffer that should be cleared.
+        Region& clearRegion;
+
+        // Viewport of the target being rendered to. This is used to determine
+        // the shadow light position.
+        const Rect& viewport;
+
+        // Dataspace of the output so we can optimize how to render the shadow
+        // by avoiding unnecessary color space conversions.
+        const ui::Dataspace dataspace;
+
+        // True if the region excluding the shadow is visible.
+        const bool realContentIsVisible;
+
+        // If set to true, change the layer settings to render a clear output.
+        // This may be requested by the HWC
+        const bool clearContent;
+    };
+
+    // A superset of LayerSettings required by RenderEngine to compose a layer
+    // and buffer info to determine duplicate client composition requests.
+    struct LayerSettings : renderengine::LayerSettings {
+        // Currently latched buffer if, 0 if invalid.
+        uint64_t bufferId = 0;
+
+        // Currently latched frame number, 0 if invalid.
+        uint64_t frameNumber = 0;
+    };
+
+    // Returns the z-ordered list of LayerSettings to pass to RenderEngine::drawLayers. The list
+    // may contain shadows casted by the layer or the content of the layer itself.  If the layer
+    // does not render then an empty list will be returned.
+    virtual std::vector<LayerSettings> prepareClientCompositionList(
+            ClientCompositionTargetSettings&) = 0;
 
     // Called after the layer is displayed to update the presentation fence
     virtual void onLayerDisplayed(const sp<Fence>&) = 0;
@@ -41,5 +137,61 @@
     virtual const char* getDebugName() const = 0;
 };
 
+// TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
+// be removed.
+struct LayerFESpHash {
+    size_t operator()(const sp<LayerFE>& p) const { return std::hash<LayerFE*>()(p.get()); }
+};
+
+using LayerFESet = std::unordered_set<sp<LayerFE>, LayerFESpHash>;
+
+static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs,
+                              const LayerFE::ClientCompositionTargetSettings& rhs) {
+    return lhs.clip.hasSameRects(rhs.clip) &&
+            lhs.useIdentityTransform == rhs.useIdentityTransform &&
+            lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure &&
+            lhs.supportsProtectedContent == rhs.supportsProtectedContent &&
+            lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.viewport == rhs.viewport &&
+            lhs.dataspace == rhs.dataspace &&
+            lhs.realContentIsVisible == rhs.realContentIsVisible &&
+            lhs.clearContent == rhs.clearContent;
+}
+
+static inline bool operator==(const LayerFE::LayerSettings& lhs,
+                              const LayerFE::LayerSettings& rhs) {
+    return static_cast<const renderengine::LayerSettings&>(lhs) ==
+            static_cast<const renderengine::LayerSettings&>(rhs) &&
+            lhs.bufferId == rhs.bufferId && lhs.frameNumber == rhs.frameNumber;
+}
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const LayerFE::ClientCompositionTargetSettings& settings,
+                           ::std::ostream* os) {
+    *os << "ClientCompositionTargetSettings{";
+    *os << "\n    .clip = \n";
+    PrintTo(settings.clip, os);
+    *os << "\n    .useIdentityTransform = " << settings.useIdentityTransform;
+    *os << "\n    .needsFiltering = " << settings.needsFiltering;
+    *os << "\n    .isSecure = " << settings.isSecure;
+    *os << "\n    .supportsProtectedContent = " << settings.supportsProtectedContent;
+    *os << "\n    .clearRegion = ";
+    PrintTo(settings.clearRegion, os);
+    *os << "\n    .viewport = ";
+    PrintTo(settings.viewport, os);
+    *os << "\n    .dataspace = ";
+    PrintTo(settings.dataspace, os);
+    *os << "\n    .realContentIsVisible = " << settings.realContentIsVisible;
+    *os << "\n    .clearContent = " << settings.clearContent;
+    *os << "\n}";
+}
+
+static inline void PrintTo(const LayerFE::LayerSettings& settings, ::std::ostream* os) {
+    *os << "LayerFE::LayerSettings{";
+    PrintTo(static_cast<const renderengine::LayerSettings&>(settings), os);
+    *os << "\n    .bufferId = " << settings.bufferId;
+    *os << "\n    .frameNumber = " << settings.frameNumber;
+    *os << "\n}";
+}
+
 } // namespace compositionengine
 } // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index e6ee078..b4ed92f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -18,27 +18,105 @@
 
 #include <cstdint>
 
-#include <gui/BufferQueue.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
 #include <ui/FloatRect.h>
-#include <ui/GraphicBuffer.h>
-#include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
 
-#include "DisplayHardware/ComposerHal.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <gui/BufferQueue.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+
+#include "DisplayHardware/Hal.h"
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
 
 namespace android::compositionengine {
 
+namespace hal = android::hardware::graphics::composer::hal;
+
+// More complex metadata for this layer
+struct GenericLayerMetadataEntry {
+    // True if the metadata may affect the composed result.
+    // See setLayerGenericMetadata in IComposerClient.hal
+    bool mandatory;
+
+    // Byte blob or parcel
+    std::vector<uint8_t> value;
+
+    std::string dumpAsString() const;
+};
+
+inline bool operator==(const GenericLayerMetadataEntry& lhs, const GenericLayerMetadataEntry& rhs) {
+    return lhs.mandatory == rhs.mandatory && lhs.value == rhs.value;
+}
+
+// Defining PrintTo helps with Google Tests.
+inline void PrintTo(const GenericLayerMetadataEntry& v, ::std::ostream* os) {
+    *os << v.dumpAsString();
+}
+
+using GenericLayerMetadataMap = std::unordered_map<std::string, GenericLayerMetadataEntry>;
+
 /*
  * Used by LayerFE::getCompositionState
  */
 struct LayerFECompositionState {
-    // TODO(lpique): b/121291683 Remove this one we are sure we don't need the
-    // value recomputed / set every frame.
-    Region geomVisibleRegion;
+    // If set to true, forces client composition on all output layers until
+    // the next geometry change.
+    bool forceClientComposition{false};
+
+    // TODO(b/121291683): Reorganize and rename the contents of this structure
+
+    /*
+     * Visibility state
+     */
+    // the layer stack this layer belongs to
+    std::optional<uint32_t> layerStackId;
+
+    // If true, this layer should be only visible on the internal display
+    bool internalOnly{false};
+
+    // If false, this layer should not be considered visible
+    bool isVisible{true};
+
+    // True if the layer is completely opaque
+    bool isOpaque{true};
+
+    // If true, invalidates the entire visible region
+    bool contentDirty{false};
+
+    // The alpha value for this layer
+    float alpha{1.f};
+
+    // Background blur in pixels
+    int backgroundBlurRadius{0};
+
+    // The transform from layer local coordinates to composition coordinates
+    ui::Transform geomLayerTransform;
+
+    // The inverse of the layer transform
+    ui::Transform geomInverseLayerTransform;
+
+    // The hint from the layer producer as to what portion of the layer is
+    // transparent.
+    Region transparentRegionHint;
+
+    // The blend mode for this layer
+    hal::BlendMode blendMode{hal::BlendMode::INVALID};
+
+    // The bounds of the layer in layer local coordinates
+    FloatRect geomLayerBounds;
+
+    // length of the shadow in screen space
+    float shadowRadius{0.f};
 
     /*
      * Geometry state
@@ -48,23 +126,9 @@
     bool geomUsesSourceCrop{false};
     bool geomBufferUsesDisplayInverseTransform{false};
     uint32_t geomBufferTransform{0};
-    ui::Transform geomLayerTransform;
-    ui::Transform geomInverseLayerTransform;
     Rect geomBufferSize;
     Rect geomContentCrop;
     Rect geomCrop;
-    Region geomActiveTransparentRegion;
-    FloatRect geomLayerBounds;
-
-    /*
-     * Presentation
-     */
-
-    // The blend mode for this layer
-    Hwc2::IComposerClient::BlendMode blendMode{Hwc2::IComposerClient::BlendMode::INVALID};
-
-    // The alpha value for this layer
-    float alpha{1.f};
 
     /*
      * Extra metadata
@@ -76,12 +140,14 @@
     // The appId for this layer
     int appId{0};
 
+    GenericLayerMetadataMap metadata;
+
     /*
      * Per-frame content
      */
 
     // The type of composition for this layer
-    Hwc2::IComposerClient::Composition compositionType{Hwc2::IComposerClient::Composition::INVALID};
+    hal::Composition compositionType{hal::Composition::INVALID};
 
     // The buffer and related state
     sp<GraphicBuffer> buffer;
@@ -93,12 +159,16 @@
     sp<NativeHandle> sidebandStream;
 
     // The color for this layer
-    Hwc2::IComposerClient::Color color;
+    half4 color;
 
     /*
      * Per-frame presentation state
      */
 
+    // If true, this layer will use the dataspace chosen for the output and
+    // ignore the dataspace value just below
+    bool isColorspaceAgnostic{false};
+
     // The dataspace for this layer
     ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
 
@@ -107,6 +177,22 @@
 
     // The color transform
     mat4 colorTransform;
+    bool colorTransformIsIdentity{true};
+
+    // True if the layer has protected content
+    bool hasProtectedContent{false};
+
+    /*
+     * Cursor state
+     */
+
+    // The output-independent frame for the cursor
+    Rect cursorFrame;
+
+    virtual ~LayerFECompositionState();
+
+    // Debugging
+    virtual void dump(std::string& out) const;
 };
 
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 54e6bd6..baf5258 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -17,10 +17,16 @@
 #pragma once
 
 #include <cstdint>
+#include <iterator>
 #include <optional>
 #include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
 
-#include <math/mat4.h>
+#include <compositionengine/LayerFE.h>
+#include <renderengine/LayerSettings.h>
+#include <ui/Fence.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -28,14 +34,22 @@
 
 #include "DisplayHardware/DisplayIdentification.h"
 
-namespace android::compositionengine {
+namespace android {
+
+namespace HWC2 {
+class Layer;
+} // namespace HWC2
+
+namespace compositionengine {
 
 class DisplayColorProfile;
-class Layer;
 class LayerFE;
 class RenderSurface;
 class OutputLayer;
 
+struct CompositionRefreshArgs;
+struct LayerFECompositionState;
+
 namespace impl {
 struct OutputCompositionState;
 } // namespace impl
@@ -45,7 +59,95 @@
  */
 class Output {
 public:
-    using OutputLayers = std::vector<std::unique_ptr<compositionengine::OutputLayer>>;
+    using ReleasedLayers = std::vector<wp<LayerFE>>;
+    using UniqueFELayerStateMap = std::unordered_map<LayerFE*, LayerFECompositionState*>;
+
+    // A helper class for enumerating the output layers using a C++11 ranged-based for loop
+    template <typename T>
+    class OutputLayersEnumerator {
+    public:
+        // TODO(lpique): Consider turning this into a C++20 view when possible.
+        template <bool IsConstIter>
+        class IteratorImpl {
+        public:
+            // Required definitions to be considered an iterator
+            using iterator_category = std::forward_iterator_tag;
+            using value_type = decltype(std::declval<T>().getOutputLayerOrderedByZByIndex(0));
+            using difference_type = std::ptrdiff_t;
+            using pointer = std::conditional_t<IsConstIter, const value_type*, value_type*>;
+            using reference = std::conditional_t<IsConstIter, const value_type&, value_type&>;
+
+            IteratorImpl() = default;
+            IteratorImpl(const T* output, size_t index) : mOutput(output), mIndex(index) {}
+
+            value_type operator*() const {
+                return mOutput->getOutputLayerOrderedByZByIndex(mIndex);
+            }
+            value_type operator->() const {
+                return mOutput->getOutputLayerOrderedByZByIndex(mIndex);
+            }
+
+            bool operator==(const IteratorImpl& other) const {
+                return mOutput == other.mOutput && mIndex == other.mIndex;
+            }
+            bool operator!=(const IteratorImpl& other) const { return !operator==(other); }
+
+            IteratorImpl& operator++() {
+                ++mIndex;
+                return *this;
+            }
+            IteratorImpl operator++(int) {
+                auto prev = *this;
+                ++mIndex;
+                return prev;
+            }
+
+        private:
+            const T* mOutput{nullptr};
+            size_t mIndex{0};
+        };
+
+        using iterator = IteratorImpl<false>;
+        using const_iterator = IteratorImpl<true>;
+
+        explicit OutputLayersEnumerator(const T& output) : mOutput(output) {}
+        auto begin() const { return iterator(&mOutput, 0); }
+        auto end() const { return iterator(&mOutput, mOutput.getOutputLayerCount()); }
+        auto cbegin() const { return const_iterator(&mOutput, 0); }
+        auto cend() const { return const_iterator(&mOutput, mOutput.getOutputLayerCount()); }
+
+    private:
+        const T& mOutput;
+    };
+
+    struct FrameFences {
+        sp<Fence> presentFence{Fence::NO_FENCE};
+        sp<Fence> clientTargetAcquireFence{Fence::NO_FENCE};
+        std::unordered_map<HWC2::Layer*, sp<Fence>> layerFences;
+    };
+
+    struct ColorProfile {
+        ui::ColorMode mode{ui::ColorMode::NATIVE};
+        ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+        ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
+        ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN};
+    };
+
+    // Use internally to incrementally compute visibility/coverage
+    struct CoverageState {
+        explicit CoverageState(LayerFESet& latchedLayers) : latchedLayers(latchedLayers) {}
+
+        // The set of layers that had been latched for the coverage calls, to
+        // avoid duplicate requests to obtain the same front-end layer state.
+        LayerFESet& latchedLayers;
+
+        // The region of the output which is covered by layers
+        Region aboveCoveredLayers;
+        // The region of the output which is opaquely covered by layers
+        Region aboveOpaqueLayers;
+        // The region of the output which should be considered dirty
+        Region dirtyRegion;
+    };
 
     virtual ~Output();
 
@@ -54,12 +156,16 @@
     // constructor.
     virtual bool isValid() const = 0;
 
+    // Returns the DisplayId the output represents, if it has one
+    virtual std::optional<DisplayId> getDisplayId() const = 0;
+
     // Enables (or disables) composition on this output
     virtual void setCompositionEnabled(bool) = 0;
 
     // Sets the projection state to use
-    virtual void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
-                               const Rect& viewport, const Rect& scissor, bool needsFiltering) = 0;
+    virtual void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
+                               const Rect& viewport, const Rect& sourceClip,
+                               const Rect& destinationClip, bool needsFiltering) = 0;
     // Sets the bounds to use
     virtual void setBounds(const ui::Size&) = 0;
 
@@ -67,11 +173,8 @@
     // belongsInOutput for full details.
     virtual void setLayerStackFilter(uint32_t layerStackId, bool isInternal) = 0;
 
-    // Sets the color transform matrix to use
-    virtual void setColorTransform(const mat4&) = 0;
-
     // Sets the output color mode
-    virtual void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) = 0;
+    virtual void setColorProfile(const ColorProfile&) = 0;
 
     // Outputs a string with a state dump
     virtual void dump(std::string&) const = 0;
@@ -111,28 +214,73 @@
     // A layer belongs to the output if its layerStackId matches. Additionally
     // if the layer should only show in the internal (primary) display only and
     // this output allows that.
-    virtual bool belongsInOutput(uint32_t layerStackId, bool internalOnly) const = 0;
+    virtual bool belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const = 0;
+
+    // Determines if a layer belongs to the output.
+    virtual bool belongsInOutput(const sp<LayerFE>&) const = 0;
 
     // Returns a pointer to the output layer corresponding to the given layer on
     // this output, or nullptr if the layer does not have one
-    virtual OutputLayer* getOutputLayerForLayer(Layer*) const = 0;
+    virtual OutputLayer* getOutputLayerForLayer(const sp<LayerFE>&) const = 0;
 
-    // Gets the OutputLayer corresponding to the input Layer instance from the
-    // current ordered set of output layers. If there is no such layer, a new
-    // one is created and returned.
-    virtual std::unique_ptr<OutputLayer> getOrCreateOutputLayer(std::optional<DisplayId>,
-                                                                std::shared_ptr<Layer>,
-                                                                sp<LayerFE>) = 0;
+    // Immediately clears all layers from the output.
+    virtual void clearOutputLayers() = 0;
 
-    // Sets the new ordered set of output layers for this output
-    virtual void setOutputLayersOrderedByZ(OutputLayers&&) = 0;
+    // For tests use only. Creates and appends an OutputLayer into the output.
+    virtual OutputLayer* injectOutputLayerForTest(const sp<LayerFE>&) = 0;
 
-    // Gets the ordered set of output layers for this output
-    virtual const OutputLayers& getOutputLayersOrderedByZ() const = 0;
+    // Gets the count of output layers managed by this output
+    virtual size_t getOutputLayerCount() const = 0;
+
+    // Gets an output layer in Z order given its index
+    virtual OutputLayer* getOutputLayerOrderedByZByIndex(size_t) const = 0;
+
+    // A helper function for enumerating all the output layers in Z order using
+    // a C++11 range-based for loop.
+    auto getOutputLayersOrderedByZ() const { return OutputLayersEnumerator(*this); }
+
+    // Sets the new set of layers being released this frame
+    virtual void setReleasedLayers(ReleasedLayers&&) = 0;
+
+    // Prepare the output, updating the OutputLayers used in the output
+    virtual void prepare(const CompositionRefreshArgs&, LayerFESet&) = 0;
+
+    // Presents the output, finalizing all composition details
+    virtual void present(const CompositionRefreshArgs&) = 0;
+
+    // Latches the front-end layer state for each output layer
+    virtual void updateLayerStateFromFE(const CompositionRefreshArgs&) const = 0;
 
 protected:
     virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
     virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
+
+    virtual void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) = 0;
+    virtual void collectVisibleLayers(const CompositionRefreshArgs&, CoverageState&) = 0;
+    virtual void ensureOutputLayerIfVisible(sp<LayerFE>&, CoverageState&) = 0;
+    virtual void setReleasedLayers(const CompositionRefreshArgs&) = 0;
+
+    virtual void updateAndWriteCompositionState(const CompositionRefreshArgs&) = 0;
+    virtual void setColorTransform(const CompositionRefreshArgs&) = 0;
+    virtual void updateColorProfile(const CompositionRefreshArgs&) = 0;
+    virtual void beginFrame() = 0;
+    virtual void prepareFrame() = 0;
+    virtual void devOptRepaintFlash(const CompositionRefreshArgs&) = 0;
+    virtual void finishFrame(const CompositionRefreshArgs&) = 0;
+    virtual std::optional<base::unique_fd> composeSurfaces(
+            const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) = 0;
+    virtual void postFramebuffer() = 0;
+    virtual void chooseCompositionStrategy() = 0;
+    virtual bool getSkipColorTransform() const = 0;
+    virtual FrameFences presentAndGetFrameFences() = 0;
+    virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
+            bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) = 0;
+    virtual void appendRegionFlashRequests(
+            const Region& flashRegion,
+            std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0;
+    virtual void setExpensiveRenderingExpected(bool enabled) = 0;
+    virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0;
 };
 
-} // namespace android::compositionengine
+} // namespace compositionengine
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputColorSetting.h
similarity index 71%
rename from services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/OutputColorSetting.h
index db3312b..6e798ce 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputColorSetting.h
@@ -16,20 +16,12 @@
 
 #pragma once
 
-#include <utils/RefBase.h>
-
 namespace android::compositionengine {
 
-class CompositionEngine;
-class LayerFE;
-
-/**
- * A parameter object for creating Layer instances
- */
-struct LayerCreationArgs {
-    // A weak pointer to the front-end layer instance that the new layer will
-    // represent.
-    wp<LayerFE> layerFE;
+enum class OutputColorSetting : int32_t {
+    kManaged = 0,
+    kUnmanaged = 1,
+    kEnhanced = 2,
 };
 
-} // namespace android::compositionengine
+} // namespace android::compositionengine
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index cd63b57..aa70ef8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -19,17 +19,29 @@
 #include <optional>
 #include <string>
 
+#include <ui/Transform.h>
 #include <utils/StrongPointer.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/DisplayIdentification.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android {
 
+namespace HWC2 {
+class Layer;
+} // namespace HWC2
+
 namespace compositionengine {
 
 class CompositionEngine;
 class Output;
-class Layer;
 class LayerFE;
 
 namespace impl {
@@ -43,12 +55,12 @@
 public:
     virtual ~OutputLayer();
 
+    // Sets the HWC2::Layer associated with this layer
+    virtual void setHwcLayer(std::shared_ptr<HWC2::Layer>) = 0;
+
     // Gets the output which owns this output layer
     virtual const Output& getOutput() const = 0;
 
-    // Gets the display-independent layer which this output layer represents
-    virtual Layer& getLayer() const = 0;
-
     // Gets the front-end layer interface this output layer represents
     virtual LayerFE& getLayerFE() const = 0;
 
@@ -66,12 +78,41 @@
 
     // 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) = 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
     // skipped.
-    virtual void writeStateToHWC(bool includeGeometry) const = 0;
+    virtual void writeStateToHWC(bool includeGeometry) = 0;
+
+    // Updates the cursor position with the HWC
+    virtual void writeCursorPositionToHWC() const = 0;
+
+    // Returns the HWC2::Layer associated with this layer, if it exists
+    virtual HWC2::Layer* getHwcLayer() const = 0;
+
+    // Returns true if the current layer state requires client composition
+    virtual bool requiresClientComposition() const = 0;
+
+    // Returns true if the current layer should be treated as a cursor layer
+    virtual bool isHardwareCursor() const = 0;
+
+    // Applies a HWC device requested composition type change
+    virtual void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) = 0;
+
+    // Prepares to apply any HWC device layer requests
+    virtual void prepareForDeviceLayerRequests() = 0;
+
+    // Applies a HWC device layer request
+    virtual void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) = 0;
+
+    // Returns true if the composition settings scale pixels
+    virtual bool needsFiltering() const = 0;
 
     // Debugging
     virtual void dump(std::string& result) const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index e21128c..f680460 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -63,6 +63,12 @@
     // Sets the dataspace used for rendering the surface
     virtual void setBufferDataspace(ui::Dataspace) = 0;
 
+    // Sets the pixel format used for rendering the surface.
+    // Changing the pixel format of the buffer will result in buffer
+    // reallocation as well as some reconfiguration of the graphics context,
+    // which are both expensive operations.
+    virtual void setBufferPixelFormat(ui::PixelFormat) = 0;
+
     // Configures the protected rendering on the surface
     virtual void setProtected(bool useProtected) = 0;
 
@@ -71,22 +77,18 @@
     virtual status_t beginFrame(bool mustRecompose) = 0;
 
     // Prepares the frame for rendering
-    virtual status_t prepareFrame() = 0;
+    virtual void prepareFrame(bool usesClientComposition, bool usesDeviceComposition) = 0;
 
     // Allocates a buffer as scratch space for GPU composition
     virtual sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) = 0;
 
     // Queues the drawn buffer for consumption by HWC. readyFence is the fence
     // which will fire when the buffer is ready for consumption.
-    virtual void queueBuffer(base::unique_fd&& readyFence) = 0;
+    virtual void queueBuffer(base::unique_fd readyFence) = 0;
 
     // Called after the HWC calls are made to present the display
     virtual void onPresentDisplayCompleted() = 0;
 
-    // Called to set the viewport and projection state for rendering into this
-    // surface
-    virtual void setViewportAndProjection() = 0;
-
     // Called after the surface has been rendering to signal the surface should
     // be made ready for displaying
     virtual void flip() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/ClientCompositionRequestCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/ClientCompositionRequestCache.h
new file mode 100644
index 0000000..c14a6e8
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/ClientCompositionRequestCache.h
@@ -0,0 +1,66 @@
+/*
+ * 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 <cstdint>
+#include <deque>
+
+#include <compositionengine/LayerFE.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/LayerSettings.h>
+
+namespace android {
+
+namespace compositionengine::impl {
+
+// The cache is used to skip duplicate client composition requests. We do so by keeping track
+// of every composition request and the buffer that the request is rendered into. During the
+// next composition request, if the request matches what was rendered into the buffer, then
+// we can skip of the request, pass back an empty fence, and let HWC use the previous render
+// result.
+//
+// The cache is a mapping of the RenderSurface buffer id (unique per process) and a snapshot of
+// the composition request. We need to make sure the request, including the order of the
+// layers, do not change from call to call. The snapshot removes strong references to the
+// client buffer id so we don't extend the lifetime of the buffer by storing it in the cache.
+class ClientCompositionRequestCache {
+public:
+    explicit ClientCompositionRequestCache(uint32_t cacheSize) : mMaxCacheSize(cacheSize){};
+    ~ClientCompositionRequestCache() = default;
+    bool exists(uint64_t bufferId, const renderengine::DisplaySettings& display,
+                const std::vector<LayerFE::LayerSettings>& layerSettings) const;
+    void add(uint64_t bufferId, const renderengine::DisplaySettings& display,
+             const std::vector<LayerFE::LayerSettings>& layerSettings);
+    void remove(uint64_t bufferId);
+
+private:
+    uint32_t mMaxCacheSize;
+    struct ClientCompositionRequest {
+        renderengine::DisplaySettings display;
+        std::vector<LayerFE::LayerSettings> layerSettings;
+        ClientCompositionRequest(const renderengine::DisplaySettings& _display,
+                                 const std::vector<LayerFE::LayerSettings>& _layerSettings);
+        bool equals(const renderengine::DisplaySettings& _display,
+                    const std::vector<LayerFE::LayerSettings>& _layerSettings) const;
+    };
+
+    // Cache of requests, keyed by corresponding GraphicBuffer ID.
+    std::deque<std::pair<uint64_t /* bufferId */, ClientCompositionRequest>> mCache;
+};
+
+} // namespace compositionengine::impl
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index b01eb64..386808d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -26,9 +26,9 @@
     ~CompositionEngine() override;
 
     std::shared_ptr<compositionengine::Display> createDisplay(
-            compositionengine::DisplayCreationArgs&&) override;
-    std::shared_ptr<compositionengine::Layer> createLayer(
-            compositionengine::LayerCreationArgs&&) override;
+            const compositionengine::DisplayCreationArgs&) override;
+    std::unique_ptr<compositionengine::LayerFECompositionState> createLayerFECompositionState()
+            override;
 
     HWComposer& getHwComposer() const override;
     void setHwComposer(std::unique_ptr<HWComposer>) override;
@@ -36,9 +36,32 @@
     renderengine::RenderEngine& getRenderEngine() const override;
     void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override;
 
+    TimeStats& getTimeStats() const override;
+    void setTimeStats(const std::shared_ptr<TimeStats>&) override;
+
+    bool needsAnotherUpdate() const override;
+    nsecs_t getLastFrameRefreshTimestamp() const override;
+
+    void present(CompositionRefreshArgs&) override;
+
+    void updateCursorAsync(CompositionRefreshArgs&) override;
+
+    void preComposition(CompositionRefreshArgs&) override;
+
+    // Debugging
+    void dump(std::string&) const override;
+
+    void updateLayerStateFromFE(CompositionRefreshArgs& args);
+
+    // Testing
+    void setNeedsAnotherUpdateForTest(bool);
+
 private:
     std::unique_ptr<HWComposer> mHwComposer;
     std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+    std::shared_ptr<TimeStats> mTimeStats;
+    bool mNeedsAnotherUpdate = false;
+    nsecs_t mRefreshStartTime = 0;
 };
 
 std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 0e20c43..7a4f738 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -19,42 +19,97 @@
 #include <memory>
 
 #include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/RenderSurface.h>
 #include <compositionengine/impl/Output.h>
+#include <ui/PixelFormat.h>
+#include <ui/Size.h>
 
 #include "DisplayHardware/DisplayIdentification.h"
+#include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/PowerAdvisor.h"
 
 namespace android::compositionengine {
 
 class CompositionEngine;
 
-struct DisplayCreationArgs;
-
 namespace impl {
 
-class Display : public compositionengine::impl::Output, public compositionengine::Display {
+// The implementation class contains the common implementation, but does not
+// actually contain the final display state.
+class Display : public compositionengine::impl::Output, public virtual compositionengine::Display {
 public:
-    Display(const CompositionEngine&, compositionengine::DisplayCreationArgs&&);
     virtual ~Display();
 
     // compositionengine::Output overrides
+    std::optional<DisplayId> getDisplayId() const override;
+    bool isValid() const override;
     void dump(std::string&) const override;
-    void setColorTransform(const mat4&) override;
-    void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+    using compositionengine::impl::Output::setReleasedLayers;
+    void setReleasedLayers(const CompositionRefreshArgs&) override;
+    void setColorTransform(const CompositionRefreshArgs&) override;
+    void setColorProfile(const ColorProfile&) override;
+    void chooseCompositionStrategy() override;
+    bool getSkipColorTransform() const override;
+    compositionengine::Output::FrameFences presentAndGetFrameFences() override;
+    void setExpensiveRenderingExpected(bool) override;
+    void finishFrame(const CompositionRefreshArgs&) override;
 
     // compositionengine::Display overrides
     const std::optional<DisplayId>& getId() const override;
     bool isSecure() const override;
     bool isVirtual() const override;
     void disconnect() override;
-    void createDisplayColorProfile(compositionengine::DisplayColorProfileCreationArgs&&) override;
-    void createRenderSurface(compositionengine::RenderSurfaceCreationArgs&&) override;
+    void createDisplayColorProfile(
+            const compositionengine::DisplayColorProfileCreationArgs&) override;
+    void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
+    void createClientCompositionCache(uint32_t cacheSize) override;
+
+    // Internal helpers used by chooseCompositionStrategy()
+    using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
+    using DisplayRequests = android::HWComposer::DeviceRequestedChanges::DisplayRequests;
+    using LayerRequests = android::HWComposer::DeviceRequestedChanges::LayerRequests;
+    using ClientTargetProperty = android::HWComposer::DeviceRequestedChanges::ClientTargetProperty;
+    virtual bool anyLayersRequireClientComposition() const;
+    virtual bool allLayersRequireClientComposition() const;
+    virtual void applyChangedTypesToLayers(const ChangedTypes&);
+    virtual void applyDisplayRequests(const DisplayRequests&);
+    virtual void applyLayerRequestsToLayers(const LayerRequests&);
+    virtual void applyClientTargetRequests(const ClientTargetProperty&);
+
+    // Internal
+    virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
+    virtual std::optional<DisplayId> maybeAllocateDisplayIdForVirtualDisplay(ui::Size,
+                                                                             ui::PixelFormat) const;
+    std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
+
+    // Testing
+    void setDisplayIdForTesting(std::optional<DisplayId> displayId);
 
 private:
-    const bool mIsVirtual;
+    bool mIsVirtual = false;
     std::optional<DisplayId> mId;
+    Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
 };
 
-std::shared_ptr<compositionengine::Display> createDisplay(
-        const compositionengine::CompositionEngine&, compositionengine::DisplayCreationArgs&&);
+// This template factory function standardizes the implementation details of the
+// final class using the types actually required by the implementation. This is
+// not possible to do in the base class as those types may not even be visible
+// to the base code.
+template <typename BaseDisplay, typename CompositionEngine>
+std::shared_ptr<BaseDisplay> createDisplayTemplated(
+        const CompositionEngine& compositionEngine,
+        const compositionengine::DisplayCreationArgs& args) {
+    auto display = createOutputTemplated<BaseDisplay>(compositionEngine);
+
+    display->setConfiguration(args);
+
+    return display;
+}
+
+std::shared_ptr<Display> createDisplay(const compositionengine::CompositionEngine&,
+                                       const compositionengine::DisplayCreationArgs&);
+
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
index 49c2d2c..9bc0e68 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
@@ -33,7 +33,7 @@
 
 class DisplayColorProfile : public compositionengine::DisplayColorProfile {
 public:
-    DisplayColorProfile(DisplayColorProfileCreationArgs&&);
+    DisplayColorProfile(const DisplayColorProfileCreationArgs&);
     ~DisplayColorProfile() override;
 
     bool isValid() const override;
@@ -54,6 +54,8 @@
     bool hasDolbyVisionSupport() const override;
 
     const HdrCapabilities& getHdrCapabilities() const override;
+    bool isDataspaceSupported(ui::Dataspace) const override;
+    ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace) const override;
 
     void dump(std::string&) const override;
 
@@ -89,7 +91,7 @@
 };
 
 std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
-        DisplayColorProfileCreationArgs&&);
+        const DisplayColorProfileCreationArgs&);
 
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index 8eec035..2864c10 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -19,7 +19,15 @@
 #include <cstdint>
 #include <vector>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <gui/BufferQueue.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 #include <utils/StrongPointer.h>
 
 namespace android {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
deleted file mode 100644
index 3e56b21..0000000
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <memory>
-
-#include <compositionengine/Layer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-#include <utils/RefBase.h>
-#include <utils/StrongPointer.h>
-
-namespace android::compositionengine {
-
-class CompositionEngine;
-class LayerFE;
-
-struct LayerCreationArgs;
-
-namespace impl {
-
-class Display;
-
-class Layer : public compositionengine::Layer {
-public:
-    Layer(const CompositionEngine&, compositionengine::LayerCreationArgs&&);
-    ~Layer() override;
-
-    sp<LayerFE> getLayerFE() const override;
-
-    const LayerCompositionState& getState() const override;
-    LayerCompositionState& editState() override;
-
-    void dump(std::string& result) const override;
-
-private:
-    const compositionengine::CompositionEngine& mCompositionEngine;
-    const wp<LayerFE> mLayerFE;
-
-    LayerCompositionState mState;
-};
-
-std::shared_ptr<compositionengine::Layer> createLayer(const compositionengine::CompositionEngine&,
-                                                      compositionengine::LayerCreationArgs&&);
-
-} // namespace impl
-} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index b1d1f42..6f25e63 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -16,36 +16,36 @@
 
 #pragma once
 
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Output.h>
+#include <compositionengine/impl/ClientCompositionRequestCache.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/LayerSettings.h>
 #include <memory>
 #include <utility>
 #include <vector>
 
-#include <compositionengine/Output.h>
-#include <compositionengine/impl/OutputCompositionState.h>
+namespace android::compositionengine::impl {
 
-namespace android::compositionengine {
-
-class CompositionEngine;
-class Layer;
-class OutputLayer;
-
-namespace impl {
-
+// The implementation class contains the common implementation, but does not
+// actually contain the final output state.
 class Output : public virtual compositionengine::Output {
 public:
-    Output(const CompositionEngine&);
     ~Output() override;
 
+    // compositionengine::Output overrides
     bool isValid() const override;
-
+    std::optional<DisplayId> getDisplayId() const override;
     void setCompositionEnabled(bool) override;
-    void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
-                       const Rect& viewport, const Rect& scissor, bool needsFiltering) override;
+    void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
+                       const Rect& viewport, const Rect& sourceClip, const Rect& destinationClip,
+                       bool needsFiltering) override;
     void setBounds(const ui::Size&) override;
     void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override;
 
-    void setColorTransform(const mat4&) override;
-    void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+    void setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
+    void setColorProfile(const ColorProfile&) override;
 
     void dump(std::string&) const override;
 
@@ -58,42 +58,175 @@
     compositionengine::RenderSurface* getRenderSurface() const override;
     void setRenderSurface(std::unique_ptr<compositionengine::RenderSurface>) override;
 
-    const OutputCompositionState& getState() const override;
-    OutputCompositionState& editState() override;
-
     Region getDirtyRegion(bool repaintEverything) const override;
-    bool belongsInOutput(uint32_t, bool) const override;
+    bool belongsInOutput(std::optional<uint32_t>, bool) const override;
+    bool belongsInOutput(const sp<LayerFE>&) const override;
 
-    compositionengine::OutputLayer* getOutputLayerForLayer(
-            compositionengine::Layer*) const override;
-    std::unique_ptr<compositionengine::OutputLayer> getOrCreateOutputLayer(
-            std::optional<DisplayId>, std::shared_ptr<compositionengine::Layer>,
-            sp<LayerFE>) override;
-    void setOutputLayersOrderedByZ(OutputLayers&&) override;
-    const OutputLayers& getOutputLayersOrderedByZ() const override;
+    compositionengine::OutputLayer* getOutputLayerForLayer(const sp<LayerFE>&) const override;
+
+    void setReleasedLayers(ReleasedLayers&&) override;
+
+    void prepare(const CompositionRefreshArgs&, LayerFESet&) override;
+    void present(const CompositionRefreshArgs&) override;
+
+    void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
+    void collectVisibleLayers(const CompositionRefreshArgs&,
+                              compositionengine::Output::CoverageState&) override;
+    void ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>&,
+                                    compositionengine::Output::CoverageState&) override;
+    void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
+
+    void updateLayerStateFromFE(const CompositionRefreshArgs&) const override;
+    void updateAndWriteCompositionState(const compositionengine::CompositionRefreshArgs&) override;
+    void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
+    void beginFrame() override;
+    void prepareFrame() override;
+    void devOptRepaintFlash(const CompositionRefreshArgs&) override;
+    void finishFrame(const CompositionRefreshArgs&) override;
+    std::optional<base::unique_fd> composeSurfaces(
+            const Region&, const compositionengine::CompositionRefreshArgs& refreshArgs) override;
+    void postFramebuffer() override;
+    void cacheClientCompositionRequests(uint32_t) override;
 
     // Testing
+    const ReleasedLayers& getReleasedLayersForTest() const;
     void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
     void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
 
 protected:
-    const CompositionEngine& getCompositionEngine() const;
+    std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
+    std::optional<size_t> findCurrentOutputLayerForLayer(
+            const sp<compositionengine::LayerFE>&) const;
+    void chooseCompositionStrategy() override;
+    bool getSkipColorTransform() const override;
+    compositionengine::Output::FrameFences presentAndGetFrameFences() override;
+    std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
+            bool supportsProtectedContent, Region& clearRegion,
+            ui::Dataspace outputDataspace) override;
+    void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override;
+    void setExpensiveRenderingExpected(bool enabled) override;
     void dumpBase(std::string&) const;
 
+    // Implemented by the final implementation for the final state it uses.
+    virtual compositionengine::OutputLayer* ensureOutputLayer(std::optional<size_t>,
+                                                              const sp<LayerFE>&) = 0;
+    virtual compositionengine::OutputLayer* injectOutputLayerForTest(const sp<LayerFE>&) = 0;
+    virtual void finalizePendingOutputLayers() = 0;
+    virtual const compositionengine::CompositionEngine& getCompositionEngine() const = 0;
+    virtual void dumpState(std::string& out) const = 0;
+
 private:
     void dirtyEntireOutput();
-
-    const CompositionEngine& mCompositionEngine;
+    compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const;
+    ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const;
+    compositionengine::Output::ColorProfile pickColorProfile(
+            const compositionengine::CompositionRefreshArgs&) const;
 
     std::string mName;
 
-    OutputCompositionState mState;
-
     std::unique_ptr<compositionengine::DisplayColorProfile> mDisplayColorProfile;
     std::unique_ptr<compositionengine::RenderSurface> mRenderSurface;
 
-    OutputLayers mOutputLayersOrderedByZ;
+    ReleasedLayers mReleasedLayers;
+    OutputLayer* mLayerRequestingBackgroundBlur = nullptr;
+    std::unique_ptr<ClientCompositionRequestCache> mClientCompositionRequestCache;
 };
 
-} // namespace impl
-} // namespace android::compositionengine
+// This template factory function standardizes the implementation details of the
+// final class using the types actually required by the implementation. This is
+// not possible to do in the base class as those types may not even be visible
+// to the base code.
+template <typename BaseOutput, typename CompositionEngine, typename... Args>
+std::shared_ptr<BaseOutput> createOutputTemplated(const CompositionEngine& compositionEngine,
+                                                  Args... args) {
+    class Output final : public BaseOutput {
+    public:
+// Clang incorrectly complains that these are unused.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-local-typedef"
+
+        using OutputCompositionState = std::remove_const_t<
+                std::remove_reference_t<decltype(std::declval<BaseOutput>().getState())>>;
+        using OutputLayer = std::remove_pointer_t<decltype(
+                std::declval<BaseOutput>().getOutputLayerOrderedByZByIndex(0))>;
+
+#pragma clang diagnostic pop
+
+        explicit Output(const CompositionEngine& compositionEngine, Args... args)
+              : BaseOutput(std::forward<Args>(args)...), mCompositionEngine(compositionEngine) {}
+        ~Output() override = default;
+
+    private:
+        // compositionengine::Output overrides
+        const OutputCompositionState& getState() const override { return mState; }
+
+        OutputCompositionState& editState() override { return mState; }
+
+        size_t getOutputLayerCount() const override {
+            return mCurrentOutputLayersOrderedByZ.size();
+        }
+
+        OutputLayer* getOutputLayerOrderedByZByIndex(size_t index) const override {
+            if (index >= mCurrentOutputLayersOrderedByZ.size()) {
+                return nullptr;
+            }
+            return mCurrentOutputLayersOrderedByZ[index].get();
+        }
+
+        // compositionengine::impl::Output overrides
+        const CompositionEngine& getCompositionEngine() const override {
+            return mCompositionEngine;
+        };
+
+        OutputLayer* ensureOutputLayer(std::optional<size_t> prevIndex,
+                                       const sp<LayerFE>& layerFE) {
+            auto outputLayer = (prevIndex && *prevIndex <= mCurrentOutputLayersOrderedByZ.size())
+                    ? std::move(mCurrentOutputLayersOrderedByZ[*prevIndex])
+                    : BaseOutput::createOutputLayer(layerFE);
+            auto result = outputLayer.get();
+            mPendingOutputLayersOrderedByZ.emplace_back(std::move(outputLayer));
+            return result;
+        }
+
+        void finalizePendingOutputLayers() override {
+            // The pending layers are added in reverse order. Reverse them to
+            // get the back-to-front ordered list of layers.
+            std::reverse(mPendingOutputLayersOrderedByZ.begin(),
+                         mPendingOutputLayersOrderedByZ.end());
+
+            mCurrentOutputLayersOrderedByZ = std::move(mPendingOutputLayersOrderedByZ);
+        }
+
+        void dumpState(std::string& out) const override { mState.dump(out); }
+
+        OutputLayer* injectOutputLayerForTest(const sp<LayerFE>& layerFE) override {
+            auto outputLayer = BaseOutput::createOutputLayer(layerFE);
+            auto result = outputLayer.get();
+            mCurrentOutputLayersOrderedByZ.emplace_back(std::move(outputLayer));
+            return result;
+        }
+
+        // Note: This is declared as a private virtual non-override so it can be
+        // an override implementation in the unit tests, but otherwise is not an
+        // accessible override for the normal implementation.
+        virtual void injectOutputLayerForTest(std::unique_ptr<OutputLayer> outputLayer) {
+            mCurrentOutputLayersOrderedByZ.emplace_back(std::move(outputLayer));
+        }
+
+        void clearOutputLayers() override {
+            mCurrentOutputLayersOrderedByZ.clear();
+            mPendingOutputLayersOrderedByZ.clear();
+        }
+
+        const CompositionEngine& mCompositionEngine;
+        OutputCompositionState mState;
+        std::vector<std::unique_ptr<OutputLayer>> mCurrentOutputLayersOrderedByZ;
+        std::vector<std::unique_ptr<OutputLayer>> mPendingOutputLayersOrderedByZ;
+    };
+
+    return std::make_shared<Output>(compositionEngine, std::forward<Args>(args)...);
+}
+
+std::shared_ptr<Output> createOutput(const compositionengine::CompositionEngine&);
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 0c47eb5..66ed2b6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -19,7 +19,16 @@
 #include <cstdint>
 
 #include <math/mat4.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <ui/GraphicTypes.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -35,6 +44,19 @@
     // If false, this output is not considered secure
     bool isSecure{false};
 
+    // If true, the current frame on this output uses client composition
+    bool usesClientComposition{false};
+
+    // If true, the current frame on this output uses device composition
+    bool usesDeviceComposition{false};
+
+    // If true, the client target should be flipped when performing client
+    // composition
+    bool flipClientTarget{false};
+
+    // If true, the current frame reused the buffer from a previous client composition
+    bool reusedClientComposition{false};
+
     // If true, this output displays layers that are internal-only
     bool layerStackInternal{false};
 
@@ -57,8 +79,11 @@
     // The logical space user viewport rectangle
     Rect viewport;
 
-    // The physical space scissor rectangle
-    Rect scissor;
+    // The physical space source clip rectangle
+    Rect sourceClip;
+
+    // The physical space destination clip rectangle
+    Rect destinationClip;
 
     // If true, RenderEngine filtering should be enabled
     bool needsFiltering{false};
@@ -76,11 +101,8 @@
     // True if the last composition frame had visible layers
     bool lastCompositionHadVisibleLayers{false};
 
-    // The color transform to apply
-    android_color_transform_t colorTransform{HAL_COLOR_TRANSFORM_IDENTITY};
-
-    // The color transform matrix to apply, corresponding with colorTransform.
-    mat4 colorTransformMat;
+    // The color transform matrix to apply
+    mat4 colorTransformMatrix;
 
     // Current active color mode
     ui::ColorMode colorMode{ui::ColorMode::NATIVE};
@@ -88,9 +110,12 @@
     // Current active render intent
     ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
 
-    // Current active dstaspace
+    // Current active dataspace
     ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
 
+    // Current target dataspace
+    ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN};
+
     // Debugging
     void dump(std::string& result) const;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 6a4818f..8cb5ae8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -20,50 +20,109 @@
 #include <string>
 
 #include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <ui/FloatRect.h>
 #include <ui/Rect.h>
 
 #include "DisplayHardware/DisplayIdentification.h"
 
-namespace android::compositionengine::impl {
+namespace android::compositionengine {
 
-class OutputLayer : public compositionengine::OutputLayer {
+struct LayerFECompositionState;
+
+namespace impl {
+
+// The implementation class contains the common implementation, but does not
+// actually contain the final layer state.
+class OutputLayer : public virtual compositionengine::OutputLayer {
 public:
-    OutputLayer(const compositionengine::Output&, std::shared_ptr<compositionengine::Layer>,
-                sp<compositionengine::LayerFE>);
     ~OutputLayer() override;
 
-    void initialize(const CompositionEngine&, std::optional<DisplayId>);
+    void setHwcLayer(std::shared_ptr<HWC2::Layer>) override;
 
-    const compositionengine::Output& getOutput() const override;
-    compositionengine::Layer& getLayer() const override;
-    compositionengine::LayerFE& getLayerFE() const override;
+    void updateCompositionState(bool includeGeometry, bool forceClientComposition,
+                                ui::Transform::RotationFlags) override;
+    void writeStateToHWC(bool) override;
+    void writeCursorPositionToHWC() const override;
 
-    const OutputLayerCompositionState& getState() const override;
-    OutputLayerCompositionState& editState() override;
+    HWC2::Layer* getHwcLayer() const override;
+    bool requiresClientComposition() const override;
+    bool isHardwareCursor() const override;
+    void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) override;
+    void prepareForDeviceLayerRequests() override;
+    void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
+    bool needsFiltering() const override;
 
-    void updateCompositionState(bool) override;
-    void writeStateToHWC(bool) const override;
-
-    void dump(std::string& result) const override;
+    void dump(std::string&) const override;
 
     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.
+    virtual void dumpState(std::string&) const = 0;
 
 private:
     Rect calculateInitialCrop() const;
-
-    const compositionengine::Output& mOutput;
-    std::shared_ptr<compositionengine::Layer> mLayer;
-    sp<compositionengine::LayerFE> mLayerFE;
-
-    OutputLayerCompositionState mState;
+    void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
+    void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
+    void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
+    void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from,
+                                               Hwc2::IComposerClient::Composition to) const;
 };
 
-std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
-        const CompositionEngine&, std::optional<DisplayId>, const compositionengine::Output&,
-        std::shared_ptr<compositionengine::Layer>, sp<compositionengine::LayerFE>);
+// This template factory function standardizes the implementation details of the
+// final class using the types actually required by the implementation. This is
+// not possible to do in the base class as those types may not even be visible
+// to the base code.
+template <typename BaseOutputLayer>
+std::unique_ptr<BaseOutputLayer> createOutputLayerTemplated(const Output& output,
+                                                            sp<LayerFE> layerFE) {
+    class OutputLayer final : public BaseOutputLayer {
+    public:
+// Clang incorrectly complains that these are unused.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-local-typedef"
 
-} // namespace android::compositionengine::impl
+        using OutputLayerCompositionState = std::remove_const_t<
+                std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getState())>>;
+        using Output = std::remove_const_t<
+                std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getOutput())>>;
+        using LayerFE =
+                std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getLayerFE())>;
+
+#pragma clang diagnostic pop
+
+        OutputLayer(const Output& output, const sp<LayerFE>& layerFE)
+              : mOutput(output), mLayerFE(layerFE) {}
+        ~OutputLayer() override = default;
+
+    private:
+        // compositionengine::OutputLayer overrides
+        const Output& getOutput() const override { return mOutput; }
+        LayerFE& getLayerFE() const override { return *mLayerFE; }
+        const OutputLayerCompositionState& getState() const override { return mState; }
+        OutputLayerCompositionState& editState() override { return mState; }
+
+        // compositionengine::impl::OutputLayer overrides
+        void dumpState(std::string& out) const override { mState.dump(out); }
+
+        const Output& mOutput;
+        const sp<LayerFE> mLayerFE;
+        OutputLayerCompositionState mState;
+    };
+
+    return std::make_unique<OutputLayer>(output, layerFE);
+}
+
+std::unique_ptr<OutputLayer> createOutputLayer(const compositionengine::Output&,
+                                               const sp<LayerFE>&);
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index b78e9e0..d2b38d1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -23,25 +23,45 @@
 #include <compositionengine/impl/HwcBufferCache.h>
 #include <renderengine/Mesh.h>
 #include <ui/FloatRect.h>
+#include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "DisplayHardware/ComposerHal.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
+namespace android {
+
 namespace HWC2 {
 class Layer;
 } // namespace HWC2
 
-namespace android {
-
 class HWComposer;
 
 namespace compositionengine::impl {
 
 struct OutputLayerCompositionState {
-    // The region of this layer which is visible on this output
+    // The portion of the layer that is not obscured by opaque layers on top
     Region visibleRegion;
 
+    // The portion of the layer that is not obscured and is also opaque
+    Region visibleNonTransparentRegion;
+
+    // The portion of the layer that is obscured by opaque layers on top
+    Region coveredRegion;
+
+    // The visibleRegion transformed to output space
+    Region outputSpaceVisibleRegion;
+
+    // Region cast by the layer's shadow
+    Region shadowRegion;
+
     // If true, client composition will be used on this output
     bool forceClientComposition{false};
 
@@ -57,8 +77,11 @@
     // The buffer transform to use for this layer o on this output.
     Hwc2::Transform bufferTransform{static_cast<Hwc2::Transform>(0)};
 
+    // The dataspace for this layer
+    ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+
     // The Z order index of this layer on this output
-    uint32_t z;
+    uint32_t z{0};
 
     /*
      * HWC state
@@ -70,7 +93,7 @@
         // The HWC Layer backing this layer
         std::shared_ptr<HWC2::Layer> hwcLayer;
 
-        // The HWC composition type for this layer
+        // The most recently set HWC composition type for this layer
         Hwc2::IComposerClient::Composition hwcCompositionType{
                 Hwc2::IComposerClient::Composition::INVALID};
 
@@ -85,6 +108,9 @@
 
     // Debugging
     void dump(std::string& result) const;
+
+    // Timestamp for when the layer is queued for client composition
+    nsecs_t clientCompositionTimestamp{0};
 };
 
 } // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index 0f57315..5127a6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -39,7 +39,7 @@
 class RenderSurface : public compositionengine::RenderSurface {
 public:
     RenderSurface(const CompositionEngine&, compositionengine::Display&,
-                  compositionengine::RenderSurfaceCreationArgs&&);
+                  const compositionengine::RenderSurfaceCreationArgs&);
     ~RenderSurface() override;
 
     bool isValid() const override;
@@ -49,14 +49,14 @@
 
     const sp<Fence>& getClientTargetAcquireFence() const override;
     void setBufferDataspace(ui::Dataspace) override;
+    void setBufferPixelFormat(ui::PixelFormat) override;
     void setDisplaySize(const ui::Size&) override;
     void setProtected(bool useProtected) override;
     status_t beginFrame(bool mustRecompose) override;
-    status_t prepareFrame() override;
+    void prepareFrame(bool usesClientComposition, bool usesDeviceComposition) override;
     sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) override;
-    void queueBuffer(base::unique_fd&& readyFence) override;
+    void queueBuffer(base::unique_fd readyFence) override;
     void onPresentDisplayCompleted() override;
-    void setViewportAndProjection() override;
     void flip() override;
 
     // Debugging
@@ -85,7 +85,7 @@
 
 std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
         const compositionengine::CompositionEngine&, compositionengine::Display&,
-        compositionengine::RenderSurfaceCreationArgs&&);
+        const compositionengine::RenderSurfaceCreationArgs&);
 
 } // namespace impl
 } // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 0f57685..f953d0b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -17,8 +17,9 @@
 #pragma once
 
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/DisplayCreationArgs.h>
-#include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <gmock/gmock.h>
 #include <renderengine/RenderEngine.h>
 
@@ -31,14 +32,28 @@
     CompositionEngine();
     ~CompositionEngine() override;
 
-    MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(DisplayCreationArgs&&));
-    MOCK_METHOD1(createLayer, std::shared_ptr<Layer>(LayerCreationArgs&&));
+    MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(const DisplayCreationArgs&));
+    MOCK_METHOD0(createLayerFECompositionState,
+                 std::unique_ptr<compositionengine::LayerFECompositionState>());
 
     MOCK_CONST_METHOD0(getHwComposer, HWComposer&());
     MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>));
 
     MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&());
     MOCK_METHOD1(setRenderEngine, void(std::unique_ptr<renderengine::RenderEngine>));
+
+    MOCK_CONST_METHOD0(getTimeStats, TimeStats&());
+    MOCK_METHOD1(setTimeStats, void(const std::shared_ptr<TimeStats>&));
+
+    MOCK_CONST_METHOD0(needsAnotherUpdate, bool());
+    MOCK_CONST_METHOD0(getLastFrameRefreshTimestamp, nsecs_t());
+
+    MOCK_METHOD1(present, void(CompositionRefreshArgs&));
+    MOCK_METHOD1(updateCursorAsync, void(CompositionRefreshArgs&));
+
+    MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
+
+    MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index d763aa6..3a4c70f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -38,8 +38,9 @@
 
     MOCK_METHOD0(disconnect, void());
 
-    MOCK_METHOD1(createDisplayColorProfile, void(DisplayColorProfileCreationArgs&&));
-    MOCK_METHOD1(createRenderSurface, void(RenderSurfaceCreationArgs&&));
+    MOCK_METHOD1(createDisplayColorProfile, void(const DisplayColorProfileCreationArgs&));
+    MOCK_METHOD1(createRenderSurface, void(const RenderSurfaceCreationArgs&));
+    MOCK_METHOD1(createClientCompositionCache, void(uint32_t));
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
index 8056c9d..1aaebea 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
@@ -42,6 +42,9 @@
     MOCK_CONST_METHOD0(hasDolbyVisionSupport, bool());
 
     MOCK_CONST_METHOD0(getHdrCapabilities, const HdrCapabilities&());
+    MOCK_CONST_METHOD1(isDataspaceSupported, bool(ui::Dataspace));
+    MOCK_CONST_METHOD3(getTargetDataspace,
+                       ui::Dataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h
deleted file mode 100644
index cce3b97..0000000
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <compositionengine/Layer.h>
-#include <compositionengine/LayerFE.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-#include <gmock/gmock.h>
-
-namespace android::compositionengine::mock {
-
-class Layer : public compositionengine::Layer {
-public:
-    Layer();
-    virtual ~Layer();
-
-    MOCK_CONST_METHOD0(getLayerFE, sp<LayerFE>());
-
-    MOCK_CONST_METHOD0(getState, const CompositionState&());
-    MOCK_METHOD0(editState, CompositionState&());
-
-    MOCK_CONST_METHOD1(dump, void(std::string&));
-};
-
-} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index aab18db..45891a7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -30,7 +30,15 @@
     LayerFE();
     virtual ~LayerFE();
 
-    MOCK_CONST_METHOD2(latchCompositionState, void(LayerFECompositionState&, bool));
+    MOCK_CONST_METHOD0(getCompositionState, const LayerFECompositionState*());
+
+    MOCK_METHOD1(onPreComposition, bool(nsecs_t));
+
+    MOCK_METHOD1(prepareCompositionState, void(compositionengine::LayerFE::StateSubset));
+    MOCK_METHOD1(prepareClientCompositionList,
+                 std::vector<compositionengine::LayerFE::LayerSettings>(
+                         compositionengine::LayerFE::ClientCompositionTargetSettings&));
+
     MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
 
     MOCK_CONST_METHOD0(getDebugName, const char*());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index d0e7b19..4661c5d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -16,8 +16,8 @@
 
 #pragma once
 
+#include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/Layer.h>
 #include <compositionengine/LayerFE.h>
 #include <compositionengine/Output.h>
 #include <compositionengine/OutputLayer.h>
@@ -33,40 +33,86 @@
     virtual ~Output();
 
     MOCK_CONST_METHOD0(isValid, bool());
+    MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
 
     MOCK_METHOD1(setCompositionEnabled, void(bool));
-    MOCK_METHOD6(setProjection,
-                 void(const ui::Transform&, int32_t, const Rect&, const Rect&, const Rect&, bool));
+    MOCK_METHOD7(setProjection,
+                 void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&,
+                      const Rect&, bool));
     MOCK_METHOD1(setBounds, void(const ui::Size&));
     MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool));
 
-    MOCK_METHOD1(setColorTransform, void(const mat4&));
-    MOCK_METHOD3(setColorMode, void(ui::ColorMode, ui::Dataspace, ui::RenderIntent));
+    MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
+    MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_CONST_METHOD0(getName, const std::string&());
     MOCK_METHOD1(setName, void(const std::string&));
 
-    MOCK_CONST_METHOD0(getDisplayColorProfile, DisplayColorProfile*());
-    MOCK_METHOD1(setDisplayColorProfile, void(std::unique_ptr<DisplayColorProfile>));
+    MOCK_CONST_METHOD0(getDisplayColorProfile, compositionengine::DisplayColorProfile*());
+    MOCK_METHOD1(setDisplayColorProfile,
+                 void(std::unique_ptr<compositionengine::DisplayColorProfile>));
 
-    MOCK_CONST_METHOD0(getRenderSurface, RenderSurface*());
-    MOCK_METHOD1(setRenderSurface, void(std::unique_ptr<RenderSurface>));
+    MOCK_CONST_METHOD0(getRenderSurface, compositionengine::RenderSurface*());
+    MOCK_METHOD1(setRenderSurface, void(std::unique_ptr<compositionengine::RenderSurface>));
 
     MOCK_CONST_METHOD0(getState, const OutputCompositionState&());
     MOCK_METHOD0(editState, OutputCompositionState&());
 
     MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
-    MOCK_CONST_METHOD2(belongsInOutput, bool(uint32_t, bool));
+    MOCK_CONST_METHOD2(belongsInOutput, bool(std::optional<uint32_t>, bool));
+    MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&));
 
     MOCK_CONST_METHOD1(getOutputLayerForLayer,
-                       compositionengine::OutputLayer*(compositionengine::Layer*));
-    MOCK_METHOD3(getOrCreateOutputLayer,
-                 std::unique_ptr<compositionengine::OutputLayer>(
-                         std::optional<DisplayId>, std::shared_ptr<compositionengine::Layer>,
-                         sp<compositionengine::LayerFE>));
-    MOCK_METHOD1(setOutputLayersOrderedByZ, void(OutputLayers&&));
-    MOCK_CONST_METHOD0(getOutputLayersOrderedByZ, OutputLayers&());
+                       compositionengine::OutputLayer*(const sp<compositionengine::LayerFE>&));
+    MOCK_METHOD0(clearOutputLayers, void());
+    MOCK_METHOD1(injectOutputLayerForTest,
+                 compositionengine::OutputLayer*(const sp<compositionengine::LayerFE>&));
+    MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+    MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t));
+
+    MOCK_METHOD1(setReleasedLayers, void(ReleasedLayers&&));
+
+    MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
+    MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&));
+
+    MOCK_METHOD2(rebuildLayerStacks,
+                 void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
+    MOCK_METHOD2(collectVisibleLayers,
+                 void(const compositionengine::CompositionRefreshArgs&,
+                      compositionengine::Output::CoverageState&));
+    MOCK_METHOD2(ensureOutputLayerIfVisible,
+                 void(sp<compositionengine::LayerFE>&, compositionengine::Output::CoverageState&));
+    MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
+
+    MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&));
+    MOCK_METHOD1(updateAndWriteCompositionState, void(const CompositionRefreshArgs&));
+    MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
+
+    MOCK_METHOD0(beginFrame, void());
+
+    MOCK_METHOD0(prepareFrame, void());
+    MOCK_METHOD0(chooseCompositionStrategy, void());
+
+    MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
+
+    MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
+
+    MOCK_METHOD2(composeSurfaces,
+                 std::optional<base::unique_fd>(
+                         const Region&,
+                         const compositionengine::CompositionRefreshArgs& refreshArgs));
+    MOCK_CONST_METHOD0(getSkipColorTransform, bool());
+
+    MOCK_METHOD0(postFramebuffer, void());
+    MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
+
+    MOCK_METHOD3(generateClientCompositionRequests,
+                 std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace));
+    MOCK_METHOD2(appendRegionFlashRequests,
+                 void(const Region&, std::vector<LayerFE::LayerSettings>&));
+    MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
+    MOCK_METHOD1(cacheClientCompositionRequests, void(uint32_t));
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 29cd08a..81e1fc7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Layer.h>
 #include <compositionengine/LayerFE.h>
 #include <compositionengine/Output.h>
 #include <compositionengine/OutputLayer.h>
@@ -31,15 +30,25 @@
     OutputLayer();
     virtual ~OutputLayer();
 
+    MOCK_METHOD1(setHwcLayer, void(std::shared_ptr<HWC2::Layer>));
+
     MOCK_CONST_METHOD0(getOutput, const compositionengine::Output&());
-    MOCK_CONST_METHOD0(getLayer, compositionengine::Layer&());
     MOCK_CONST_METHOD0(getLayerFE, compositionengine::LayerFE&());
 
     MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&());
     MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
 
-    MOCK_METHOD1(updateCompositionState, void(bool));
-    MOCK_CONST_METHOD1(writeStateToHWC, void(bool));
+    MOCK_METHOD3(updateCompositionState, void(bool, bool, ui::Transform::RotationFlags));
+    MOCK_METHOD1(writeStateToHWC, void(bool));
+    MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
+
+    MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
+    MOCK_CONST_METHOD0(requiresClientComposition, bool());
+    MOCK_CONST_METHOD0(isHardwareCursor, bool());
+    MOCK_METHOD1(applyDeviceCompositionTypeChange, void(Hwc2::IComposerClient::Composition));
+    MOCK_METHOD0(prepareForDeviceLayerRequests, void());
+    MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
+    MOCK_CONST_METHOD0(needsFiltering, bool());
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index ca2299a..a0cae6f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -36,12 +36,12 @@
     MOCK_METHOD1(setDisplaySize, void(const ui::Size&));
     MOCK_METHOD1(setProtected, void(bool));
     MOCK_METHOD1(setBufferDataspace, void(ui::Dataspace));
+    MOCK_METHOD1(setBufferPixelFormat, void(ui::PixelFormat));
     MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
-    MOCK_METHOD0(prepareFrame, status_t());
+    MOCK_METHOD2(prepareFrame, void(bool, bool));
     MOCK_METHOD1(dequeueBuffer, sp<GraphicBuffer>(base::unique_fd*));
-    MOCK_METHOD1(queueBuffer, void(base::unique_fd&&));
+    MOCK_METHOD1(queueBuffer, void(base::unique_fd));
     MOCK_METHOD0(onPresentDisplayCompleted, void());
-    MOCK_METHOD0(setViewportAndProjection, void());
     MOCK_METHOD0(flip, void());
     MOCK_CONST_METHOD1(dump, void(std::string& result));
     MOCK_CONST_METHOD0(getPageFlipCount, std::uint32_t());
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
new file mode 100644
index 0000000..2d9f01b
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+
+#include <compositionengine/impl/ClientCompositionRequestCache.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/LayerSettings.h>
+
+namespace android::compositionengine::impl {
+
+namespace {
+LayerFE::LayerSettings getLayerSettingsSnapshot(const LayerFE::LayerSettings& settings) {
+    LayerFE::LayerSettings snapshot = settings;
+    snapshot.source.buffer.buffer = nullptr;
+    snapshot.source.buffer.fence = nullptr;
+    return snapshot;
+}
+
+inline bool equalIgnoringSource(const renderengine::LayerSettings& lhs,
+                                const renderengine::LayerSettings& rhs) {
+    return lhs.geometry == rhs.geometry && lhs.alpha == rhs.alpha &&
+            lhs.sourceDataspace == rhs.sourceDataspace &&
+            lhs.colorTransform == rhs.colorTransform &&
+            lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
+            lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
+}
+
+inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
+    return lhs.textureName == rhs.textureName &&
+            lhs.useTextureFiltering == rhs.useTextureFiltering &&
+            lhs.textureTransform == rhs.textureTransform &&
+            lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
+            lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
+            lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
+            lhs.maxContentLuminance == rhs.maxContentLuminance;
+}
+
+inline bool equalIgnoringBuffer(const renderengine::LayerSettings& lhs,
+                                const renderengine::LayerSettings& rhs) {
+    // compare LayerSettings without LayerSettings.PixelSource
+    return equalIgnoringSource(lhs, rhs) &&
+
+            // compare LayerSettings.PixelSource without buffer
+            lhs.source.solidColor == rhs.source.solidColor &&
+
+            // compare LayerSettings.PixelSource.Buffer without buffer & fence
+            equalIgnoringBuffer(lhs.source.buffer, rhs.source.buffer);
+}
+
+bool layerSettingsAreEqual(const LayerFE::LayerSettings& lhs, const LayerFE::LayerSettings& rhs) {
+    return lhs.bufferId == rhs.bufferId && lhs.frameNumber == rhs.frameNumber &&
+            equalIgnoringBuffer(lhs, rhs);
+}
+
+} // namespace
+
+ClientCompositionRequestCache::ClientCompositionRequest::ClientCompositionRequest(
+        const renderengine::DisplaySettings& initDisplay,
+        const std::vector<LayerFE::LayerSettings>& initLayerSettings)
+      : display(initDisplay) {
+    layerSettings.reserve(initLayerSettings.size());
+    for (const LayerFE::LayerSettings& settings : initLayerSettings) {
+        layerSettings.push_back(getLayerSettingsSnapshot(settings));
+    }
+}
+
+bool ClientCompositionRequestCache::ClientCompositionRequest::equals(
+        const renderengine::DisplaySettings& newDisplay,
+        const std::vector<LayerFE::LayerSettings>& newLayerSettings) const {
+    return newDisplay == display &&
+            std::equal(layerSettings.begin(), layerSettings.end(), newLayerSettings.begin(),
+                       newLayerSettings.end(), layerSettingsAreEqual);
+}
+
+bool ClientCompositionRequestCache::exists(
+        uint64_t bufferId, const renderengine::DisplaySettings& display,
+        const std::vector<LayerFE::LayerSettings>& layerSettings) const {
+    for (const auto& [cachedBufferId, cachedRequest] : mCache) {
+        if (cachedBufferId == bufferId) {
+            return cachedRequest.equals(display, layerSettings);
+        }
+    }
+    return false;
+}
+
+void ClientCompositionRequestCache::add(uint64_t bufferId,
+                                        const renderengine::DisplaySettings& display,
+                                        const std::vector<LayerFE::LayerSettings>& layerSettings) {
+    const ClientCompositionRequest request(display, layerSettings);
+    for (auto& [cachedBufferId, cachedRequest] : mCache) {
+        if (cachedBufferId == bufferId) {
+            cachedRequest = std::move(request);
+            return;
+        }
+    }
+
+    if (mCache.size() >= mMaxCacheSize) {
+        mCache.pop_front();
+    }
+
+    mCache.emplace_back(bufferId, std::move(request));
+}
+
+void ClientCompositionRequestCache::remove(uint64_t bufferId) {
+    for (auto it = mCache.begin(); it != mCache.end(); it++) {
+        if (it->first == bufferId) {
+            mCache.erase(it);
+            return;
+        }
+    }
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index cb08b81..6203dc6 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -14,13 +14,25 @@
  * limitations under the License.
  */
 
+#include <compositionengine/CompositionRefreshArgs.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/CompositionEngine.h>
 #include <compositionengine/impl/Display.h>
-#include <compositionengine/impl/Layer.h>
+
 #include <renderengine/RenderEngine.h>
+#include <utils/Trace.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 
 #include "DisplayHardware/HWComposer.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android::compositionengine {
 
 CompositionEngine::~CompositionEngine() = default;
@@ -35,12 +47,13 @@
 CompositionEngine::~CompositionEngine() = default;
 
 std::shared_ptr<compositionengine::Display> CompositionEngine::createDisplay(
-        DisplayCreationArgs&& args) {
-    return compositionengine::impl::createDisplay(*this, std::move(args));
+        const DisplayCreationArgs& args) {
+    return compositionengine::impl::createDisplay(*this, args);
 }
 
-std::shared_ptr<compositionengine::Layer> CompositionEngine::createLayer(LayerCreationArgs&& args) {
-    return compositionengine::impl::createLayer(*this, std::move(args));
+std::unique_ptr<compositionengine::LayerFECompositionState>
+CompositionEngine::createLayerFECompositionState() {
+    return std::make_unique<compositionengine::LayerFECompositionState>();
 }
 
 HWComposer& CompositionEngine::getHwComposer() const {
@@ -59,5 +72,92 @@
     mRenderEngine = std::move(renderEngine);
 }
 
+TimeStats& CompositionEngine::getTimeStats() const {
+    return *mTimeStats.get();
+}
+
+void CompositionEngine::setTimeStats(const std::shared_ptr<TimeStats>& timeStats) {
+    mTimeStats = timeStats;
+}
+
+bool CompositionEngine::needsAnotherUpdate() const {
+    return mNeedsAnotherUpdate;
+}
+
+nsecs_t CompositionEngine::getLastFrameRefreshTimestamp() const {
+    return mRefreshStartTime;
+}
+
+void CompositionEngine::present(CompositionRefreshArgs& args) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    preComposition(args);
+
+    {
+        // latchedLayers is used to track the set of front-end layer state that
+        // has been latched across all outputs for the prepare step, and is not
+        // needed for anything else.
+        LayerFESet latchedLayers;
+
+        for (const auto& output : args.outputs) {
+            output->prepare(args, latchedLayers);
+        }
+    }
+
+    updateLayerStateFromFE(args);
+
+    for (const auto& output : args.outputs) {
+        output->present(args);
+    }
+}
+
+void CompositionEngine::updateCursorAsync(CompositionRefreshArgs& args) {
+    std::unordered_map<compositionengine::LayerFE*, compositionengine::LayerFECompositionState*>
+            uniqueVisibleLayers;
+
+    for (const auto& output : args.outputs) {
+        for (auto* layer : output->getOutputLayersOrderedByZ()) {
+            if (layer->isHardwareCursor()) {
+                // Latch the cursor composition state from each front-end layer.
+                layer->getLayerFE().prepareCompositionState(LayerFE::StateSubset::Cursor);
+                layer->writeCursorPositionToHWC();
+            }
+        }
+    }
+}
+
+void CompositionEngine::preComposition(CompositionRefreshArgs& args) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    bool needsAnotherUpdate = false;
+
+    mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    for (auto& layer : args.layers) {
+        if (layer->onPreComposition(mRefreshStartTime)) {
+            needsAnotherUpdate = true;
+        }
+    }
+
+    mNeedsAnotherUpdate = needsAnotherUpdate;
+}
+
+void CompositionEngine::dump(std::string&) const {
+    // The base class has no state to dump, but derived classes might.
+}
+
+void CompositionEngine::setNeedsAnotherUpdateForTest(bool value) {
+    mNeedsAnotherUpdate = value;
+}
+
+void CompositionEngine::updateLayerStateFromFE(CompositionRefreshArgs& args) {
+    // Update the composition state from each front-end layer
+    for (const auto& output : args.outputs) {
+        output->updateLayerStateFromFE(args);
+    }
+}
+
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index f9d70e3..d201104 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -16,32 +16,67 @@
 
 #include <android-base/stringprintf.h>
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/DisplayCreationArgs.h>
 #include <compositionengine/DisplaySurface.h>
+#include <compositionengine/LayerFE.h>
 #include <compositionengine/impl/Display.h>
 #include <compositionengine/impl/DisplayColorProfile.h>
 #include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/RenderSurface.h>
 
+#include <utils/Trace.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "DisplayHardware/HWComposer.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
+#include "DisplayHardware/PowerAdvisor.h"
+
 namespace android::compositionengine::impl {
 
-std::shared_ptr<compositionengine::Display> createDisplay(
+std::shared_ptr<Display> createDisplay(
         const compositionengine::CompositionEngine& compositionEngine,
-        compositionengine::DisplayCreationArgs&& args) {
-    return std::make_shared<Display>(compositionEngine, std::move(args));
-}
-
-Display::Display(const CompositionEngine& compositionEngine, DisplayCreationArgs&& args)
-      : compositionengine::impl::Output(compositionEngine),
-        mIsVirtual(args.isVirtual),
-        mId(args.displayId) {
-    editState().isSecure = args.isSecure;
+        const compositionengine::DisplayCreationArgs& args) {
+    return createDisplayTemplated<Display>(compositionEngine, args);
 }
 
 Display::~Display() = default;
 
+void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
+    mIsVirtual = !args.physical;
+    mId = args.physical ? std::make_optional(args.physical->id) : std::nullopt;
+    mPowerAdvisor = args.powerAdvisor;
+
+    editState().isSecure = args.isSecure;
+
+    setLayerStackFilter(args.layerStackId,
+                        args.physical ? args.physical->type == DisplayConnectionType::Internal
+                                      : false);
+    setName(args.name);
+
+    if (!args.physical && args.useHwcVirtualDisplays) {
+        mId = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat);
+    }
+}
+
+std::optional<DisplayId> Display::maybeAllocateDisplayIdForVirtualDisplay(
+        ui::Size pixels, ui::PixelFormat pixelFormat) const {
+    auto& hwc = getCompositionEngine().getHwComposer();
+    return hwc.allocateVirtualDisplay(static_cast<uint32_t>(pixels.width),
+                                      static_cast<uint32_t>(pixels.height), &pixelFormat);
+}
+
+bool Display::isValid() const {
+    return Output::isValid() && mPowerAdvisor;
+}
+
 const std::optional<DisplayId>& Display::getId() const {
     return mId;
 }
@@ -54,6 +89,14 @@
     return mIsVirtual;
 }
 
+std::optional<DisplayId> Display::getDisplayId() const {
+    return mId;
+}
+
+void Display::setDisplayIdForTesting(std::optional<DisplayId> displayId) {
+    mId = displayId;
+}
+
 void Display::disconnect() {
     if (!mId) {
         return;
@@ -64,19 +107,28 @@
     mId.reset();
 }
 
-void Display::setColorTransform(const mat4& transform) {
-    Output::setColorTransform(transform);
+void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
+    Output::setColorTransform(args);
+
+    if (!mId || CC_LIKELY(!args.colorTransformMatrix)) {
+        return;
+    }
 
     auto& hwc = getCompositionEngine().getHwComposer();
-    status_t result = hwc.setColorTransform(*mId, transform);
+    status_t result = hwc.setColorTransform(*mId, *args.colorTransformMatrix);
     ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
              mId ? to_string(*mId).c_str() : "", result);
 }
 
-void Display::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
-                           ui::RenderIntent renderIntent) {
-    if (mode == getState().colorMode && dataspace == getState().dataspace &&
-        renderIntent == getState().renderIntent) {
+void Display::setColorProfile(const ColorProfile& colorProfile) {
+    const ui::Dataspace targetDataspace =
+            getDisplayColorProfile()->getTargetDataspace(colorProfile.mode, colorProfile.dataspace,
+                                                         colorProfile.colorSpaceAgnosticDataspace);
+
+    if (colorProfile.mode == getState().colorMode &&
+        colorProfile.dataspace == getState().dataspace &&
+        colorProfile.renderIntent == getState().renderIntent &&
+        targetDataspace == getState().targetDataspace) {
         return;
     }
 
@@ -85,10 +137,10 @@
         return;
     }
 
-    Output::setColorMode(mode, dataspace, renderIntent);
+    Output::setColorProfile(colorProfile);
 
     auto& hwc = getCompositionEngine().getHwComposer();
-    hwc.setActiveColorMode(*mId, mode, renderIntent);
+    hwc.setActiveColorMode(*mId, colorProfile.mode, colorProfile.renderIntent);
 }
 
 void Display::dump(std::string& out) const {
@@ -110,13 +162,229 @@
     Output::dumpBase(out);
 }
 
-void Display::createDisplayColorProfile(DisplayColorProfileCreationArgs&& args) {
-    setDisplayColorProfile(compositionengine::impl::createDisplayColorProfile(std::move(args)));
+void Display::createDisplayColorProfile(const DisplayColorProfileCreationArgs& args) {
+    setDisplayColorProfile(compositionengine::impl::createDisplayColorProfile(args));
 }
 
-void Display::createRenderSurface(RenderSurfaceCreationArgs&& args) {
-    setRenderSurface(compositionengine::impl::createRenderSurface(getCompositionEngine(), *this,
-                                                                  std::move(args)));
+void Display::createRenderSurface(const RenderSurfaceCreationArgs& args) {
+    setRenderSurface(
+            compositionengine::impl::createRenderSurface(getCompositionEngine(), *this, args));
+}
+
+void Display::createClientCompositionCache(uint32_t cacheSize) {
+    cacheClientCompositionRequests(cacheSize);
+}
+
+std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer(
+        const sp<compositionengine::LayerFE>& layerFE) const {
+    auto result = impl::createOutputLayer(*this, layerFE);
+
+    if (result && mId) {
+        auto& hwc = getCompositionEngine().getHwComposer();
+        auto displayId = *mId;
+        // Note: For the moment we ensure it is safe to take a reference to the
+        // HWComposer implementation by destroying all the OutputLayers (and
+        // hence the HWC2::Layers they own) before setting a new HWComposer. See
+        // for example SurfaceFlinger::updateVrFlinger().
+        // TODO(b/121291683): Make this safer.
+        auto hwcLayer = std::shared_ptr<HWC2::Layer>(hwc.createLayer(displayId),
+                                                     [&hwc, displayId](HWC2::Layer* layer) {
+                                                         hwc.destroyLayer(displayId, layer);
+                                                     });
+        ALOGE_IF(!hwcLayer, "Failed to create a HWC layer for a HWC supported display %s",
+                 getName().c_str());
+        result->setHwcLayer(std::move(hwcLayer));
+    }
+    return result;
+}
+
+void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    Output::setReleasedLayers(refreshArgs);
+
+    if (!mId || refreshArgs.layersWithQueuedFrames.empty()) {
+        return;
+    }
+
+    // For layers that are being removed from a HWC display, and that have
+    // queued frames, add them to a a list of released layers so we can properly
+    // set a fence.
+    compositionengine::Output::ReleasedLayers releasedLayers;
+
+    // Any non-null entries in the current list of layers are layers that are no
+    // longer going to be visible
+    for (auto* outputLayer : getOutputLayersOrderedByZ()) {
+        if (!outputLayer) {
+            continue;
+        }
+
+        compositionengine::LayerFE* layerFE = &outputLayer->getLayerFE();
+        const bool hasQueuedFrames =
+                std::any_of(refreshArgs.layersWithQueuedFrames.cbegin(),
+                            refreshArgs.layersWithQueuedFrames.cend(),
+                            [layerFE](sp<compositionengine::LayerFE> layerWithQueuedFrames) {
+                                return layerFE == layerWithQueuedFrames.get();
+                            });
+
+        if (hasQueuedFrames) {
+            releasedLayers.emplace_back(layerFE);
+        }
+    }
+
+    setReleasedLayers(std::move(releasedLayers));
+}
+
+void Display::chooseCompositionStrategy() {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    // Default to the base settings -- client composition only.
+    Output::chooseCompositionStrategy();
+
+    // If we don't have a HWC display, then we are done
+    if (!mId) {
+        return;
+    }
+
+    // Get any composition changes requested by the HWC device, and apply them.
+    std::optional<android::HWComposer::DeviceRequestedChanges> changes;
+    auto& hwc = getCompositionEngine().getHwComposer();
+    if (status_t result = hwc.getDeviceCompositionChanges(*mId, anyLayersRequireClientComposition(),
+                                                          &changes);
+        result != NO_ERROR) {
+        ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
+              strerror(-result));
+        return;
+    }
+    if (changes) {
+        applyChangedTypesToLayers(changes->changedTypes);
+        applyDisplayRequests(changes->displayRequests);
+        applyLayerRequestsToLayers(changes->layerRequests);
+        applyClientTargetRequests(changes->clientTargetProperty);
+    }
+
+    // Determine what type of composition we are doing from the final state
+    auto& state = editState();
+    state.usesClientComposition = anyLayersRequireClientComposition();
+    state.usesDeviceComposition = !allLayersRequireClientComposition();
+}
+
+bool Display::getSkipColorTransform() const {
+    const auto& hwc = getCompositionEngine().getHwComposer();
+    return mId ? hwc.hasDisplayCapability(*mId, hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM)
+               : hwc.hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM);
+}
+
+bool Display::anyLayersRequireClientComposition() const {
+    const auto layers = getOutputLayersOrderedByZ();
+    return std::any_of(layers.begin(), layers.end(),
+                       [](const auto& layer) { return layer->requiresClientComposition(); });
+}
+
+bool Display::allLayersRequireClientComposition() const {
+    const auto layers = getOutputLayersOrderedByZ();
+    return std::all_of(layers.begin(), layers.end(),
+                       [](const auto& layer) { return layer->requiresClientComposition(); });
+}
+
+void Display::applyChangedTypesToLayers(const ChangedTypes& changedTypes) {
+    if (changedTypes.empty()) {
+        return;
+    }
+
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        auto hwcLayer = layer->getHwcLayer();
+        if (!hwcLayer) {
+            continue;
+        }
+
+        if (auto it = changedTypes.find(hwcLayer); it != changedTypes.end()) {
+            layer->applyDeviceCompositionTypeChange(
+                    static_cast<Hwc2::IComposerClient::Composition>(it->second));
+        }
+    }
+}
+
+void Display::applyDisplayRequests(const DisplayRequests& displayRequests) {
+    auto& state = editState();
+    state.flipClientTarget = (static_cast<uint32_t>(displayRequests) &
+                              static_cast<uint32_t>(hal::DisplayRequest::FLIP_CLIENT_TARGET)) != 0;
+    // Note: HWC2::DisplayRequest::WriteClientTargetToOutput is currently ignored.
+}
+
+void Display::applyLayerRequestsToLayers(const LayerRequests& layerRequests) {
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        layer->prepareForDeviceLayerRequests();
+
+        auto hwcLayer = layer->getHwcLayer();
+        if (!hwcLayer) {
+            continue;
+        }
+
+        if (auto it = layerRequests.find(hwcLayer); it != layerRequests.end()) {
+            layer->applyDeviceLayerRequest(
+                    static_cast<Hwc2::IComposerClient::LayerRequest>(it->second));
+        }
+    }
+}
+
+void Display::applyClientTargetRequests(const ClientTargetProperty& clientTargetProperty) {
+    if (clientTargetProperty.dataspace == ui::Dataspace::UNKNOWN) {
+        return;
+    }
+    auto outputState = editState();
+    outputState.dataspace = clientTargetProperty.dataspace;
+    getRenderSurface()->setBufferDataspace(clientTargetProperty.dataspace);
+    getRenderSurface()->setBufferPixelFormat(clientTargetProperty.pixelFormat);
+}
+
+compositionengine::Output::FrameFences Display::presentAndGetFrameFences() {
+    auto result = impl::Output::presentAndGetFrameFences();
+
+    if (!mId) {
+        return result;
+    }
+
+    auto& hwc = getCompositionEngine().getHwComposer();
+    hwc.presentAndGetReleaseFences(*mId);
+
+    result.presentFence = hwc.getPresentFence(*mId);
+
+    // TODO(b/121291683): Change HWComposer call to return entire map
+    for (const auto* layer : getOutputLayersOrderedByZ()) {
+        auto hwcLayer = layer->getHwcLayer();
+        if (!hwcLayer) {
+            continue;
+        }
+
+        result.layerFences.emplace(hwcLayer, hwc.getLayerReleaseFence(*mId, hwcLayer));
+    }
+
+    hwc.clearReleaseFences(*mId);
+
+    return result;
+}
+
+void Display::setExpensiveRenderingExpected(bool enabled) {
+    Output::setExpensiveRenderingExpected(enabled);
+
+    if (mPowerAdvisor && mId) {
+        mPowerAdvisor->setExpensiveRenderingExpected(*mId, enabled);
+    }
+}
+
+void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    // We only need to actually compose the display if:
+    // 1) It is being handled by hardware composer, which may need this to
+    //    keep its virtual display state machine in sync, or
+    // 2) There is work to be done (the dirty region isn't empty)
+    if (!mId) {
+        if (getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) {
+            ALOGV("Skipping display composition");
+            return;
+        }
+    }
+
+    impl::Output::finishFrame(refreshArgs);
 }
 
 } // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
index 130ab1d..a7c4512 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -64,6 +64,12 @@
         RenderIntent::TONE_MAP_COLORIMETRIC,
 };
 
+// Returns true if the given colorMode is considered an HDR color mode
+bool isHdrColorMode(const ColorMode colorMode) {
+    return std::any_of(std::begin(sHdrColorModes), std::end(sHdrColorModes),
+                       [colorMode](ColorMode hdrColorMode) { return hdrColorMode == colorMode; });
+}
+
 // map known color mode to dataspace
 Dataspace colorModeToDataspace(ColorMode mode) {
     switch (mode) {
@@ -90,13 +96,7 @@
     candidates.push_back(mode);
 
     // check if mode is HDR
-    bool isHdr = false;
-    for (auto hdrMode : sHdrColorModes) {
-        if (hdrMode == mode) {
-            isHdr = true;
-            break;
-        }
-    }
+    bool isHdr = isHdrColorMode(mode);
 
     // add other HDR candidates when mode is HDR
     if (isHdr) {
@@ -184,11 +184,11 @@
 } // anonymous namespace
 
 std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
-        DisplayColorProfileCreationArgs&& args) {
-    return std::make_unique<DisplayColorProfile>(std::move(args));
+        const DisplayColorProfileCreationArgs& args) {
+    return std::make_unique<DisplayColorProfile>(args);
 }
 
-DisplayColorProfile::DisplayColorProfile(DisplayColorProfileCreationArgs&& args)
+DisplayColorProfile::DisplayColorProfile(const DisplayColorProfileCreationArgs& args)
       : mHasWideColorGamut(args.hasWideColorGamut),
         mSupportedPerFrameMetadata(args.supportedPerFrameMetadata) {
     populateColorModes(args.hwcColorModes);
@@ -376,6 +376,32 @@
     }
 }
 
+bool DisplayColorProfile::isDataspaceSupported(Dataspace dataspace) const {
+    switch (dataspace) {
+        case Dataspace::BT2020_PQ:
+        case Dataspace::BT2020_ITU_PQ:
+            return hasHDR10Support();
+
+        case Dataspace::BT2020_HLG:
+        case Dataspace::BT2020_ITU_HLG:
+            return hasHLGSupport();
+
+        default:
+            return true;
+    }
+}
+
+ui::Dataspace DisplayColorProfile::getTargetDataspace(ColorMode mode, Dataspace dataspace,
+                                                      Dataspace colorSpaceAgnosticDataspace) const {
+    if (isHdrColorMode(mode)) {
+        return Dataspace::UNKNOWN;
+    }
+    if (colorSpaceAgnosticDataspace != ui::Dataspace::UNKNOWN) {
+        return colorSpaceAgnosticDataspace;
+    }
+    return dataspace;
+}
+
 void DisplayColorProfile::dump(std::string& out) const {
     out.append("   Composition Display Color State:");
 
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index f72862b..cedc333 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -15,9 +15,17 @@
  */
 
 #include <compositionengine/impl/HwcBufferCache.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <gui/BufferQueue.h>
 #include <ui/GraphicBuffer.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android::compositionengine::impl {
 
 HwcBufferCache::HwcBufferCache() {
@@ -31,7 +39,7 @@
         slot >= BufferQueue::NUM_BUFFER_SLOTS) {
         *outSlot = 0;
     } else {
-        *outSlot = slot;
+        *outSlot = static_cast<uint32_t>(slot);
     }
 
     auto& currentBuffer = mBuffers[*outSlot];
diff --git a/services/surfaceflinger/CompositionEngine/src/Layer.cpp b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
deleted file mode 100644
index 96e9731..0000000
--- a/services/surfaceflinger/CompositionEngine/src/Layer.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.
- */
-
-#include <android-base/stringprintf.h>
-#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/LayerCreationArgs.h>
-#include <compositionengine/LayerFE.h>
-#include <compositionengine/impl/Layer.h>
-
-namespace android::compositionengine {
-
-Layer::~Layer() = default;
-
-namespace impl {
-
-std::shared_ptr<compositionengine::Layer> createLayer(
-        const compositionengine::CompositionEngine& compositionEngine,
-        compositionengine::LayerCreationArgs&& args) {
-    return std::make_shared<Layer>(compositionEngine, std::move(args));
-}
-
-Layer::Layer(const CompositionEngine& compositionEngine, LayerCreationArgs&& args)
-      : mCompositionEngine(compositionEngine), mLayerFE(args.layerFE) {
-    static_cast<void>(mCompositionEngine); // Temporary use to prevent an unused warning
-}
-
-Layer::~Layer() = default;
-
-sp<LayerFE> Layer::getLayerFE() const {
-    return mLayerFE.promote();
-}
-
-const LayerCompositionState& Layer::getState() const {
-    return mState;
-}
-
-LayerCompositionState& Layer::editState() {
-    return mState;
-}
-
-void Layer::dump(std::string& out) const {
-    auto layerFE = getLayerFE();
-    android::base::StringAppendF(&out, "* compositionengine::Layer %p (%s)\n", this,
-                                 layerFE ? layerFE->getDebugName() : "<unknown>");
-    mState.dump(out);
-}
-
-} // namespace impl
-} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
deleted file mode 100644
index 40c4da9..0000000
--- a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.
- */
-
-#include <android-base/stringprintf.h>
-#include <compositionengine/impl/DumpHelpers.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-
-namespace android::compositionengine::impl {
-
-namespace {
-
-using android::compositionengine::impl::dumpVal;
-
-void dumpVal(std::string& out, const char* name, Hwc2::IComposerClient::Color value) {
-    using android::base::StringAppendF;
-    StringAppendF(&out, "%s=[%d %d %d] ", name, value.r, value.g, value.b);
-}
-
-void dumpFrontEnd(std::string& out, const LayerFECompositionState& state) {
-    out.append("      ");
-    dumpVal(out, "isSecure", state.isSecure);
-    dumpVal(out, "geomUsesSourceCrop", state.geomUsesSourceCrop);
-    dumpVal(out, "geomBufferUsesDisplayInverseTransform",
-            state.geomBufferUsesDisplayInverseTransform);
-    dumpVal(out, "geomLayerTransform", state.geomLayerTransform);
-
-    out.append("\n      ");
-    dumpVal(out, "geomBufferSize", state.geomBufferSize);
-    dumpVal(out, "geomContentCrop", state.geomContentCrop);
-    dumpVal(out, "geomCrop", state.geomCrop);
-    dumpVal(out, "geomBufferTransform", state.geomBufferTransform);
-
-    out.append("\n      ");
-    dumpVal(out, "geomActiveTransparentRegion", state.geomActiveTransparentRegion);
-
-    out.append("      ");
-    dumpVal(out, "geomLayerBounds", state.geomLayerBounds);
-
-    out.append("\n      ");
-    dumpVal(out, "blend", toString(state.blendMode), state.blendMode);
-    dumpVal(out, "alpha", state.alpha);
-
-    out.append("\n      ");
-    dumpVal(out, "type", state.type);
-    dumpVal(out, "appId", state.appId);
-
-    dumpVal(out, "composition type", toString(state.compositionType), state.compositionType);
-
-    out.append("\n      buffer: ");
-    dumpVal(out, "buffer", state.buffer.get());
-    dumpVal(out, "slot", state.bufferSlot);
-
-    out.append("\n      ");
-    dumpVal(out, "sideband stream", state.sidebandStream.get());
-
-    out.append("\n      ");
-    dumpVal(out, "color", state.color);
-
-    out.append("\n      ");
-    dumpVal(out, "dataspace", toString(state.dataspace), state.dataspace);
-    dumpVal(out, "hdr metadata types", state.hdrMetadata.validTypes);
-    dumpVal(out, "colorTransform", state.colorTransform);
-
-    out.append("\n");
-}
-
-} // namespace
-
-void LayerCompositionState::dump(std::string& out) const {
-    out.append("    frontend:\n");
-    dumpFrontEnd(out, frontEnd);
-}
-
-} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
new file mode 100644
index 0000000..02e3a45
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#include <android-base/stringprintf.h>
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/impl/DumpHelpers.h>
+
+namespace android::compositionengine {
+
+namespace {
+
+using android::compositionengine::impl::dumpVal;
+
+void dumpVal(std::string& out, const char* name, half4 value) {
+    using android::base::StringAppendF;
+    StringAppendF(&out, "%s=[%f %f %f] ", name, static_cast<float>(value.r),
+                  static_cast<float>(value.g), static_cast<float>(value.b));
+}
+
+} // namespace
+
+std::string GenericLayerMetadataEntry::dumpAsString() const {
+    using android::base::StringAppendF;
+    std::string out;
+
+    out.append("GenericLayerMetadataEntry{mandatory: ");
+    StringAppendF(&out, "%d", mandatory);
+    out.append(" value: ");
+    for (uint8_t byte : value) {
+        StringAppendF(&out, "0x08%" PRIx8 " ", byte);
+    }
+    out.append("]}");
+    return out;
+}
+
+LayerFECompositionState::~LayerFECompositionState() = default;
+
+void LayerFECompositionState::dump(std::string& out) const {
+    out.append("      ");
+    dumpVal(out, "isSecure", isSecure);
+    dumpVal(out, "geomUsesSourceCrop", geomUsesSourceCrop);
+    dumpVal(out, "geomBufferUsesDisplayInverseTransform", geomBufferUsesDisplayInverseTransform);
+    dumpVal(out, "geomLayerTransform", geomLayerTransform);
+
+    out.append("\n      ");
+    dumpVal(out, "geomBufferSize", geomBufferSize);
+    dumpVal(out, "geomContentCrop", geomContentCrop);
+    dumpVal(out, "geomCrop", geomCrop);
+    dumpVal(out, "geomBufferTransform", geomBufferTransform);
+
+    out.append("\n      ");
+    dumpVal(out, "transparentRegionHint", transparentRegionHint);
+
+    out.append("      ");
+    dumpVal(out, "geomLayerBounds", geomLayerBounds);
+
+    out.append("      ");
+    dumpVal(out, "shadowRadius", shadowRadius);
+
+    out.append("\n      ");
+    dumpVal(out, "blend", toString(blendMode), blendMode);
+    dumpVal(out, "alpha", alpha);
+    dumpVal(out, "backgroundBlurRadius", backgroundBlurRadius);
+
+    out.append("\n      ");
+    dumpVal(out, "type", type);
+    dumpVal(out, "appId", appId);
+
+    if (!metadata.empty()) {
+        out.append("\n      metadata {");
+        for (const auto& [key, entry] : metadata) {
+            out.append("\n           ");
+            out.append(key);
+            out.append("=");
+            out.append(entry.dumpAsString());
+        }
+        out.append("\n      }\n      ");
+    }
+
+    dumpVal(out, "composition type", toString(compositionType), compositionType);
+
+    out.append("\n      buffer: ");
+    dumpVal(out, "slot", bufferSlot);
+    dumpVal(out, "buffer", buffer.get());
+
+    out.append("\n      ");
+    dumpVal(out, "sideband stream", sidebandStream.get());
+
+    out.append("\n      ");
+    dumpVal(out, "color", color);
+
+    out.append("\n      ");
+    dumpVal(out, "isOpaque", isOpaque);
+    dumpVal(out, "hasProtectedContent", hasProtectedContent);
+    dumpVal(out, "isColorspaceAgnostic", isColorspaceAgnostic);
+    dumpVal(out, "dataspace", toString(dataspace), dataspace);
+    dumpVal(out, "hdr metadata types", hdrMetadata.validTypes);
+    dumpVal(out, "colorTransform", colorTransform);
+
+    out.append("\n");
+}
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 01b5781..34dc536 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -14,14 +14,35 @@
  * limitations under the License.
  */
 
+#include <thread>
+
 #include <android-base/stringprintf.h>
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/RenderSurface.h>
 #include <compositionengine/impl/Output.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/RenderEngine.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 #include <ui/DebugUtils.h>
+#include <ui/HdrCapabilities.h>
+#include <utils/Trace.h>
+
+#include "TracedOrdinal.h"
 
 namespace android::compositionengine {
 
@@ -29,20 +50,43 @@
 
 namespace impl {
 
-Output::Output(const CompositionEngine& compositionEngine)
-      : mCompositionEngine(compositionEngine) {}
+namespace {
+
+template <typename T>
+class Reversed {
+public:
+    explicit Reversed(const T& container) : mContainer(container) {}
+    auto begin() { return mContainer.rbegin(); }
+    auto end() { return mContainer.rend(); }
+
+private:
+    const T& mContainer;
+};
+
+// Helper for enumerating over a container in reverse order
+template <typename T>
+Reversed<T> reversed(const T& c) {
+    return Reversed<T>(c);
+}
+
+} // namespace
+
+std::shared_ptr<Output> createOutput(
+        const compositionengine::CompositionEngine& compositionEngine) {
+    return createOutputTemplated<Output>(compositionEngine);
+}
 
 Output::~Output() = default;
 
-const CompositionEngine& Output::getCompositionEngine() const {
-    return mCompositionEngine;
-}
-
 bool Output::isValid() const {
     return mDisplayColorProfile && mDisplayColorProfile->isValid() && mRenderSurface &&
             mRenderSurface->isValid();
 }
 
+std::optional<DisplayId> Output::getDisplayId() const {
+    return {};
+}
+
 const std::string& Output::getName() const {
     return mName;
 }
@@ -52,73 +96,81 @@
 }
 
 void Output::setCompositionEnabled(bool enabled) {
-    if (mState.isEnabled == enabled) {
+    auto& outputState = editState();
+    if (outputState.isEnabled == enabled) {
         return;
     }
 
-    mState.isEnabled = enabled;
+    outputState.isEnabled = enabled;
     dirtyEntireOutput();
 }
 
-void Output::setProjection(const ui::Transform& transform, int32_t orientation, const Rect& frame,
-                           const Rect& viewport, const Rect& scissor, bool needsFiltering) {
-    mState.transform = transform;
-    mState.orientation = orientation;
-    mState.scissor = scissor;
-    mState.frame = frame;
-    mState.viewport = viewport;
-    mState.needsFiltering = needsFiltering;
+void Output::setProjection(const ui::Transform& transform, uint32_t orientation, const Rect& frame,
+                           const Rect& viewport, const Rect& sourceClip,
+                           const Rect& destinationClip, bool needsFiltering) {
+    auto& outputState = editState();
+    outputState.transform = transform;
+    outputState.orientation = orientation;
+    outputState.sourceClip = sourceClip;
+    outputState.destinationClip = destinationClip;
+    outputState.frame = frame;
+    outputState.viewport = viewport;
+    outputState.needsFiltering = needsFiltering;
 
     dirtyEntireOutput();
 }
 
-// TODO(lpique): Rename setSize() once more is moved.
+// TODO(b/121291683): Rename setSize() once more is moved.
 void Output::setBounds(const ui::Size& size) {
     mRenderSurface->setDisplaySize(size);
-    // TODO(lpique): Rename mState.size once more is moved.
-    mState.bounds = Rect(mRenderSurface->getSize());
+    // TODO(b/121291683): Rename outputState.size once more is moved.
+    editState().bounds = Rect(mRenderSurface->getSize());
 
     dirtyEntireOutput();
 }
 
 void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
-    mState.layerStackId = layerStackId;
-    mState.layerStackInternal = isInternal;
+    auto& outputState = editState();
+    outputState.layerStackId = layerStackId;
+    outputState.layerStackInternal = isInternal;
 
     dirtyEntireOutput();
 }
 
-void Output::setColorTransform(const mat4& transform) {
-    if (mState.colorTransformMat == transform) {
+void Output::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
+    auto& colorTransformMatrix = editState().colorTransformMatrix;
+    if (!args.colorTransformMatrix || colorTransformMatrix == args.colorTransformMatrix) {
         return;
     }
 
-    const bool isIdentity = (transform == mat4());
-    const auto newColorTransform =
-            isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
-
-    mState.colorTransform = newColorTransform;
-    mState.colorTransformMat = transform;
+    colorTransformMatrix = *args.colorTransformMatrix;
 
     dirtyEntireOutput();
 }
 
-void Output::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
-                          ui::RenderIntent renderIntent) {
-    if (mState.colorMode == mode && mState.dataspace == dataspace &&
-        mState.renderIntent == renderIntent) {
+void Output::setColorProfile(const ColorProfile& colorProfile) {
+    ui::Dataspace targetDataspace =
+            getDisplayColorProfile()->getTargetDataspace(colorProfile.mode, colorProfile.dataspace,
+                                                         colorProfile.colorSpaceAgnosticDataspace);
+
+    auto& outputState = editState();
+    if (outputState.colorMode == colorProfile.mode &&
+        outputState.dataspace == colorProfile.dataspace &&
+        outputState.renderIntent == colorProfile.renderIntent &&
+        outputState.targetDataspace == targetDataspace) {
         return;
     }
 
-    mState.colorMode = mode;
-    mState.dataspace = dataspace;
-    mState.renderIntent = renderIntent;
+    outputState.colorMode = colorProfile.mode;
+    outputState.dataspace = colorProfile.dataspace;
+    outputState.renderIntent = colorProfile.renderIntent;
+    outputState.targetDataspace = targetDataspace;
 
-    mRenderSurface->setBufferDataspace(dataspace);
+    mRenderSurface->setBufferDataspace(colorProfile.dataspace);
 
     ALOGV("Set active color mode: %s (%d), active render intent: %s (%d)",
-          decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(),
-          renderIntent);
+          decodeColorMode(colorProfile.mode).c_str(), colorProfile.mode,
+          decodeRenderIntent(colorProfile.renderIntent).c_str(), colorProfile.renderIntent);
 
     dirtyEntireOutput();
 }
@@ -134,7 +186,7 @@
 }
 
 void Output::dumpBase(std::string& out) const {
-    mState.dump(out);
+    dumpState(out);
 
     if (mDisplayColorProfile) {
         mDisplayColorProfile->dump(out);
@@ -148,8 +200,8 @@
         out.append("    No render surface!\n");
     }
 
-    android::base::StringAppendF(&out, "\n   %zu Layers\b", mOutputLayersOrderedByZ.size());
-    for (const auto& outputLayer : mOutputLayersOrderedByZ) {
+    android::base::StringAppendF(&out, "\n   %zu Layers\n", getOutputLayerCount());
+    for (const auto* outputLayer : getOutputLayersOrderedByZ()) {
         if (!outputLayer) {
             continue;
         }
@@ -165,6 +217,10 @@
     mDisplayColorProfile = std::move(mode);
 }
 
+const Output::ReleasedLayers& Output::getReleasedLayersForTest() const {
+    return mReleasedLayers;
+}
+
 void Output::setDisplayColorProfileForTest(
         std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
     mDisplayColorProfile = std::move(mode);
@@ -176,68 +232,888 @@
 
 void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
     mRenderSurface = std::move(surface);
-    mState.bounds = Rect(mRenderSurface->getSize());
+    editState().bounds = Rect(mRenderSurface->getSize());
 
     dirtyEntireOutput();
 }
 
+void Output::cacheClientCompositionRequests(uint32_t cacheSize) {
+    if (cacheSize == 0) {
+        mClientCompositionRequestCache.reset();
+    } else {
+        mClientCompositionRequestCache = std::make_unique<ClientCompositionRequestCache>(cacheSize);
+    }
+};
+
 void Output::setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface> surface) {
     mRenderSurface = std::move(surface);
 }
 
-const OutputCompositionState& Output::getState() const {
-    return mState;
-}
-
-OutputCompositionState& Output::editState() {
-    return mState;
-}
-
 Region Output::getDirtyRegion(bool repaintEverything) const {
-    Region dirty(mState.viewport);
+    const auto& outputState = getState();
+    Region dirty(outputState.viewport);
     if (!repaintEverything) {
-        dirty.andSelf(mState.dirtyRegion);
+        dirty.andSelf(outputState.dirtyRegion);
     }
     return dirty;
 }
 
-bool Output::belongsInOutput(uint32_t layerStackId, bool internalOnly) const {
+bool Output::belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const {
     // The layerStackId's must match, and also the layer must not be internal
     // only when not on an internal output.
-    return (layerStackId == mState.layerStackId) && (!internalOnly || mState.layerStackInternal);
+    const auto& outputState = getState();
+    return layerStackId && (*layerStackId == outputState.layerStackId) &&
+            (!internalOnly || outputState.layerStackInternal);
 }
 
-compositionengine::OutputLayer* Output::getOutputLayerForLayer(
-        compositionengine::Layer* layer) const {
-    for (const auto& outputLayer : mOutputLayersOrderedByZ) {
-        if (outputLayer && &outputLayer->getLayer() == layer) {
-            return outputLayer.get();
+bool Output::belongsInOutput(const sp<compositionengine::LayerFE>& layerFE) const {
+    const auto* layerFEState = layerFE->getCompositionState();
+    return layerFEState && belongsInOutput(layerFEState->layerStackId, layerFEState->internalOnly);
+}
+
+std::unique_ptr<compositionengine::OutputLayer> Output::createOutputLayer(
+        const sp<LayerFE>& layerFE) const {
+    return impl::createOutputLayer(*this, layerFE);
+}
+
+compositionengine::OutputLayer* Output::getOutputLayerForLayer(const sp<LayerFE>& layerFE) const {
+    auto index = findCurrentOutputLayerForLayer(layerFE);
+    return index ? getOutputLayerOrderedByZByIndex(*index) : nullptr;
+}
+
+std::optional<size_t> Output::findCurrentOutputLayerForLayer(
+        const sp<compositionengine::LayerFE>& layer) const {
+    for (size_t i = 0; i < getOutputLayerCount(); i++) {
+        auto outputLayer = getOutputLayerOrderedByZByIndex(i);
+        if (outputLayer && &outputLayer->getLayerFE() == layer.get()) {
+            return i;
         }
     }
-    return nullptr;
+    return std::nullopt;
 }
 
-std::unique_ptr<compositionengine::OutputLayer> Output::getOrCreateOutputLayer(
-        std::optional<DisplayId> displayId, std::shared_ptr<compositionengine::Layer> layer,
-        sp<compositionengine::LayerFE> layerFE) {
-    for (auto& outputLayer : mOutputLayersOrderedByZ) {
-        if (outputLayer && &outputLayer->getLayer() == layer.get()) {
-            return std::move(outputLayer);
+void Output::setReleasedLayers(Output::ReleasedLayers&& layers) {
+    mReleasedLayers = std::move(layers);
+}
+
+void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArgs,
+                     LayerFESet& geomSnapshots) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    rebuildLayerStacks(refreshArgs, geomSnapshots);
+}
+
+void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    updateColorProfile(refreshArgs);
+    updateAndWriteCompositionState(refreshArgs);
+    setColorTransform(refreshArgs);
+    beginFrame();
+    prepareFrame();
+    devOptRepaintFlash(refreshArgs);
+    finishFrame(refreshArgs);
+    postFramebuffer();
+}
+
+void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
+                                LayerFESet& layerFESet) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    auto& outputState = editState();
+
+    // Do nothing if this output is not enabled or there is no need to perform this update
+    if (!outputState.isEnabled || CC_LIKELY(!refreshArgs.updatingOutputGeometryThisFrame)) {
+        return;
+    }
+
+    // Process the layers to determine visibility and coverage
+    compositionengine::Output::CoverageState coverage{layerFESet};
+    collectVisibleLayers(refreshArgs, coverage);
+
+    // Compute the resulting coverage for this output, and store it for later
+    const ui::Transform& tr = outputState.transform;
+    Region undefinedRegion{outputState.bounds};
+    undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));
+
+    outputState.undefinedRegion = undefinedRegion;
+    outputState.dirtyRegion.orSelf(coverage.dirtyRegion);
+}
+
+void Output::collectVisibleLayers(const compositionengine::CompositionRefreshArgs& refreshArgs,
+                                  compositionengine::Output::CoverageState& coverage) {
+    // Evaluate the layers from front to back to determine what is visible. This
+    // also incrementally calculates the coverage information for each layer as
+    // well as the entire output.
+    for (auto layer : reversed(refreshArgs.layers)) {
+        // Incrementally process the coverage for each layer
+        ensureOutputLayerIfVisible(layer, coverage);
+
+        // TODO(b/121291683): Stop early if the output is completely covered and
+        // no more layers could even be visible underneath the ones on top.
+    }
+
+    setReleasedLayers(refreshArgs);
+
+    finalizePendingOutputLayers();
+
+    // Generate a simple Z-order values to each visible output layer
+    uint32_t zOrder = 0;
+    for (auto* outputLayer : getOutputLayersOrderedByZ()) {
+        outputLayer->editState().z = zOrder++;
+    }
+}
+
+void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE,
+                                        compositionengine::Output::CoverageState& coverage) {
+    // Ensure we have a snapshot of the basic geometry layer state. Limit the
+    // snapshots to once per frame for each candidate layer, as layers may
+    // appear on multiple outputs.
+    if (!coverage.latchedLayers.count(layerFE)) {
+        coverage.latchedLayers.insert(layerFE);
+        layerFE->prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry);
+    }
+
+    // Only consider the layers on the given layer stack
+    if (!belongsInOutput(layerFE)) {
+        return;
+    }
+
+    // Obtain a read-only pointer to the front-end layer state
+    const auto* layerFEState = layerFE->getCompositionState();
+    if (CC_UNLIKELY(!layerFEState)) {
+        return;
+    }
+
+    // handle hidden surfaces by setting the visible region to empty
+    if (CC_UNLIKELY(!layerFEState->isVisible)) {
+        return;
+    }
+
+    /*
+     * opaqueRegion: area of a surface that is fully opaque.
+     */
+    Region opaqueRegion;
+
+    /*
+     * visibleRegion: area of a surface that is visible on screen and not fully
+     * transparent. This is essentially the layer's footprint minus the opaque
+     * regions above it. Areas covered by a translucent surface are considered
+     * visible.
+     */
+    Region visibleRegion;
+
+    /*
+     * coveredRegion: area of a surface that is covered by all visible regions
+     * above it (which includes the translucent areas).
+     */
+    Region coveredRegion;
+
+    /*
+     * transparentRegion: area of a surface that is hinted to be completely
+     * transparent. This is only used to tell when the layer has no visible non-
+     * transparent regions and can be removed from the layer list. It does not
+     * affect the visibleRegion of this layer or any layers beneath it. The hint
+     * may not be correct if apps don't respect the SurfaceView restrictions
+     * (which, sadly, some don't).
+     */
+    Region transparentRegion;
+
+    /*
+     * shadowRegion: Region cast by the layer's shadow.
+     */
+    Region shadowRegion;
+
+    const ui::Transform& tr = layerFEState->geomLayerTransform;
+
+    // Get the visible region
+    // TODO(b/121291683): Is it worth creating helper methods on LayerFEState
+    // for computations like this?
+    const Rect visibleRect(tr.transform(layerFEState->geomLayerBounds));
+    visibleRegion.set(visibleRect);
+
+    if (layerFEState->shadowRadius > 0.0f) {
+        // if the layer casts a shadow, offset the layers visible region and
+        // calculate the shadow region.
+        const auto inset = static_cast<int32_t>(ceilf(layerFEState->shadowRadius) * -1.0f);
+        Rect visibleRectWithShadows(visibleRect);
+        visibleRectWithShadows.inset(inset, inset, inset, inset);
+        visibleRegion.set(visibleRectWithShadows);
+        shadowRegion = visibleRegion.subtract(visibleRect);
+    }
+
+    if (visibleRegion.isEmpty()) {
+        return;
+    }
+
+    // Remove the transparent area from the visible region
+    if (!layerFEState->isOpaque) {
+        if (tr.preserveRects()) {
+            // transform the transparent region
+            transparentRegion = tr.transform(layerFEState->transparentRegionHint);
+        } else {
+            // transformation too complex, can't do the
+            // transparent region optimization.
+            transparentRegion.clear();
         }
     }
-    return createOutputLayer(mCompositionEngine, displayId, *this, layer, layerFE);
+
+    // compute the opaque region
+    const auto layerOrientation = tr.getOrientation();
+    if (layerFEState->isOpaque && ((layerOrientation & ui::Transform::ROT_INVALID) == 0)) {
+        // If we one of the simple category of transforms (0/90/180/270 rotation
+        // + any flip), then the opaque region is the layer's footprint.
+        // Otherwise we don't try and compute the opaque region since there may
+        // be errors at the edges, and we treat the entire layer as
+        // translucent.
+        opaqueRegion.set(visibleRect);
+    }
+
+    // Clip the covered region to the visible region
+    coveredRegion = coverage.aboveCoveredLayers.intersect(visibleRegion);
+
+    // Update accumAboveCoveredLayers for next (lower) layer
+    coverage.aboveCoveredLayers.orSelf(visibleRegion);
+
+    // subtract the opaque region covered by the layers above us
+    visibleRegion.subtractSelf(coverage.aboveOpaqueLayers);
+
+    if (visibleRegion.isEmpty()) {
+        return;
+    }
+
+    // Get coverage information for the layer as previously displayed,
+    // also taking over ownership from mOutputLayersorderedByZ.
+    auto prevOutputLayerIndex = findCurrentOutputLayerForLayer(layerFE);
+    auto prevOutputLayer =
+            prevOutputLayerIndex ? getOutputLayerOrderedByZByIndex(*prevOutputLayerIndex) : nullptr;
+
+    //  Get coverage information for the layer as previously displayed
+    // TODO(b/121291683): Define kEmptyRegion as a constant in Region.h
+    const Region kEmptyRegion;
+    const Region& oldVisibleRegion =
+            prevOutputLayer ? prevOutputLayer->getState().visibleRegion : kEmptyRegion;
+    const Region& oldCoveredRegion =
+            prevOutputLayer ? prevOutputLayer->getState().coveredRegion : kEmptyRegion;
+
+    // compute this layer's dirty region
+    Region dirty;
+    if (layerFEState->contentDirty) {
+        // we need to invalidate the whole region
+        dirty = visibleRegion;
+        // as well, as the old visible region
+        dirty.orSelf(oldVisibleRegion);
+    } else {
+        /* compute the exposed region:
+         *   the exposed region consists of two components:
+         *   1) what's VISIBLE now and was COVERED before
+         *   2) what's EXPOSED now less what was EXPOSED before
+         *
+         * note that (1) is conservative, we start with the whole visible region
+         * but only keep what used to be covered by something -- which mean it
+         * may have been exposed.
+         *
+         * (2) handles areas that were not covered by anything but got exposed
+         * because of a resize.
+         *
+         */
+        const Region newExposed = visibleRegion - coveredRegion;
+        const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
+        dirty = (visibleRegion & oldCoveredRegion) | (newExposed - oldExposed);
+    }
+    dirty.subtractSelf(coverage.aboveOpaqueLayers);
+
+    // accumulate to the screen dirty region
+    coverage.dirtyRegion.orSelf(dirty);
+
+    // Update accumAboveOpaqueLayers for next (lower) layer
+    coverage.aboveOpaqueLayers.orSelf(opaqueRegion);
+
+    // Compute the visible non-transparent region
+    Region visibleNonTransparentRegion = visibleRegion.subtract(transparentRegion);
+
+    // Perform the final check to see if this layer is visible on this output
+    // TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
+    const auto& outputState = getState();
+    Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
+    drawRegion.andSelf(outputState.bounds);
+    if (drawRegion.isEmpty()) {
+        return;
+    }
+
+    Region visibleNonShadowRegion = visibleRegion.subtract(shadowRegion);
+
+    // The layer is visible. Either reuse the existing outputLayer if we have
+    // one, or create a new one if we do not.
+    auto result = ensureOutputLayer(prevOutputLayerIndex, layerFE);
+
+    // Store the layer coverage information into the layer state as some of it
+    // is useful later.
+    auto& outputLayerState = result->editState();
+    outputLayerState.visibleRegion = visibleRegion;
+    outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
+    outputLayerState.coveredRegion = coveredRegion;
+    outputLayerState.outputSpaceVisibleRegion =
+            outputState.transform.transform(visibleNonShadowRegion.intersect(outputState.viewport));
+    outputLayerState.shadowRegion = shadowRegion;
 }
 
-void Output::setOutputLayersOrderedByZ(OutputLayers&& layers) {
-    mOutputLayersOrderedByZ = std::move(layers);
+void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
+    // The base class does nothing with this call.
 }
 
-const Output::OutputLayers& Output::getOutputLayersOrderedByZ() const {
-    return mOutputLayersOrderedByZ;
+void Output::updateLayerStateFromFE(const CompositionRefreshArgs& args) const {
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        layer->getLayerFE().prepareCompositionState(
+                args.updatingGeometryThisFrame ? LayerFE::StateSubset::GeometryAndContent
+                                               : LayerFE::StateSubset::Content);
+    }
+}
+
+void Output::updateAndWriteCompositionState(
+        const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    if (!getState().isEnabled) {
+        return;
+    }
+
+    mLayerRequestingBackgroundBlur = findLayerRequestingBackgroundComposition();
+    bool forceClientComposition = mLayerRequestingBackgroundBlur != nullptr;
+
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame,
+                                      refreshArgs.devOptForceClientComposition ||
+                                              forceClientComposition,
+                                      refreshArgs.internalDisplayRotationFlags);
+
+        if (mLayerRequestingBackgroundBlur == layer) {
+            forceClientComposition = false;
+        }
+
+        // Send the updated state to the HWC, if appropriate.
+        layer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame);
+    }
+}
+
+compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const {
+    compositionengine::OutputLayer* layerRequestingBgComposition = nullptr;
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0) {
+            layerRequestingBgComposition = layer;
+        }
+    }
+    return layerRequestingBgComposition;
+}
+
+void Output::updateColorProfile(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    setColorProfile(pickColorProfile(refreshArgs));
+}
+
+// Returns a data space that fits all visible layers.  The returned data space
+// can only be one of
+//  - Dataspace::SRGB (use legacy dataspace and let HWC saturate when colors are enhanced)
+//  - Dataspace::DISPLAY_P3
+//  - Dataspace::DISPLAY_BT2020
+// The returned HDR data space is one of
+//  - Dataspace::UNKNOWN
+//  - Dataspace::BT2020_HLG
+//  - Dataspace::BT2020_PQ
+ui::Dataspace Output::getBestDataspace(ui::Dataspace* outHdrDataSpace,
+                                       bool* outIsHdrClientComposition) const {
+    ui::Dataspace bestDataSpace = ui::Dataspace::V0_SRGB;
+    *outHdrDataSpace = ui::Dataspace::UNKNOWN;
+
+    for (const auto* layer : getOutputLayersOrderedByZ()) {
+        switch (layer->getLayerFE().getCompositionState()->dataspace) {
+            case ui::Dataspace::V0_SCRGB:
+            case ui::Dataspace::V0_SCRGB_LINEAR:
+            case ui::Dataspace::BT2020:
+            case ui::Dataspace::BT2020_ITU:
+            case ui::Dataspace::BT2020_LINEAR:
+            case ui::Dataspace::DISPLAY_BT2020:
+                bestDataSpace = ui::Dataspace::DISPLAY_BT2020;
+                break;
+            case ui::Dataspace::DISPLAY_P3:
+                bestDataSpace = ui::Dataspace::DISPLAY_P3;
+                break;
+            case ui::Dataspace::BT2020_PQ:
+            case ui::Dataspace::BT2020_ITU_PQ:
+                bestDataSpace = ui::Dataspace::DISPLAY_P3;
+                *outHdrDataSpace = ui::Dataspace::BT2020_PQ;
+                *outIsHdrClientComposition =
+                        layer->getLayerFE().getCompositionState()->forceClientComposition;
+                break;
+            case ui::Dataspace::BT2020_HLG:
+            case ui::Dataspace::BT2020_ITU_HLG:
+                bestDataSpace = ui::Dataspace::DISPLAY_P3;
+                // When there's mixed PQ content and HLG content, we set the HDR
+                // data space to be BT2020_PQ and convert HLG to PQ.
+                if (*outHdrDataSpace == ui::Dataspace::UNKNOWN) {
+                    *outHdrDataSpace = ui::Dataspace::BT2020_HLG;
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    return bestDataSpace;
+}
+
+compositionengine::Output::ColorProfile Output::pickColorProfile(
+        const compositionengine::CompositionRefreshArgs& refreshArgs) const {
+    if (refreshArgs.outputColorSetting == OutputColorSetting::kUnmanaged) {
+        return ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+                            ui::RenderIntent::COLORIMETRIC,
+                            refreshArgs.colorSpaceAgnosticDataspace};
+    }
+
+    ui::Dataspace hdrDataSpace;
+    bool isHdrClientComposition = false;
+    ui::Dataspace bestDataSpace = getBestDataspace(&hdrDataSpace, &isHdrClientComposition);
+
+    switch (refreshArgs.forceOutputColorMode) {
+        case ui::ColorMode::SRGB:
+            bestDataSpace = ui::Dataspace::V0_SRGB;
+            break;
+        case ui::ColorMode::DISPLAY_P3:
+            bestDataSpace = ui::Dataspace::DISPLAY_P3;
+            break;
+        default:
+            break;
+    }
+
+    // respect hdrDataSpace only when there is no legacy HDR support
+    const bool isHdr = hdrDataSpace != ui::Dataspace::UNKNOWN &&
+            !mDisplayColorProfile->hasLegacyHdrSupport(hdrDataSpace) && !isHdrClientComposition;
+    if (isHdr) {
+        bestDataSpace = hdrDataSpace;
+    }
+
+    ui::RenderIntent intent;
+    switch (refreshArgs.outputColorSetting) {
+        case OutputColorSetting::kManaged:
+        case OutputColorSetting::kUnmanaged:
+            intent = isHdr ? ui::RenderIntent::TONE_MAP_COLORIMETRIC
+                           : ui::RenderIntent::COLORIMETRIC;
+            break;
+        case OutputColorSetting::kEnhanced:
+            intent = isHdr ? ui::RenderIntent::TONE_MAP_ENHANCE : ui::RenderIntent::ENHANCE;
+            break;
+        default: // vendor display color setting
+            intent = static_cast<ui::RenderIntent>(refreshArgs.outputColorSetting);
+            break;
+    }
+
+    ui::ColorMode outMode;
+    ui::Dataspace outDataSpace;
+    ui::RenderIntent outRenderIntent;
+    mDisplayColorProfile->getBestColorMode(bestDataSpace, intent, &outDataSpace, &outMode,
+                                           &outRenderIntent);
+
+    return ColorProfile{outMode, outDataSpace, outRenderIntent,
+                        refreshArgs.colorSpaceAgnosticDataspace};
+}
+
+void Output::beginFrame() {
+    auto& outputState = editState();
+    const bool dirty = !getDirtyRegion(false).isEmpty();
+    const bool empty = getOutputLayerCount() == 0;
+    const bool wasEmpty = !outputState.lastCompositionHadVisibleLayers;
+
+    // If nothing has changed (!dirty), don't recompose.
+    // If something changed, but we don't currently have any visible layers,
+    //   and didn't when we last did a composition, then skip it this time.
+    // The second rule does two things:
+    // - When all layers are removed from a display, we'll emit one black
+    //   frame, then nothing more until we get new layers.
+    // - When a display is created with a private layer stack, we won't
+    //   emit any black frames until a layer is added to the layer stack.
+    const bool mustRecompose = dirty && !(empty && wasEmpty);
+
+    const char flagPrefix[] = {'-', '+'};
+    static_cast<void>(flagPrefix);
+    ALOGV_IF("%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", __FUNCTION__,
+             mustRecompose ? "doing" : "skipping", getName().c_str(), flagPrefix[dirty],
+             flagPrefix[empty], flagPrefix[wasEmpty]);
+
+    mRenderSurface->beginFrame(mustRecompose);
+
+    if (mustRecompose) {
+        outputState.lastCompositionHadVisibleLayers = !empty;
+    }
+}
+
+void Output::prepareFrame() {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    const auto& outputState = getState();
+    if (!outputState.isEnabled) {
+        return;
+    }
+
+    chooseCompositionStrategy();
+
+    mRenderSurface->prepareFrame(outputState.usesClientComposition,
+                                 outputState.usesDeviceComposition);
+}
+
+void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    if (CC_LIKELY(!refreshArgs.devOptFlashDirtyRegionsDelay)) {
+        return;
+    }
+
+    if (getState().isEnabled) {
+        // transform the dirty region into this screen's coordinate space
+        const Region dirtyRegion = getDirtyRegion(refreshArgs.repaintEverything);
+        if (!dirtyRegion.isEmpty()) {
+            base::unique_fd readyFence;
+            // redraw the whole screen
+            static_cast<void>(composeSurfaces(dirtyRegion, refreshArgs));
+
+            mRenderSurface->queueBuffer(std::move(readyFence));
+        }
+    }
+
+    postFramebuffer();
+
+    std::this_thread::sleep_for(*refreshArgs.devOptFlashDirtyRegionsDelay);
+
+    prepareFrame();
+}
+
+void Output::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    if (!getState().isEnabled) {
+        return;
+    }
+
+    // Repaint the framebuffer (if needed), getting the optional fence for when
+    // the composition completes.
+    auto optReadyFence = composeSurfaces(Region::INVALID_REGION, refreshArgs);
+    if (!optReadyFence) {
+        return;
+    }
+
+    // swap buffers (presentation)
+    mRenderSurface->queueBuffer(std::move(*optReadyFence));
+}
+
+std::optional<base::unique_fd> Output::composeSurfaces(
+        const Region& debugRegion, const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    const auto& outputState = getState();
+    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);
+        return readyFence;
+    }
+
+    ALOGV("hasClientComposition");
+
+    renderengine::DisplaySettings clientCompositionDisplay;
+    clientCompositionDisplay.physicalDisplay = outputState.destinationClip;
+    clientCompositionDisplay.clip = outputState.sourceClip;
+    clientCompositionDisplay.orientation = outputState.orientation;
+    clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
+            ? outputState.dataspace
+            : ui::Dataspace::UNKNOWN;
+    clientCompositionDisplay.maxLuminance =
+            mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
+
+    // Compute the global color transform matrix.
+    if (!outputState.usesDeviceComposition && !getSkipColorTransform()) {
+        clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
+    }
+
+    // Note: Updated by generateClientCompositionRequests
+    clientCompositionDisplay.clearRegion = Region::INVALID_REGION;
+
+    // Generate the client composition requests for the layers on this output.
+    std::vector<LayerFE::LayerSettings> clientCompositionLayers =
+            generateClientCompositionRequests(supportsProtectedContent,
+                                              clientCompositionDisplay.clearRegion,
+                                              clientCompositionDisplay.outputDataspace);
+    appendRegionFlashRequests(debugRegion, clientCompositionLayers);
+
+    // 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) {
+        if (mClientCompositionRequestCache->exists(buf->getId(), clientCompositionDisplay,
+                                                   clientCompositionLayers)) {
+            outputCompositionState.reusedClientComposition = true;
+            setExpensiveRenderingExpected(false);
+            return readyFence;
+        }
+        mClientCompositionRequestCache->add(buf->getId(), clientCompositionDisplay,
+                                            clientCompositionLayers);
+    }
+
+    // We boost GPU frequency here because there will be color spaces conversion
+    // or complex GPU shaders and it's expensive. We boost the GPU frequency so that
+    // GPU composition can finish in time. We must reset GPU frequency afterwards,
+    // because high frequency consumes extra battery.
+    const bool expensiveBlurs =
+            refreshArgs.blursAreExpensive && mLayerRequestingBackgroundBlur != nullptr;
+    const bool expensiveRenderingExpected =
+            clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3 || expensiveBlurs;
+    if (expensiveRenderingExpected) {
+        setExpensiveRenderingExpected(true);
+    }
+
+    std::vector<const renderengine::LayerSettings*> clientCompositionLayerPointers;
+    clientCompositionLayerPointers.reserve(clientCompositionLayers.size());
+    std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(),
+                   std::back_inserter(clientCompositionLayerPointers),
+                   [](LayerFE::LayerSettings& settings) -> renderengine::LayerSettings* {
+                       return &settings;
+                   });
+
+    const nsecs_t renderEngineStart = systemTime();
+    status_t status =
+            renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers,
+                                    buf->getNativeBuffer(), /*useFramebufferCache=*/true,
+                                    std::move(fd), &readyFence);
+
+    if (status != NO_ERROR && mClientCompositionRequestCache) {
+        // If rendering was not successful, remove the request from the cache.
+        mClientCompositionRequestCache->remove(buf->getId());
+    }
+
+    auto& timeStats = getCompositionEngine().getTimeStats();
+    if (readyFence.get() < 0) {
+        timeStats.recordRenderEngineDuration(renderEngineStart, systemTime());
+    } else {
+        timeStats.recordRenderEngineDuration(renderEngineStart,
+                                             std::make_shared<FenceTime>(
+                                                     new Fence(dup(readyFence.get()))));
+    }
+
+    return readyFence;
+}
+
+std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests(
+        bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) {
+    std::vector<LayerFE::LayerSettings> clientCompositionLayers;
+    ALOGV("Rendering client layers");
+
+    const auto& outputState = getState();
+    const Region viewportRegion(outputState.viewport);
+    const bool useIdentityTransform = false;
+    bool firstLayer = true;
+    // Used when a layer clears part of the buffer.
+    Region stubRegion;
+
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        const auto& layerState = layer->getState();
+        const auto* layerFEState = layer->getLayerFE().getCompositionState();
+        auto& layerFE = layer->getLayerFE();
+
+        const Region clip(viewportRegion.intersect(layerState.visibleRegion));
+        ALOGV("Layer: %s", layerFE.getDebugName());
+        if (clip.isEmpty()) {
+            ALOGV("  Skipping for empty clip");
+            firstLayer = false;
+            continue;
+        }
+
+        const bool clientComposition = layer->requiresClientComposition();
+
+        // We clear the client target for non-client composed layers if
+        // requested by the HWC. We skip this if the layer is not an opaque
+        // rectangle, as by definition the layer must blend with whatever is
+        // underneath. We also skip the first layer as the buffer target is
+        // guaranteed to start out cleared.
+        const bool clearClientComposition =
+                layerState.clearClientTarget && layerFEState->isOpaque && !firstLayer;
+
+        ALOGV("  Composition type: client %d clear %d", clientComposition, clearClientComposition);
+
+        // If the layer casts a shadow but the content casting the shadow is occluded, skip
+        // composing the non-shadow content and only draw the shadows.
+        const bool realContentIsVisible = clientComposition &&
+                !layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty();
+
+        if (clientComposition || clearClientComposition) {
+            compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
+                    clip,
+                    useIdentityTransform,
+                    layer->needsFiltering() || outputState.needsFiltering,
+                    outputState.isSecure,
+                    supportsProtectedContent,
+                    clientComposition ? clearRegion : stubRegion,
+                    outputState.viewport,
+                    outputDataspace,
+                    realContentIsVisible,
+                    !clientComposition, /* clearContent  */
+            };
+            std::vector<LayerFE::LayerSettings> results =
+                    layerFE.prepareClientCompositionList(targetSettings);
+            if (realContentIsVisible && !results.empty()) {
+                layer->editState().clientCompositionTimestamp = systemTime();
+            }
+
+            clientCompositionLayers.insert(clientCompositionLayers.end(),
+                                           std::make_move_iterator(results.begin()),
+                                           std::make_move_iterator(results.end()));
+            results.clear();
+        }
+
+        firstLayer = false;
+    }
+
+    return clientCompositionLayers;
+}
+
+void Output::appendRegionFlashRequests(
+        const Region& flashRegion, std::vector<LayerFE::LayerSettings>& clientCompositionLayers) {
+    if (flashRegion.isEmpty()) {
+        return;
+    }
+
+    LayerFE::LayerSettings layerSettings;
+    layerSettings.source.buffer.buffer = nullptr;
+    layerSettings.source.solidColor = half3(1.0, 0.0, 1.0);
+    layerSettings.alpha = half(1.0);
+
+    for (const auto& rect : flashRegion) {
+        layerSettings.geometry.boundaries = rect.toFloatRect();
+        clientCompositionLayers.push_back(layerSettings);
+    }
+}
+
+void Output::setExpensiveRenderingExpected(bool) {
+    // The base class does nothing with this call.
+}
+
+void Output::postFramebuffer() {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    if (!getState().isEnabled) {
+        return;
+    }
+
+    auto& outputState = editState();
+    outputState.dirtyRegion.clear();
+    mRenderSurface->flip();
+
+    auto frame = presentAndGetFrameFences();
+
+    mRenderSurface->onPresentDisplayCompleted();
+
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        // The layer buffer from the previous frame (if any) is released
+        // by HWC only when the release fence from this frame (if any) is
+        // signaled.  Always get the release fence from HWC first.
+        sp<Fence> releaseFence = Fence::NO_FENCE;
+
+        if (auto hwcLayer = layer->getHwcLayer()) {
+            if (auto f = frame.layerFences.find(hwcLayer); f != frame.layerFences.end()) {
+                releaseFence = f->second;
+            }
+        }
+
+        // If the layer was client composited in the previous frame, we
+        // need to merge with the previous client target acquire fence.
+        // Since we do not track that, always merge with the current
+        // client target acquire fence when it is available, even though
+        // this is suboptimal.
+        // TODO(b/121291683): Track previous frame client target acquire fence.
+        if (outputState.usesClientComposition) {
+            releaseFence =
+                    Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
+        }
+
+        layer->getLayerFE().onLayerDisplayed(releaseFence);
+    }
+
+    // We've got a list of layers needing fences, that are disjoint with
+    // OutputLayersOrderedByZ.  The best we can do is to
+    // supply them with the present fence.
+    for (auto& weakLayer : mReleasedLayers) {
+        if (auto layer = weakLayer.promote(); layer != nullptr) {
+            layer->onLayerDisplayed(frame.presentFence);
+        }
+    }
+
+    // Clear out the released layers now that we're done with them.
+    mReleasedLayers.clear();
 }
 
 void Output::dirtyEntireOutput() {
-    mState.dirtyRegion.set(mState.bounds);
+    auto& outputState = editState();
+    outputState.dirtyRegion.set(outputState.bounds);
+}
+
+void Output::chooseCompositionStrategy() {
+    // The base output implementation can only do client composition
+    auto& outputState = editState();
+    outputState.usesClientComposition = true;
+    outputState.usesDeviceComposition = false;
+    outputState.reusedClientComposition = false;
+}
+
+bool Output::getSkipColorTransform() const {
+    return true;
+}
+
+compositionengine::Output::FrameFences Output::presentAndGetFrameFences() {
+    compositionengine::Output::FrameFences result;
+    if (getState().usesClientComposition) {
+        result.clientTargetAcquireFence = mRenderSurface->getClientTargetAcquireFence();
+    }
+    return result;
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 9549054..4835aef 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -24,6 +24,11 @@
     dumpVal(out, "isEnabled", isEnabled);
     dumpVal(out, "isSecure", isSecure);
 
+    dumpVal(out, "usesClientComposition", usesClientComposition);
+    dumpVal(out, "usesDeviceComposition", usesDeviceComposition);
+    dumpVal(out, "flipClientTarget", flipClientTarget);
+    dumpVal(out, "reusedClientComposition", reusedClientComposition);
+
     dumpVal(out, "layerStack", layerStackId);
     dumpVal(out, "layerStackInternal", layerStackInternal);
 
@@ -33,9 +38,11 @@
 
     out.append("\n   ");
 
+    dumpVal(out, "bounds", bounds);
     dumpVal(out, "frame", frame);
     dumpVal(out, "viewport", viewport);
-    dumpVal(out, "scissor", scissor);
+    dumpVal(out, "sourceClip", sourceClip);
+    dumpVal(out, "destinationClip", destinationClip);
     dumpVal(out, "needsFiltering", needsFiltering);
 
     out.append("\n   ");
@@ -43,7 +50,8 @@
     dumpVal(out, "colorMode", toString(colorMode), colorMode);
     dumpVal(out, "renderIntent", toString(renderIntent), renderIntent);
     dumpVal(out, "dataspace", toString(dataspace), dataspace);
-    dumpVal(out, "colorTransform", colorTransform);
+    dumpVal(out, "colorTransformMatrix", colorTransformMatrix);
+    dumpVal(out, "target dataspace", toString(targetDataspace), targetDataspace);
 
     out.append("\n");
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 0afcc97..1faf775 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -15,17 +15,23 @@
  */
 
 #include <android-base/stringprintf.h>
-#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Layer.h>
+#include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/Output.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "DisplayHardware/HWComposer.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android::compositionengine {
 
 OutputLayer::~OutputLayer() = default;
@@ -44,56 +50,24 @@
 
 } // namespace
 
-std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
-        const CompositionEngine& compositionEngine, std::optional<DisplayId> displayId,
-        const compositionengine::Output& output, std::shared_ptr<compositionengine::Layer> layer,
-        sp<compositionengine::LayerFE> layerFE) {
-    auto result = std::make_unique<OutputLayer>(output, layer, layerFE);
-    result->initialize(compositionEngine, displayId);
-    return result;
+std::unique_ptr<OutputLayer> createOutputLayer(const compositionengine::Output& output,
+                                               const sp<compositionengine::LayerFE>& layerFE) {
+    return createOutputLayerTemplated<OutputLayer>(output, layerFE);
 }
 
-OutputLayer::OutputLayer(const Output& output, std::shared_ptr<Layer> layer, sp<LayerFE> layerFE)
-      : mOutput(output), mLayer(layer), mLayerFE(layerFE) {}
-
 OutputLayer::~OutputLayer() = default;
 
-void OutputLayer::initialize(const CompositionEngine& compositionEngine,
-                             std::optional<DisplayId> displayId) {
-    if (!displayId) {
-        return;
+void OutputLayer::setHwcLayer(std::shared_ptr<HWC2::Layer> hwcLayer) {
+    auto& state = editState();
+    if (hwcLayer) {
+        state.hwc.emplace(std::move(hwcLayer));
+    } else {
+        state.hwc.reset();
     }
-
-    auto& hwc = compositionEngine.getHwComposer();
-
-    mState.hwc.emplace(std::shared_ptr<HWC2::Layer>(hwc.createLayer(*displayId),
-                                                    [&hwc, displayId](HWC2::Layer* layer) {
-                                                        hwc.destroyLayer(*displayId, layer);
-                                                    }));
-}
-
-const compositionengine::Output& OutputLayer::getOutput() const {
-    return mOutput;
-}
-
-compositionengine::Layer& OutputLayer::getLayer() const {
-    return *mLayer;
-}
-
-compositionengine::LayerFE& OutputLayer::getLayerFE() const {
-    return *mLayerFE;
-}
-
-const OutputLayerCompositionState& OutputLayer::getState() const {
-    return mState;
-}
-
-OutputLayerCompositionState& OutputLayer::editState() {
-    return mState;
 }
 
 Rect OutputLayer::calculateInitialCrop() const {
-    const auto& layerState = mLayer->getState().frontEnd;
+    const auto& layerState = *getLayerFE().getCompositionState();
 
     // apply the projection's clipping to the window crop in
     // layerstack space, and convert-back to layer space.
@@ -101,9 +75,9 @@
     // pixels in the buffer.
 
     FloatRect activeCropFloat =
-            reduce(layerState.geomLayerBounds, layerState.geomActiveTransparentRegion);
+            reduce(layerState.geomLayerBounds, layerState.transparentRegionHint);
 
-    const Rect& viewport = mOutput.getState().viewport;
+    const Rect& viewport = getOutput().getState().viewport;
     const ui::Transform& layerTransform = layerState.geomLayerTransform;
     const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
     // Transform to screen space.
@@ -126,8 +100,8 @@
 }
 
 FloatRect OutputLayer::calculateOutputSourceCrop() const {
-    const auto& layerState = mLayer->getState().frontEnd;
-    const auto& outputState = mOutput.getState();
+    const auto& layerState = *getLayerFE().getCompositionState();
+    const auto& outputState = getOutput().getState();
 
     if (!layerState.geomUsesSourceCrop) {
         return {};
@@ -175,9 +149,9 @@
         // a modification of the axes of rotation. To account for this we
         // need to reorient the inverse rotation in terms of the current
         // axes of rotation.
-        bool is_h_flipped = (invTransform & HAL_TRANSFORM_FLIP_H) != 0;
-        bool is_v_flipped = (invTransform & HAL_TRANSFORM_FLIP_V) != 0;
-        if (is_h_flipped == is_v_flipped) {
+        bool isHFlipped = (invTransform & HAL_TRANSFORM_FLIP_H) != 0;
+        bool isVFlipped = (invTransform & HAL_TRANSFORM_FLIP_V) != 0;
+        if (isHFlipped == isVFlipped) {
             invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H;
         }
         std::swap(winWidth, winHeight);
@@ -186,29 +160,29 @@
             activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight());
 
     // below, crop is intersected with winCrop expressed in crop's coordinate space
-    float xScale = crop.getWidth() / float(winWidth);
-    float yScale = crop.getHeight() / float(winHeight);
+    const float xScale = crop.getWidth() / float(winWidth);
+    const float yScale = crop.getHeight() / float(winHeight);
 
-    float insetL = winCrop.left * xScale;
-    float insetT = winCrop.top * yScale;
-    float insetR = (winWidth - winCrop.right) * xScale;
-    float insetB = (winHeight - winCrop.bottom) * yScale;
+    const float insetLeft = winCrop.left * xScale;
+    const float insetTop = winCrop.top * yScale;
+    const float insetRight = (winWidth - winCrop.right) * xScale;
+    const float insetBottom = (winHeight - winCrop.bottom) * yScale;
 
-    crop.left += insetL;
-    crop.top += insetT;
-    crop.right -= insetR;
-    crop.bottom -= insetB;
+    crop.left += insetLeft;
+    crop.top += insetTop;
+    crop.right -= insetRight;
+    crop.bottom -= insetBottom;
 
     return crop;
 }
 
 Rect OutputLayer::calculateOutputDisplayFrame() const {
-    const auto& layerState = mLayer->getState().frontEnd;
-    const auto& outputState = mOutput.getState();
+    const auto& layerState = *getLayerFE().getCompositionState();
+    const auto& outputState = getOutput().getState();
 
     // apply the layer's transform, followed by the display's global transform
     // here we're guaranteed that the layer's transform preserves rects
-    Region activeTransparentRegion = layerState.geomActiveTransparentRegion;
+    Region activeTransparentRegion = layerState.transparentRegionHint;
     const ui::Transform& layerTransform = layerState.geomLayerTransform;
     const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
     const Rect& bufferSize = layerState.geomBufferSize;
@@ -249,9 +223,10 @@
     return displayTransform.transform(frame);
 }
 
-uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const {
-    const auto& layerState = mLayer->getState().frontEnd;
-    const auto& outputState = mOutput.getState();
+uint32_t OutputLayer::calculateOutputRelativeBufferTransform(
+        uint32_t internalDisplayRotationFlags) const {
+    const auto& layerState = *getLayerFE().getCompositionState();
+    const auto& outputState = getOutput().getState();
 
     /*
      * Transformations are applied in this order:
@@ -267,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;
@@ -287,101 +263,406 @@
 
     // this gives us only the "orientation" component of the transform
     return transform.getOrientation();
-} // namespace impl
+}
 
-void OutputLayer::updateCompositionState(bool includeGeometry) {
+void OutputLayer::updateCompositionState(
+        bool includeGeometry, bool forceClientComposition,
+        ui::Transform::RotationFlags internalDisplayRotationFlags) {
+    const auto* layerFEState = getLayerFE().getCompositionState();
+    if (!layerFEState) {
+        return;
+    }
+
+    const auto& outputState = getOutput().getState();
+    const auto& profile = *getOutput().getDisplayColorProfile();
+    auto& state = editState();
+
     if (includeGeometry) {
-        mState.displayFrame = calculateOutputDisplayFrame();
-        mState.sourceCrop = calculateOutputSourceCrop();
-        mState.bufferTransform =
-                static_cast<Hwc2::Transform>(calculateOutputRelativeBufferTransform());
+        // Clear the forceClientComposition flag before it is set for any
+        // reason. Note that since it can be set by some checks below when
+        // updating the geometry state, we only clear it when updating the
+        // geometry since those conditions for forcing client composition won't
+        // go away otherwise.
+        state.forceClientComposition = false;
 
-        if ((mLayer->getState().frontEnd.isSecure && !mOutput.getState().isSecure) ||
-            (mState.bufferTransform & ui::Transform::ROT_INVALID)) {
-            mState.forceClientComposition = true;
+        state.displayFrame = calculateOutputDisplayFrame();
+        state.sourceCrop = calculateOutputSourceCrop();
+        state.bufferTransform = static_cast<Hwc2::Transform>(
+                calculateOutputRelativeBufferTransform(internalDisplayRotationFlags));
+
+        if ((layerFEState->isSecure && !outputState.isSecure) ||
+            (state.bufferTransform & ui::Transform::ROT_INVALID)) {
+            state.forceClientComposition = true;
+        }
+    }
+
+    // Determine the output dependent dataspace for this layer. If it is
+    // colorspace agnostic, it just uses the dataspace chosen for the output to
+    // avoid the need for color conversion.
+    state.dataspace = layerFEState->isColorspaceAgnostic &&
+                    outputState.targetDataspace != ui::Dataspace::UNKNOWN
+            ? outputState.targetDataspace
+            : layerFEState->dataspace;
+
+    // These are evaluated every frame as they can potentially change at any
+    // time.
+    if (layerFEState->forceClientComposition || !profile.isDataspaceSupported(state.dataspace) ||
+        forceClientComposition) {
+        state.forceClientComposition = true;
+    }
+}
+
+void OutputLayer::writeStateToHWC(bool includeGeometry) {
+    const auto& state = getState();
+    // Skip doing this if there is no HWC interface
+    if (!state.hwc) {
+        return;
+    }
+
+    auto& hwcLayer = (*state.hwc).hwcLayer;
+    if (!hwcLayer) {
+        ALOGE("[%s] failed to write composition state to HWC -- no hwcLayer for output %s",
+              getLayerFE().getDebugName(), getOutput().getName().c_str());
+        return;
+    }
+
+    const auto* outputIndependentState = getLayerFE().getCompositionState();
+    if (!outputIndependentState) {
+        return;
+    }
+
+    auto requestedCompositionType = outputIndependentState->compositionType;
+
+    if (includeGeometry) {
+        writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType);
+        writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState);
+    }
+
+    writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
+    writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState);
+
+    writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType);
+
+    // Always set the layer color after setting the composition type.
+    writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
+}
+
+void OutputLayer::writeOutputDependentGeometryStateToHWC(
+        HWC2::Layer* hwcLayer, hal::Composition requestedCompositionType) {
+    const auto& outputDependentState = getState();
+
+    if (auto error = hwcLayer->setDisplayFrame(outputDependentState.displayFrame);
+        error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
+              getLayerFE().getDebugName(), outputDependentState.displayFrame.left,
+              outputDependentState.displayFrame.top, outputDependentState.displayFrame.right,
+              outputDependentState.displayFrame.bottom, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setSourceCrop(outputDependentState.sourceCrop);
+        error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
+              "%s (%d)",
+              getLayerFE().getDebugName(), outputDependentState.sourceCrop.left,
+              outputDependentState.sourceCrop.top, outputDependentState.sourceCrop.right,
+              outputDependentState.sourceCrop.bottom, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setZOrder(outputDependentState.z); error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set Z %u: %s (%d)", getLayerFE().getDebugName(),
+              outputDependentState.z, to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    // Solid-color layers should always use an identity transform.
+    const auto bufferTransform = requestedCompositionType != hal::Composition::SOLID_COLOR
+            ? outputDependentState.bufferTransform
+            : static_cast<hal::Transform>(0);
+    if (auto error = hwcLayer->setTransform(static_cast<hal::Transform>(bufferTransform));
+        error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set transform %s: %s (%d)", getLayerFE().getDebugName(),
+              toString(outputDependentState.bufferTransform).c_str(), to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeOutputIndependentGeometryStateToHWC(
+        HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) {
+    if (auto error = hwcLayer->setBlendMode(outputIndependentState.blendMode);
+        error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set blend mode %s: %s (%d)", getLayerFE().getDebugName(),
+              toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha);
+        error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", getLayerFE().getDebugName(),
+              outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setInfo(static_cast<uint32_t>(outputIndependentState.type),
+                                       static_cast<uint32_t>(outputIndependentState.appId));
+        error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set info %s (%d)", getLayerFE().getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    for (const auto& [name, entry] : outputIndependentState.metadata) {
+        if (auto error = hwcLayer->setLayerGenericMetadata(name, entry.mandatory, entry.value);
+            error != hal::Error::NONE) {
+            ALOGE("[%s] Failed to set generic metadata %s %s (%d)", getLayerFE().getDebugName(),
+                  name.c_str(), to_string(error).c_str(), static_cast<int32_t>(error));
         }
     }
 }
 
-void OutputLayer::writeStateToHWC(bool includeGeometry) const {
+void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) {
+    const auto& outputDependentState = getState();
+
+    // TODO(lpique): b/121291683 outputSpaceVisibleRegion is output-dependent geometry
+    // state and should not change every frame.
+    if (auto error = hwcLayer->setVisibleRegion(outputDependentState.outputSpaceVisibleRegion);
+        error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set visible region: %s (%d)", getLayerFE().getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+        outputDependentState.outputSpaceVisibleRegion.dump(LOG_TAG);
+    }
+
+    if (auto error = hwcLayer->setDataspace(outputDependentState.dataspace);
+        error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(),
+              outputDependentState.dataspace, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
+        HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) {
+    switch (auto error = hwcLayer->setColorTransform(outputIndependentState.colorTransform)) {
+        case hal::Error::NONE:
+            break;
+        case hal::Error::UNSUPPORTED:
+            editState().forceClientComposition = true;
+            break;
+        default:
+            ALOGE("[%s] Failed to set color transform: %s (%d)", getLayerFE().getDebugName(),
+                  to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setSurfaceDamage(outputIndependentState.surfaceDamage);
+        error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set surface damage: %s (%d)", getLayerFE().getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+        outputIndependentState.surfaceDamage.dump(LOG_TAG);
+    }
+
+    // Content-specific per-frame state
+    switch (outputIndependentState.compositionType) {
+        case hal::Composition::SOLID_COLOR:
+            // For compatibility, should be written AFTER the composition type.
+            break;
+        case hal::Composition::SIDEBAND:
+            writeSidebandStateToHWC(hwcLayer, outputIndependentState);
+            break;
+        case hal::Composition::CURSOR:
+        case hal::Composition::DEVICE:
+            writeBufferStateToHWC(hwcLayer, outputIndependentState);
+            break;
+        case hal::Composition::INVALID:
+        case hal::Composition::CLIENT:
+            // Ignored
+            break;
+    }
+}
+
+void OutputLayer::writeSolidColorStateToHWC(HWC2::Layer* hwcLayer,
+                                            const LayerFECompositionState& outputIndependentState) {
+    if (outputIndependentState.compositionType != hal::Composition::SOLID_COLOR) {
+        return;
+    }
+
+    hal::Color color = {static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.r)),
+                        static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.g)),
+                        static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.b)),
+                        255};
+
+    if (auto error = hwcLayer->setColor(color); error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set color: %s (%d)", getLayerFE().getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeSidebandStateToHWC(HWC2::Layer* hwcLayer,
+                                          const LayerFECompositionState& outputIndependentState) {
+    if (auto error = hwcLayer->setSidebandStream(outputIndependentState.sidebandStream->handle());
+        error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", getLayerFE().getDebugName(),
+              outputIndependentState.sidebandStream->handle(), to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
+                                        const LayerFECompositionState& outputIndependentState) {
+    auto supportedPerFrameMetadata =
+            getOutput().getDisplayColorProfile()->getSupportedPerFrameMetadata();
+    if (auto error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata,
+                                                   outputIndependentState.hdrMetadata);
+        error != hal::Error::NONE && error != hal::Error::UNSUPPORTED) {
+        ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", getLayerFE().getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    uint32_t hwcSlot = 0;
+    sp<GraphicBuffer> hwcBuffer;
+    // We need access to the output-dependent state for the buffer cache there,
+    // though otherwise the buffer is not output-dependent.
+    editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot,
+                                                 outputIndependentState.buffer, &hwcSlot,
+                                                 &hwcBuffer);
+
+    if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, outputIndependentState.acquireFence);
+        error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(),
+              outputIndependentState.buffer->handle, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeCompositionTypeToHWC(HWC2::Layer* hwcLayer,
+                                            hal::Composition requestedCompositionType) {
+    auto& outputDependentState = editState();
+
+    // If we are forcing client composition, we need to tell the HWC
+    if (outputDependentState.forceClientComposition) {
+        requestedCompositionType = hal::Composition::CLIENT;
+    }
+
+    // Set the requested composition type with the HWC whenever it changes
+    if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType) {
+        outputDependentState.hwc->hwcCompositionType = requestedCompositionType;
+
+        if (auto error = hwcLayer->setCompositionType(requestedCompositionType);
+            error != hal::Error::NONE) {
+            ALOGE("[%s] Failed to set composition type %s: %s (%d)", getLayerFE().getDebugName(),
+                  toString(requestedCompositionType).c_str(), to_string(error).c_str(),
+                  static_cast<int32_t>(error));
+        }
+    }
+}
+
+void OutputLayer::writeCursorPositionToHWC() const {
     // Skip doing this if there is no HWC interface
-    if (!mState.hwc) {
-        return;
-    }
-
-    auto& hwcLayer = (*mState.hwc).hwcLayer;
+    auto hwcLayer = getHwcLayer();
     if (!hwcLayer) {
-        ALOGE("[%s] failed to write composition state to HWC -- no hwcLayer for output %s",
-              mLayerFE->getDebugName(), mOutput.getName().c_str());
         return;
     }
 
-    if (includeGeometry) {
-        // Output dependent state
-
-        if (auto error = hwcLayer->setDisplayFrame(mState.displayFrame);
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
-                  mLayerFE->getDebugName(), mState.displayFrame.left, mState.displayFrame.top,
-                  mState.displayFrame.right, mState.displayFrame.bottom, to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-
-        if (auto error = hwcLayer->setSourceCrop(mState.sourceCrop); error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
-                  "%s (%d)",
-                  mLayerFE->getDebugName(), mState.sourceCrop.left, mState.sourceCrop.top,
-                  mState.sourceCrop.right, mState.sourceCrop.bottom, to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-
-        if (auto error = hwcLayer->setZOrder(mState.z); error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set Z %u: %s (%d)", mLayerFE->getDebugName(), mState.z,
-                  to_string(error).c_str(), static_cast<int32_t>(error));
-        }
-
-        if (auto error =
-                    hwcLayer->setTransform(static_cast<HWC2::Transform>(mState.bufferTransform));
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set transform %s: %s (%d)", mLayerFE->getDebugName(),
-                  toString(mState.bufferTransform).c_str(), to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-
-        // Output independent state
-
-        const auto& outputIndependentState = mLayer->getState().frontEnd;
-
-        if (auto error = hwcLayer->setBlendMode(
-                    static_cast<HWC2::BlendMode>(outputIndependentState.blendMode));
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set blend mode %s: %s (%d)", mLayerFE->getDebugName(),
-                  toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-
-        if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha);
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", mLayerFE->getDebugName(),
-                  outputIndependentState.alpha, to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-
-        if (auto error =
-                    hwcLayer->setInfo(outputIndependentState.type, outputIndependentState.appId);
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set info %s (%d)", mLayerFE->getDebugName(),
-                  to_string(error).c_str(), static_cast<int32_t>(error));
-        }
+    const auto* layerFEState = getLayerFE().getCompositionState();
+    if (!layerFEState) {
+        return;
     }
+
+    const auto& outputState = getOutput().getState();
+
+    Rect frame = layerFEState->cursorFrame;
+    frame.intersect(outputState.viewport, &frame);
+    Rect position = outputState.transform.transform(frame);
+
+    if (auto error = hwcLayer->setCursorPosition(position.left, position.top);
+        error != hal::Error::NONE) {
+        ALOGE("[%s] Failed to set cursor position to (%d, %d): %s (%d)",
+              getLayerFE().getDebugName(), position.left, position.top, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+HWC2::Layer* OutputLayer::getHwcLayer() const {
+    const auto& state = getState();
+    return state.hwc ? state.hwc->hwcLayer.get() : nullptr;
+}
+
+bool OutputLayer::requiresClientComposition() const {
+    const auto& state = getState();
+    return !state.hwc || state.hwc->hwcCompositionType == hal::Composition::CLIENT;
+}
+
+bool OutputLayer::isHardwareCursor() const {
+    const auto& state = getState();
+    return state.hwc && state.hwc->hwcCompositionType == hal::Composition::CURSOR;
+}
+
+void OutputLayer::detectDisallowedCompositionTypeChange(hal::Composition from,
+                                                        hal::Composition to) const {
+    bool result = false;
+    switch (from) {
+        case hal::Composition::INVALID:
+        case hal::Composition::CLIENT:
+            result = false;
+            break;
+
+        case hal::Composition::DEVICE:
+        case hal::Composition::SOLID_COLOR:
+            result = (to == hal::Composition::CLIENT);
+            break;
+
+        case hal::Composition::CURSOR:
+        case hal::Composition::SIDEBAND:
+            result = (to == hal::Composition::CLIENT || to == hal::Composition::DEVICE);
+            break;
+    }
+
+    if (!result) {
+        ALOGE("[%s] Invalid device requested composition type change: %s (%d) --> %s (%d)",
+              getLayerFE().getDebugName(), toString(from).c_str(), static_cast<int>(from),
+              toString(to).c_str(), static_cast<int>(to));
+    }
+}
+
+void OutputLayer::applyDeviceCompositionTypeChange(hal::Composition compositionType) {
+    auto& state = editState();
+    LOG_FATAL_IF(!state.hwc);
+    auto& hwcState = *state.hwc;
+
+    detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType);
+
+    hwcState.hwcCompositionType = compositionType;
+}
+
+void OutputLayer::prepareForDeviceLayerRequests() {
+    auto& state = editState();
+    state.clearClientTarget = false;
+}
+
+void OutputLayer::applyDeviceLayerRequest(hal::LayerRequest request) {
+    auto& state = editState();
+    switch (request) {
+        case hal::LayerRequest::CLEAR_CLIENT_TARGET:
+            state.clearClientTarget = true;
+            break;
+
+        default:
+            ALOGE("[%s] Unknown device layer request %s (%d)", getLayerFE().getDebugName(),
+                  toString(request).c_str(), static_cast<int>(request));
+            break;
+    }
+}
+
+bool OutputLayer::needsFiltering() const {
+    const auto& state = getState();
+    const auto& displayFrame = state.displayFrame;
+    const auto& sourceCrop = state.sourceCrop;
+    return sourceCrop.getHeight() != displayFrame.getHeight() ||
+            sourceCrop.getWidth() != displayFrame.getWidth();
 }
 
 void OutputLayer::dump(std::string& out) const {
     using android::base::StringAppendF;
 
-    StringAppendF(&out, "  - Output Layer %p (Composition layer %p) (%s)\n", this, mLayer.get(),
-                  mLayerFE->getDebugName());
-    mState.dump(out);
+    StringAppendF(&out, "  - Output Layer %p(%s)\n", this, getLayerFE().getDebugName());
+    dumpState(out);
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index 861ea57..165e320 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -17,8 +17,15 @@
 #include <compositionengine/impl/DumpHelpers.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "DisplayHardware/HWC2.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android::compositionengine::impl {
 
 namespace {
@@ -42,11 +49,24 @@
     dumpVal(out, "visibleRegion", visibleRegion);
 
     out.append("      ");
+    dumpVal(out, "visibleNonTransparentRegion", visibleNonTransparentRegion);
+
+    out.append("      ");
+    dumpVal(out, "coveredRegion", coveredRegion);
+
+    out.append("      ");
+    dumpVal(out, "output visibleRegion", outputSpaceVisibleRegion);
+
+    out.append("      ");
+    dumpVal(out, "shadowRegion", shadowRegion);
+
+    out.append("      ");
     dumpVal(out, "forceClientComposition", forceClientComposition);
     dumpVal(out, "clearClientTarget", clearClientTarget);
     dumpVal(out, "displayFrame", displayFrame);
     dumpVal(out, "sourceCrop", sourceCrop);
     dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform);
+    dumpVal(out, "dataspace", toString(dataspace), dataspace);
     dumpVal(out, "z-index", z);
 
     if (hwc) {
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 3fcd9d1..2773fd3 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -23,17 +23,25 @@
 #include <compositionengine/DisplaySurface.h>
 #include <compositionengine/RenderSurfaceCreationArgs.h>
 #include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/RenderSurface.h>
+
 #include <log/log.h>
 #include <renderengine/RenderEngine.h>
-#include <sync/sync.h>
 #include <system/window.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/Rect.h>
 #include <utils/Trace.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "DisplayHardware/HWComposer.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android::compositionengine {
 
 RenderSurface::~RenderSurface() = default;
@@ -42,12 +50,13 @@
 
 std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
         const compositionengine::CompositionEngine& compositionEngine,
-        compositionengine::Display& display, compositionengine::RenderSurfaceCreationArgs&& args) {
-    return std::make_unique<RenderSurface>(compositionEngine, display, std::move(args));
+        compositionengine::Display& display,
+        const compositionengine::RenderSurfaceCreationArgs& args) {
+    return std::make_unique<RenderSurface>(compositionEngine, display, args);
 }
 
 RenderSurface::RenderSurface(const CompositionEngine& compositionEngine, Display& display,
-                             RenderSurfaceCreationArgs&& args)
+                             const RenderSurfaceCreationArgs& args)
       : mCompositionEngine(compositionEngine),
         mDisplay(display),
         mNativeWindow(args.nativeWindow),
@@ -85,7 +94,8 @@
 }
 
 void RenderSurface::setDisplaySize(const ui::Size& size) {
-    mDisplaySurface->resizeBuffers(size.width, size.height);
+    mDisplaySurface->resizeBuffers(static_cast<uint32_t>(size.width),
+                                   static_cast<uint32_t>(size.height));
     mSize = size;
 }
 
@@ -94,6 +104,10 @@
                                          static_cast<android_dataspace>(dataspace));
 }
 
+void RenderSurface::setBufferPixelFormat(ui::PixelFormat pixelFormat) {
+    native_window_set_buffers_format(mNativeWindow.get(), static_cast<int32_t>(pixelFormat));
+}
+
 void RenderSurface::setProtected(bool useProtected) {
     uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER;
     if (useProtected) {
@@ -110,32 +124,25 @@
     return mDisplaySurface->beginFrame(mustRecompose);
 }
 
-status_t RenderSurface::prepareFrame() {
-    auto& hwc = mCompositionEngine.getHwComposer();
-    const auto id = mDisplay.getId();
-    if (id) {
-        status_t error = hwc.prepare(*id, mDisplay);
-        if (error != NO_ERROR) {
-            return error;
-        }
-    }
-
+void RenderSurface::prepareFrame(bool usesClientComposition, bool usesDeviceComposition) {
     DisplaySurface::CompositionType compositionType;
-    const bool hasClient = hwc.hasClientComposition(id);
-    const bool hasDevice = hwc.hasDeviceComposition(id);
-    if (hasClient && hasDevice) {
+    if (usesClientComposition && usesDeviceComposition) {
         compositionType = DisplaySurface::COMPOSITION_MIXED;
-    } else if (hasClient) {
-        compositionType = DisplaySurface::COMPOSITION_GLES;
-    } else if (hasDevice) {
+    } else if (usesClientComposition) {
+        compositionType = DisplaySurface::COMPOSITION_GPU;
+    } else if (usesDeviceComposition) {
         compositionType = DisplaySurface::COMPOSITION_HWC;
     } else {
         // Nothing to do -- when turning the screen off we get a frame like
-        // this. Call it a HWC frame since we won't be doing any GLES work but
+        // this. Call it a HWC frame since we won't be doing any GPU work but
         // will do a prepare/set cycle.
         compositionType = DisplaySurface::COMPOSITION_HWC;
     }
-    return mDisplaySurface->prepareFrame(compositionType);
+
+    if (status_t result = mDisplaySurface->prepareFrame(compositionType); result != NO_ERROR) {
+        ALOGE("updateCompositionType failed for %s: %d (%s)", mDisplay.getName().c_str(), result,
+              strerror(-result));
+    }
 }
 
 sp<GraphicBuffer> RenderSurface::dequeueBuffer(base::unique_fd* bufferFence) {
@@ -162,11 +169,10 @@
     return mGraphicBuffer;
 }
 
-void RenderSurface::queueBuffer(base::unique_fd&& readyFence) {
-    auto& hwc = mCompositionEngine.getHwComposer();
-    const auto id = mDisplay.getId();
+void RenderSurface::queueBuffer(base::unique_fd readyFence) {
+    auto& state = mDisplay.getState();
 
-    if (hwc.hasClientComposition(id) || hwc.hasFlipClientTargetRequest(id)) {
+    if (state.usesClientComposition || state.flipClientTarget) {
         // hasFlipClientTargetRequest could return true even if we haven't
         // dequeued a buffer before. Try dequeueing one if we don't have a
         // buffer ready.
@@ -215,13 +221,6 @@
     mDisplaySurface->onFrameCommitted();
 }
 
-void RenderSurface::setViewportAndProjection() {
-    auto& renderEngine = mCompositionEngine.getRenderEngine();
-    Rect sourceCrop = Rect(mSize);
-    renderEngine.setViewportAndProjection(mSize.width, mSize.height, sourceCrop,
-                                          ui::Transform::ROT_0);
-}
-
 void RenderSurface::flip() {
     mPageFlipCount++;
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/CallOrderStateMachineHelper.h b/services/surfaceflinger/CompositionEngine/tests/CallOrderStateMachineHelper.h
new file mode 100644
index 0000000..2675dcf
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/CallOrderStateMachineHelper.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+/**
+ * CallOrderStateMachineHelper is a helper class for setting up a compile-time
+ * checked state machine that a sequence of calls is correct for completely
+ * setting up the state for some other type.
+ *
+ * Two examples where this could be used are with setting up a "Builder" flow
+ * for initializing an instance of some type, and writing tests where the state
+ * machine sets up expectations and preconditions, calls the function under
+ * test, and then evaluations postconditions.
+ *
+ * The purpose of this helper is to offload some of the boilerplate code to
+ * simplify the actual state classes, and is also a place to document how to
+ * go about setting up the state classes.
+ *
+ * To work at compile time, the idea is that each state is a unique C++ type,
+ * and the valid transitions between states are given by member functions on
+ * those types, with those functions returning a simple value type expressing
+ * the new state to use. Illegal state transitions become a compile error because
+ * a named member function does not exist.
+ *
+ * Example usage in a test:
+ *
+ *    A two step (+ terminator step) setup process can defined using:
+ *
+ *        class Step1 : public CallOrderStateMachineHelper<TestFixtureType, Step1> {
+ *           [[nodiscard]] auto firstMockCalledWith(int value1) {
+ *              // Set up an expectation or initial state using the fixture
+ *              EXPECT_CALL(getInstance->firstMock, FirstCall(value1));
+ *              return nextState<Step2>();
+ *           }
+ *        };
+ *
+ *        class Step2 : public CallOrderStateMachineHelper<TestFixtureType, Step2> {
+ *           [[nodiscard]] auto secondMockCalledWith(int value2) {
+ *              // Set up an expectation or initial state using the fixture
+ *              EXPECT_CALL(getInstance()->secondMock, SecondCall(value2));
+ *              return nextState<StepExecute>();
+ *           }
+ *        };
+ *
+ *        class StepExecute : public CallOrderStateMachineHelper<TestFixtureType, Step3> {
+ *           void execute() {
+ *              invokeFunctionUnderTest();
+ *           }
+ *        };
+ *
+ *    Note how the non-terminator steps return by value and use [[nodiscard]] to
+ *    enforce the setup flow. Only the terminator step returns void.
+ *
+ *    This can then be used in the tests with:
+ *
+ *        Step1::make(this).firstMockCalledWith(value1)
+ *                .secondMockCalledWith(value2)
+ *                .execute);
+ *
+ *    If the test fixture defines a `verify()` helper function which returns
+ *    `Step1::make(this)`, this can be simplified to:
+ *
+ *        verify().firstMockCalledWith(value1)
+ *                .secondMockCalledWith(value2)
+ *                .execute();
+ *
+ *    This is equivalent to the following calls made by the text function:
+ *
+ *        EXPECT_CALL(firstMock, FirstCall(value1));
+ *        EXPECT_CALL(secondMock, SecondCall(value2));
+ *        invokeFunctionUnderTest();
+ */
+template <typename InstanceType, typename CurrentStateType>
+class CallOrderStateMachineHelper {
+public:
+    CallOrderStateMachineHelper() = default;
+
+    // Disallow copying
+    CallOrderStateMachineHelper(const CallOrderStateMachineHelper&) = delete;
+    CallOrderStateMachineHelper& operator=(const CallOrderStateMachineHelper&) = delete;
+
+    // Moving is intended use case.
+    CallOrderStateMachineHelper(CallOrderStateMachineHelper&&) = default;
+    CallOrderStateMachineHelper& operator=(CallOrderStateMachineHelper&&) = default;
+
+    // Using a static "Make" function means the CurrentStateType classes do not
+    // need anything other than a default no-argument constructor.
+    static CurrentStateType make(InstanceType* instance) {
+        auto helper = CurrentStateType();
+        helper.mInstance = instance;
+        return helper;
+    }
+
+    // Each non-terminal state function
+    template <typename NextStateType>
+    auto nextState() {
+        // Note: Further operations on the current state become undefined
+        // operations as the instance pointer is moved to the next state type.
+        // But that doesn't stop someone from storing an intermediate state
+        // instance as a local and possibly calling one than one member function
+        // on it. By swapping with nullptr, we at least can try to catch this
+        // this at runtime.
+        InstanceType* instance = nullptr;
+        std::swap(instance, mInstance);
+        return NextStateType::make(instance);
+    }
+
+    InstanceType* getInstance() const { return mInstance; }
+
+private:
+    InstanceType* mInstance;
+};
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 3766f27..d889d74 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -14,27 +14,42 @@
  * limitations under the License.
  */
 
+#include <compositionengine/CompositionRefreshArgs.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/impl/CompositionEngine.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/Output.h>
+#include <compositionengine/mock/OutputLayer.h>
 #include <gtest/gtest.h>
 #include <renderengine/mock/RenderEngine.h>
 
 #include "MockHWComposer.h"
+#include "TimeStats/TimeStats.h"
 
 namespace android::compositionengine {
 namespace {
 
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Ref;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::SaveArg;
 using ::testing::StrictMock;
 
-class CompositionEngineTest : public testing::Test {
-public:
-    ~CompositionEngineTest() override;
-    mock::HWComposer* mHwc = new StrictMock<mock::HWComposer>();
+struct CompositionEngineTest : public testing::Test {
+    android::mock::HWComposer* mHwc = new StrictMock<android::mock::HWComposer>();
     renderengine::mock::RenderEngine* mRenderEngine =
             new StrictMock<renderengine::mock::RenderEngine>();
-    impl::CompositionEngine mEngine;
-};
+    std::shared_ptr<TimeStats> mTimeStats;
 
-CompositionEngineTest::~CompositionEngineTest() = default;
+    impl::CompositionEngine mEngine;
+    CompositionRefreshArgs mRefreshArgs;
+
+    std::shared_ptr<mock::Output> mOutput1{std::make_shared<StrictMock<mock::Output>>()};
+    std::shared_ptr<mock::Output> mOutput2{std::make_shared<StrictMock<mock::Output>>()};
+    std::shared_ptr<mock::Output> mOutput3{std::make_shared<StrictMock<mock::Output>>()};
+};
 
 TEST_F(CompositionEngineTest, canInstantiateCompositionEngine) {
     auto engine = impl::createCompositionEngine();
@@ -53,5 +68,203 @@
     EXPECT_EQ(mRenderEngine, &mEngine.getRenderEngine());
 }
 
+TEST_F(CompositionEngineTest, canSetTimeStats) {
+    mEngine.setTimeStats(mTimeStats);
+
+    EXPECT_EQ(mTimeStats.get(), &mEngine.getTimeStats());
+}
+
+/*
+ * CompositionEngine::present
+ */
+
+struct CompositionEnginePresentTest : public CompositionEngineTest {
+    struct CompositionEnginePartialMock : public impl::CompositionEngine {
+        // These are the overridable functions CompositionEngine::present() may
+        // call, and have separate test coverage.
+        MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
+    };
+
+    StrictMock<CompositionEnginePartialMock> mEngine;
+};
+
+TEST_F(CompositionEnginePresentTest, worksWithEmptyRequest) {
+    // present() always calls preComposition()
+    EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs)));
+
+    mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEnginePresentTest, worksAsExpected) {
+    // Expect calls to in a certain sequence
+    InSequence seq;
+
+    // present() always calls preComposition()
+    EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs)));
+
+    // The first step in presenting is to make sure all outputs are prepared.
+    EXPECT_CALL(*mOutput1, prepare(Ref(mRefreshArgs), _));
+    EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _));
+    EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _));
+
+    // The next step in presenting is to make sure all outputs have the latest
+    // state from the front-end (SurfaceFlinger).
+    EXPECT_CALL(*mOutput1, updateLayerStateFromFE(Ref(mRefreshArgs)));
+    EXPECT_CALL(*mOutput2, updateLayerStateFromFE(Ref(mRefreshArgs)));
+    EXPECT_CALL(*mOutput3, updateLayerStateFromFE(Ref(mRefreshArgs)));
+
+    // The last step is to actually present each output.
+    EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs)));
+    EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs)));
+    EXPECT_CALL(*mOutput3, present(Ref(mRefreshArgs)));
+
+    mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
+    mEngine.present(mRefreshArgs);
+}
+
+/*
+ * CompositionEngine::updateCursorAsync
+ */
+
+struct CompositionEngineUpdateCursorAsyncTest : public CompositionEngineTest {
+public:
+    struct Layer {
+        Layer() { EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE)); }
+
+        StrictMock<mock::OutputLayer> outputLayer;
+        StrictMock<mock::LayerFE> layerFE;
+        LayerFECompositionState layerFEState;
+    };
+
+    CompositionEngineUpdateCursorAsyncTest() {
+        EXPECT_CALL(*mOutput1, getOutputLayerCount()).WillRepeatedly(Return(0u));
+        EXPECT_CALL(*mOutput1, getOutputLayerOrderedByZByIndex(_)).Times(0);
+
+        EXPECT_CALL(*mOutput2, getOutputLayerCount()).WillRepeatedly(Return(1u));
+        EXPECT_CALL(*mOutput2, getOutputLayerOrderedByZByIndex(0))
+                .WillRepeatedly(Return(&mOutput2Layer1.outputLayer));
+
+        EXPECT_CALL(*mOutput3, getOutputLayerCount()).WillRepeatedly(Return(2u));
+        EXPECT_CALL(*mOutput3, getOutputLayerOrderedByZByIndex(0))
+                .WillRepeatedly(Return(&mOutput3Layer1.outputLayer));
+        EXPECT_CALL(*mOutput3, getOutputLayerOrderedByZByIndex(1))
+                .WillRepeatedly(Return(&mOutput3Layer2.outputLayer));
+    }
+
+    Layer mOutput2Layer1;
+    Layer mOutput3Layer1;
+    Layer mOutput3Layer2;
+};
+
+TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesNoOutputs) {
+    mEngine.updateCursorAsync(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesNoLayersBeingCursorLayers) {
+    EXPECT_CALL(mOutput3Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput3Layer2.outputLayer, isHardwareCursor()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput2Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(false));
+
+    mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
+
+    mEngine.updateCursorAsync(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesMultipleLayersBeingCursorLayers) {
+    {
+        InSequence seq;
+        EXPECT_CALL(mOutput2Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
+        EXPECT_CALL(mOutput2Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+        EXPECT_CALL(mOutput2Layer1.outputLayer, writeCursorPositionToHWC());
+    }
+
+    {
+        InSequence seq;
+        EXPECT_CALL(mOutput3Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
+        EXPECT_CALL(mOutput3Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+        EXPECT_CALL(mOutput3Layer1.outputLayer, writeCursorPositionToHWC());
+    }
+
+    {
+        InSequence seq;
+        EXPECT_CALL(mOutput3Layer2.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
+        EXPECT_CALL(mOutput3Layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+        EXPECT_CALL(mOutput3Layer2.outputLayer, writeCursorPositionToHWC());
+    }
+
+    mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
+
+    mEngine.updateCursorAsync(mRefreshArgs);
+}
+
+/*
+ * CompositionEngine::preComposition
+ */
+
+struct CompositionTestPreComposition : public CompositionEngineTest {
+    sp<StrictMock<mock::LayerFE>> mLayer1FE{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> mLayer2FE{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> mLayer3FE{new StrictMock<mock::LayerFE>()};
+};
+
+TEST_F(CompositionTestPreComposition, preCompositionSetsFrameTimestamp) {
+    const nsecs_t before = systemTime(SYSTEM_TIME_MONOTONIC);
+    mEngine.preComposition(mRefreshArgs);
+    const nsecs_t after = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    // The frame timestamp should be between the before and after timestamps
+    EXPECT_GE(mEngine.getLastFrameRefreshTimestamp(), before);
+    EXPECT_LE(mEngine.getLastFrameRefreshTimestamp(), after);
+}
+
+TEST_F(CompositionTestPreComposition, preCompositionInvokesLayerPreCompositionWithFrameTimestamp) {
+    nsecs_t ts1 = 0;
+    nsecs_t ts2 = 0;
+    nsecs_t ts3 = 0;
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts1), Return(false)));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
+
+    mRefreshArgs.outputs = {mOutput1};
+    mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
+
+    mEngine.preComposition(mRefreshArgs);
+
+    // Each of the onPreComposition calls should used the same refresh timestamp
+    EXPECT_EQ(ts1, mEngine.getLastFrameRefreshTimestamp());
+    EXPECT_EQ(ts2, mEngine.getLastFrameRefreshTimestamp());
+    EXPECT_EQ(ts3, mEngine.getLastFrameRefreshTimestamp());
+}
+
+TEST_F(CompositionTestPreComposition, preCompositionDefaultsToNoUpdateNeeded) {
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
+
+    mEngine.setNeedsAnotherUpdateForTest(true);
+
+    mRefreshArgs.outputs = {mOutput1};
+    mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
+
+    mEngine.preComposition(mRefreshArgs);
+
+    // The call should have cleared the needsAnotherUpdate flag
+    EXPECT_FALSE(mEngine.needsAnotherUpdate());
+}
+
+TEST_F(CompositionTestPreComposition,
+       preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) {
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(true));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
+
+    mRefreshArgs.outputs = {mOutput1};
+    mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
+
+    mEngine.preComposition(mRefreshArgs);
+
+    EXPECT_TRUE(mEngine.needsAnotherUpdate());
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
index 9215884..21b9aa9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -19,35 +19,6 @@
 #include <compositionengine/mock/CompositionEngine.h>
 #include <gtest/gtest.h>
 
-namespace android::hardware::graphics::common::V1_1 {
-
-// Note: These operator overloads need to be defined in the same namespace as
-// the values they print.
-
-std::ostream& operator<<(std::ostream& os, const RenderIntent& value) {
-    return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
-              << ")";
-}
-
-} // namespace android::hardware::graphics::common::V1_1
-
-namespace android::hardware::graphics::common::V1_2 {
-
-// Note: These operator overloads need to be defined in the same namespace as
-// the values they print.
-
-std::ostream& operator<<(std::ostream& os, const Dataspace& value) {
-    return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
-              << ")";
-}
-
-std::ostream& operator<<(std::ostream& os, const ColorMode& value) {
-    return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
-              << ")";
-}
-
-} // namespace android::hardware::graphics::common::V1_2
-
 namespace android::compositionengine {
 namespace {
 
@@ -638,5 +609,66 @@
     checkGetBestColorMode(profile, expectedResults);
 }
 
+/*
+ * RenderSurface::isDataspaceSupported()
+ */
+
+TEST_F(DisplayColorProfileTest, isDataspaceSupportedWorksForProfileWithNoHdrSupport) {
+    auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::UNKNOWN));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::V0_SRGB));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_PQ));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_PQ));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_HLG));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG));
+}
+
+TEST_F(DisplayColorProfileTest, isDataspaceSupportedWorksForProfileWithHdr10Support) {
+    auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::UNKNOWN));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::V0_SRGB));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_PQ));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_PQ));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_HLG));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG));
+}
+
+TEST_F(DisplayColorProfileTest, isDataspaceSupportedWorksForProfileWithHlgSupport) {
+    auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::UNKNOWN));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::V0_SRGB));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_PQ));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_PQ));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_HLG));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG));
+}
+
+/*
+ * RenderSurface::getTargetDataspace()
+ */
+
+TEST_F(DisplayColorProfileTest, getTargetDataspaceWorks) {
+    auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+    // For a non-HDR colorspace with no colorSpaceAgnosticDataspace override,
+    // the input dataspace should be returned.
+    EXPECT_EQ(Dataspace::DISPLAY_P3,
+              profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3,
+                                         Dataspace::UNKNOWN));
+
+    // If colorSpaceAgnosticDataspace is set, its value should be returned
+    EXPECT_EQ(Dataspace::V0_SRGB,
+              profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3,
+                                         Dataspace::V0_SRGB));
+
+    // For an HDR colorspace, Dataspace::UNKNOWN should be returned.
+    EXPECT_EQ(Dataspace::UNKNOWN,
+              profile.getTargetDataspace(ColorMode::BT2100_PQ, Dataspace::BT2020_PQ,
+                                         Dataspace::UNKNOWN));
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 33444a5..09f37fb 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -21,140 +21,431 @@
 #include <compositionengine/DisplaySurface.h>
 #include <compositionengine/RenderSurfaceCreationArgs.h>
 #include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/RenderSurface.h>
 #include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/NativeWindow.h>
+#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>
 
+#include "MockHWC2.h"
 #include "MockHWComposer.h"
+#include "MockPowerAdvisor.h"
 
 namespace android::compositionengine {
 namespace {
 
+namespace hal = android::hardware::graphics::composer::hal;
+
+using testing::_;
+using testing::DoAll;
+using testing::Eq;
+using testing::InSequence;
+using testing::NiceMock;
+using testing::Pointee;
+using testing::Ref;
 using testing::Return;
 using testing::ReturnRef;
+using testing::Sequence;
+using testing::SetArgPointee;
 using testing::StrictMock;
 
 constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+constexpr DisplayId VIRTUAL_DISPLAY_ID = DisplayId{43};
+constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
+constexpr int32_t DEFAULT_LAYER_STACK = 123;
 
-class DisplayTest : public testing::Test {
-public:
-    ~DisplayTest() override = default;
+struct Layer {
+    Layer() {
+        EXPECT_CALL(*outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE));
+        EXPECT_CALL(*outputLayer, getHwcLayer()).WillRepeatedly(Return(&hwc2Layer));
+    }
 
-    StrictMock<android::mock::HWComposer> mHwComposer;
-    StrictMock<mock::CompositionEngine> mCompositionEngine;
-    sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
-    impl::Display mDisplay{mCompositionEngine,
-                           DisplayCreationArgsBuilder().setDisplayId(DEFAULT_DISPLAY_ID).build()};
+    sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
+    StrictMock<mock::OutputLayer>* outputLayer = new StrictMock<mock::OutputLayer>();
+    StrictMock<HWC2::mock::Layer> hwc2Layer;
 };
 
-/* ------------------------------------------------------------------------
+struct LayerNoHWC2Layer {
+    LayerNoHWC2Layer() {
+        EXPECT_CALL(*outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE));
+        EXPECT_CALL(*outputLayer, getHwcLayer()).WillRepeatedly(Return(nullptr));
+    }
+
+    sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
+    StrictMock<mock::OutputLayer>* outputLayer = new StrictMock<mock::OutputLayer>();
+};
+
+struct DisplayTestCommon : public testing::Test {
+    // Uses the full implementation of a display
+    class FullImplDisplay : public impl::Display {
+    public:
+        using impl::Display::injectOutputLayerForTest;
+        virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+
+        using impl::Display::maybeAllocateDisplayIdForVirtualDisplay;
+    };
+
+    // Uses a special implementation with key internal member functions set up
+    // as mock implementations, to allow for easier testing.
+    struct PartialMockDisplay : public impl::Display {
+        PartialMockDisplay(const compositionengine::CompositionEngine& compositionEngine)
+              : mCompositionEngine(compositionEngine) {}
+
+        // compositionengine::Output overrides
+        const OutputCompositionState& getState() const override { return mState; }
+        OutputCompositionState& editState() override { return mState; }
+
+        // compositionengine::impl::Output overrides
+        const CompositionEngine& getCompositionEngine() const override {
+            return mCompositionEngine;
+        };
+
+        // Mock implementation overrides
+        MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+        MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
+                           compositionengine::OutputLayer*(size_t));
+        MOCK_METHOD2(ensureOutputLayer,
+                     compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
+        MOCK_METHOD0(finalizePendingOutputLayers, void());
+        MOCK_METHOD0(clearOutputLayers, void());
+        MOCK_CONST_METHOD1(dumpState, void(std::string&));
+        MOCK_METHOD1(injectOutputLayerForTest, compositionengine::OutputLayer*(const sp<LayerFE>&));
+        MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
+        MOCK_CONST_METHOD0(anyLayersRequireClientComposition, bool());
+        MOCK_CONST_METHOD0(allLayersRequireClientComposition, bool());
+        MOCK_METHOD1(applyChangedTypesToLayers, void(const impl::Display::ChangedTypes&));
+        MOCK_METHOD1(applyDisplayRequests, void(const impl::Display::DisplayRequests&));
+        MOCK_METHOD1(applyLayerRequestsToLayers, void(const impl::Display::LayerRequests&));
+
+        const compositionengine::CompositionEngine& mCompositionEngine;
+        impl::OutputCompositionState mState;
+    };
+
+    static std::string getDisplayNameFromCurrentTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        return std::string("display for ") + test_info->test_case_name() + "." + test_info->name();
+    }
+
+    template <typename Display>
+    static std::shared_ptr<Display> createDisplay(
+            const compositionengine::CompositionEngine& compositionEngine,
+            compositionengine::DisplayCreationArgs args) {
+        args.name = getDisplayNameFromCurrentTest();
+        return impl::createDisplayTemplated<Display>(compositionEngine, args);
+    }
+
+    template <typename Display>
+    static std::shared_ptr<StrictMock<Display>> createPartialMockDisplay(
+            const compositionengine::CompositionEngine& compositionEngine,
+            compositionengine::DisplayCreationArgs args) {
+        args.name = getDisplayNameFromCurrentTest();
+        auto display = std::make_shared<StrictMock<Display>>(compositionEngine);
+
+        display->setConfiguration(args);
+
+        return display;
+    }
+
+    DisplayTestCommon() {
+        EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+        EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
+        EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    }
+
+    DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() {
+        return DisplayCreationArgsBuilder()
+                .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+                .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+                .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                .setIsSecure(true)
+                .setLayerStackId(DEFAULT_LAYER_STACK)
+                .setPowerAdvisor(&mPowerAdvisor)
+                .build();
+    }
+
+    DisplayCreationArgs getDisplayCreationArgsForNonHWCVirtualDisplay() {
+        return DisplayCreationArgsBuilder()
+                .setUseHwcVirtualDisplays(false)
+                .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+                .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                .setIsSecure(false)
+                .setLayerStackId(DEFAULT_LAYER_STACK)
+                .setPowerAdvisor(&mPowerAdvisor)
+                .build();
+    }
+
+    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>();
+};
+
+struct PartialMockDisplayTestCommon : public DisplayTestCommon {
+    using Display = DisplayTestCommon::PartialMockDisplay;
+    std::shared_ptr<Display> mDisplay =
+            createPartialMockDisplay<Display>(mCompositionEngine,
+                                              getDisplayCreationArgsForPhysicalHWCDisplay());
+};
+
+struct FullDisplayImplTestCommon : public DisplayTestCommon {
+    using Display = DisplayTestCommon::FullImplDisplay;
+    std::shared_ptr<Display> mDisplay =
+            createDisplay<Display>(mCompositionEngine,
+                                   getDisplayCreationArgsForPhysicalHWCDisplay());
+};
+
+struct DisplayWithLayersTestCommon : public FullDisplayImplTestCommon {
+    DisplayWithLayersTestCommon() {
+        mDisplay->injectOutputLayerForTest(
+                std::unique_ptr<compositionengine::OutputLayer>(mLayer1.outputLayer));
+        mDisplay->injectOutputLayerForTest(
+                std::unique_ptr<compositionengine::OutputLayer>(mLayer2.outputLayer));
+        mDisplay->injectOutputLayerForTest(
+                std::unique_ptr<compositionengine::OutputLayer>(mLayer3.outputLayer));
+    }
+
+    Layer mLayer1;
+    Layer mLayer2;
+    LayerNoHWC2Layer mLayer3;
+    StrictMock<HWC2::mock::Layer> hwc2LayerUnknown;
+    std::shared_ptr<Display> mDisplay =
+            createDisplay<Display>(mCompositionEngine,
+                                   getDisplayCreationArgsForPhysicalHWCDisplay());
+};
+
+/*
  * Basic construction
  */
 
-TEST_F(DisplayTest, canInstantiateDisplay) {
-    {
-        constexpr DisplayId display1 = DisplayId{123u};
-        auto display =
-                impl::createDisplay(mCompositionEngine,
-                                    DisplayCreationArgsBuilder().setDisplayId(display1).build());
-        EXPECT_FALSE(display->isSecure());
-        EXPECT_FALSE(display->isVirtual());
-        EXPECT_EQ(display1, display->getId());
-    }
+struct DisplayCreationTest : public DisplayTestCommon {
+    using Display = DisplayTestCommon::FullImplDisplay;
+};
 
-    {
-        constexpr DisplayId display2 = DisplayId{546u};
-        auto display = impl::createDisplay(mCompositionEngine,
-                                           DisplayCreationArgsBuilder()
-                                                   .setIsSecure(true)
-                                                   .setDisplayId(display2)
-                                                   .build());
-        EXPECT_TRUE(display->isSecure());
-        EXPECT_FALSE(display->isVirtual());
-        EXPECT_EQ(display2, display->getId());
-    }
-
-    {
-        constexpr DisplayId display3 = DisplayId{789u};
-        auto display = impl::createDisplay(mCompositionEngine,
-                                           DisplayCreationArgsBuilder()
-                                                   .setIsVirtual(true)
-                                                   .setDisplayId(display3)
-                                                   .build());
-        EXPECT_FALSE(display->isSecure());
-        EXPECT_TRUE(display->isVirtual());
-        EXPECT_EQ(display3, display->getId());
-    }
+TEST_F(DisplayCreationTest, createPhysicalInternalDisplay) {
+    auto display =
+            impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForPhysicalHWCDisplay());
+    EXPECT_TRUE(display->isSecure());
+    EXPECT_FALSE(display->isVirtual());
+    EXPECT_EQ(DEFAULT_DISPLAY_ID, display->getId());
 }
 
-/* ------------------------------------------------------------------------
+TEST_F(DisplayCreationTest, createNonHwcVirtualDisplay) {
+    auto display = impl::createDisplay(mCompositionEngine,
+                                       getDisplayCreationArgsForNonHWCVirtualDisplay());
+    EXPECT_FALSE(display->isSecure());
+    EXPECT_TRUE(display->isVirtual());
+    EXPECT_EQ(std::nullopt, display->getId());
+}
+
+/*
+ * Display::setConfiguration()
+ */
+
+using DisplaySetConfigurationTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplaySetConfigurationTest, configuresInternalSecurePhysicalDisplay) {
+    mDisplay->setConfiguration(
+            DisplayCreationArgsBuilder()
+                    .setUseHwcVirtualDisplays(true)
+                    .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+                    .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+                    .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                    .setIsSecure(true)
+                    .setLayerStackId(DEFAULT_LAYER_STACK)
+                    .setPowerAdvisor(&mPowerAdvisor)
+                    .setName(getDisplayNameFromCurrentTest())
+                    .build());
+
+    EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
+    EXPECT_TRUE(mDisplay->isSecure());
+    EXPECT_FALSE(mDisplay->isVirtual());
+    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+    EXPECT_TRUE(mDisplay->getState().layerStackInternal);
+    EXPECT_FALSE(mDisplay->isValid());
+}
+
+TEST_F(DisplaySetConfigurationTest, configuresExternalInsecurePhysicalDisplay) {
+    mDisplay->setConfiguration(
+            DisplayCreationArgsBuilder()
+                    .setUseHwcVirtualDisplays(true)
+                    .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::External})
+                    .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+                    .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                    .setIsSecure(false)
+                    .setLayerStackId(DEFAULT_LAYER_STACK)
+                    .setPowerAdvisor(&mPowerAdvisor)
+                    .setName(getDisplayNameFromCurrentTest())
+                    .build());
+
+    EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
+    EXPECT_FALSE(mDisplay->isSecure());
+    EXPECT_FALSE(mDisplay->isVirtual());
+    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
+    EXPECT_FALSE(mDisplay->isValid());
+}
+
+TEST_F(DisplaySetConfigurationTest, configuresHwcBackedVirtualDisplay) {
+    EXPECT_CALL(mHwComposer,
+                allocateVirtualDisplay(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH,
+                                       Pointee(Eq(static_cast<ui::PixelFormat>(
+                                               PIXEL_FORMAT_RGBA_8888)))))
+            .WillOnce(Return(VIRTUAL_DISPLAY_ID));
+
+    mDisplay->setConfiguration(
+            DisplayCreationArgsBuilder()
+                    .setUseHwcVirtualDisplays(true)
+                    .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+                    .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                    .setIsSecure(false)
+                    .setLayerStackId(DEFAULT_LAYER_STACK)
+                    .setPowerAdvisor(&mPowerAdvisor)
+                    .setName(getDisplayNameFromCurrentTest())
+                    .build());
+
+    EXPECT_EQ(VIRTUAL_DISPLAY_ID, mDisplay->getId());
+    EXPECT_FALSE(mDisplay->isSecure());
+    EXPECT_TRUE(mDisplay->isVirtual());
+    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
+    EXPECT_FALSE(mDisplay->isValid());
+}
+
+TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfHwcAllocationFails) {
+    EXPECT_CALL(mHwComposer,
+                allocateVirtualDisplay(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH,
+                                       Pointee(Eq(static_cast<ui::PixelFormat>(
+                                               PIXEL_FORMAT_RGBA_8888)))))
+            .WillOnce(Return(std::nullopt));
+
+    mDisplay->setConfiguration(
+            DisplayCreationArgsBuilder()
+                    .setUseHwcVirtualDisplays(true)
+                    .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+                    .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                    .setIsSecure(false)
+                    .setLayerStackId(DEFAULT_LAYER_STACK)
+                    .setPowerAdvisor(&mPowerAdvisor)
+                    .setName(getDisplayNameFromCurrentTest())
+                    .build());
+
+    EXPECT_EQ(std::nullopt, mDisplay->getId());
+    EXPECT_FALSE(mDisplay->isSecure());
+    EXPECT_TRUE(mDisplay->isVirtual());
+    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
+    EXPECT_FALSE(mDisplay->isValid());
+}
+
+TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfShouldNotUseHwc) {
+    mDisplay->setConfiguration(
+            DisplayCreationArgsBuilder()
+                    .setUseHwcVirtualDisplays(false)
+                    .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+                    .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                    .setIsSecure(false)
+                    .setLayerStackId(DEFAULT_LAYER_STACK)
+                    .setPowerAdvisor(&mPowerAdvisor)
+                    .setName(getDisplayNameFromCurrentTest())
+                    .build());
+
+    EXPECT_EQ(std::nullopt, mDisplay->getId());
+    EXPECT_FALSE(mDisplay->isSecure());
+    EXPECT_TRUE(mDisplay->isVirtual());
+    EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+    EXPECT_FALSE(mDisplay->getState().layerStackInternal);
+    EXPECT_FALSE(mDisplay->isValid());
+}
+
+/*
  * Display::disconnect()
  */
 
-TEST_F(DisplayTest, disconnectDisconnectsDisplay) {
-    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+using DisplayDisconnectTest = PartialMockDisplayTestCommon;
 
+TEST_F(DisplayDisconnectTest, disconnectsDisplay) {
     // The first call to disconnect will disconnect the display with the HWC and
     // set mHwcId to -1.
     EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1);
-    mDisplay.disconnect();
-    EXPECT_FALSE(mDisplay.getId());
+    mDisplay->disconnect();
+    EXPECT_FALSE(mDisplay->getId());
 
     // Subsequent calls will do nothing,
     EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(0);
-    mDisplay.disconnect();
-    EXPECT_FALSE(mDisplay.getId());
+    mDisplay->disconnect();
+    EXPECT_FALSE(mDisplay->getId());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Display::setColorTransform()
  */
 
-TEST_F(DisplayTest, setColorTransformSetsTransform) {
+using DisplaySetColorTransformTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplaySetColorTransformTest, setsTransform) {
+    // No change does nothing
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = std::nullopt;
+    mDisplay->setColorTransform(refreshArgs);
+
     // Identity matrix sets an identity state value
-    const mat4 identity;
+    const mat4 kIdentity;
 
-    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kIdentity)).Times(1);
 
-    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, identity)).Times(1);
-
-    mDisplay.setColorTransform(identity);
-
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mDisplay.getState().colorTransform);
+    refreshArgs.colorTransformMatrix = kIdentity;
+    mDisplay->setColorTransform(refreshArgs);
 
     // Non-identity matrix sets a non-identity state value
-    const mat4 nonIdentity = mat4() * 2;
+    const mat4 kNonIdentity = mat4() * 2;
 
-    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, nonIdentity)).Times(1);
+    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kNonIdentity)).Times(1);
 
-    mDisplay.setColorTransform(nonIdentity);
-
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mDisplay.getState().colorTransform);
+    refreshArgs.colorTransformMatrix = kNonIdentity;
+    mDisplay->setColorTransform(refreshArgs);
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Display::setColorMode()
  */
 
-TEST_F(DisplayTest, setColorModeSetsModeUnlessNoChange) {
-    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
-    mDisplay.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+using DisplaySetColorModeTest = PartialMockDisplayTestCommon;
 
-    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+TEST_F(DisplaySetColorModeTest, setsModeUnlessNoChange) {
+    using ColorProfile = Output::ColorProfile;
+
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+    mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mDisplay->setDisplayColorProfileForTest(std::unique_ptr<DisplayColorProfile>(colorProfile));
+
+    EXPECT_CALL(*colorProfile, getTargetDataspace(_, _, _))
+            .WillRepeatedly(Return(ui::Dataspace::UNKNOWN));
 
     // These values are expected to be the initial state.
-    ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
-    ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
-    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+    ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode);
+    ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent);
+    ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
 
     // Otherwise if the values are unchanged, nothing happens
-    mDisplay.setColorMode(ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
-                          ui::RenderIntent::COLORIMETRIC);
+    mDisplay->setColorProfile(ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+                                           ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN});
 
-    EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
-    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
-    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+    EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
 
     // Otherwise if the values are different, updates happen
     EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
@@ -163,47 +454,581 @@
                                    ui::RenderIntent::TONE_MAP_COLORIMETRIC))
             .Times(1);
 
-    mDisplay.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
-                          ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+    mDisplay->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                           ui::RenderIntent::TONE_MAP_COLORIMETRIC,
+                                           ui::Dataspace::UNKNOWN});
 
-    EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay.getState().colorMode);
-    EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay.getState().dataspace);
-    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay.getState().renderIntent);
+    EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay->getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay->getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay->getState().renderIntent);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
 }
 
-TEST_F(DisplayTest, setColorModeDoesNothingForVirtualDisplay) {
-    impl::Display virtualDisplay{mCompositionEngine,
-                                 DisplayCreationArgs{false, true, DEFAULT_DISPLAY_ID}};
+TEST_F(DisplaySetColorModeTest, doesNothingForVirtualDisplay) {
+    using ColorProfile = Output::ColorProfile;
 
-    virtualDisplay.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
-                                ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    std::shared_ptr<impl::Display> virtualDisplay = impl::createDisplay(mCompositionEngine, args);
 
-    EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay.getState().colorMode);
-    EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay.getState().dataspace);
-    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay.getState().renderIntent);
+    mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
+    virtualDisplay->setDisplayColorProfileForTest(
+            std::unique_ptr<DisplayColorProfile>(colorProfile));
+
+    EXPECT_CALL(*colorProfile,
+                getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                   ui::Dataspace::UNKNOWN))
+            .WillOnce(Return(ui::Dataspace::UNKNOWN));
+
+    virtualDisplay->setColorProfile(
+            ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                         ui::RenderIntent::TONE_MAP_COLORIMETRIC, ui::Dataspace::UNKNOWN});
+
+    EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay->getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay->getState().renderIntent);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().targetDataspace);
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Display::createDisplayColorProfile()
  */
 
-TEST_F(DisplayTest, createDisplayColorProfileSetsDisplayColorProfile) {
-    EXPECT_TRUE(mDisplay.getDisplayColorProfile() == nullptr);
-    mDisplay.createDisplayColorProfile(
+using DisplayCreateColorProfileTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplayCreateColorProfileTest, setsDisplayColorProfile) {
+    EXPECT_TRUE(mDisplay->getDisplayColorProfile() == nullptr);
+    mDisplay->createDisplayColorProfile(
             DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0,
                                             DisplayColorProfileCreationArgs::HwcColorModes()});
-    EXPECT_TRUE(mDisplay.getDisplayColorProfile() != nullptr);
+    EXPECT_TRUE(mDisplay->getDisplayColorProfile() != nullptr);
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Display::createRenderSurface()
  */
 
-TEST_F(DisplayTest, createRenderSurfaceSetsRenderSurface) {
+using DisplayCreateRenderSurfaceTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplayCreateRenderSurfaceTest, setsRenderSurface) {
     EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL)).WillRepeatedly(Return(NO_ERROR));
-    EXPECT_TRUE(mDisplay.getRenderSurface() == nullptr);
-    mDisplay.createRenderSurface(RenderSurfaceCreationArgs{640, 480, mNativeWindow, nullptr});
-    EXPECT_TRUE(mDisplay.getRenderSurface() != nullptr);
+    EXPECT_TRUE(mDisplay->getRenderSurface() == nullptr);
+    mDisplay->createRenderSurface(RenderSurfaceCreationArgs{640, 480, mNativeWindow, nullptr});
+    EXPECT_TRUE(mDisplay->getRenderSurface() != nullptr);
+}
+
+/*
+ * Display::createOutputLayer()
+ */
+
+using DisplayCreateOutputLayerTest = FullDisplayImplTestCommon;
+
+TEST_F(DisplayCreateOutputLayerTest, setsHwcLayer) {
+    sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
+    StrictMock<HWC2::mock::Layer> hwcLayer;
+
+    EXPECT_CALL(mHwComposer, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer));
+
+    auto outputLayer = mDisplay->createOutputLayer(layerFE);
+
+    EXPECT_EQ(&hwcLayer, outputLayer->getHwcLayer());
+
+    EXPECT_CALL(mHwComposer, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer));
+    outputLayer.reset();
+}
+
+/*
+ * Display::setReleasedLayers()
+ */
+
+using DisplaySetReleasedLayersTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplaySetReleasedLayersTest, doesNothingIfNotHwcDisplay) {
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
+
+    sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
+
+    {
+        Output::ReleasedLayers releasedLayers;
+        releasedLayers.emplace_back(layerXLayerFE);
+        nonHwcDisplay->setReleasedLayers(std::move(releasedLayers));
+    }
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.layersWithQueuedFrames.push_back(layerXLayerFE);
+
+    nonHwcDisplay->setReleasedLayers(refreshArgs);
+
+    const auto& releasedLayers = nonHwcDisplay->getReleasedLayersForTest();
+    ASSERT_EQ(1, releasedLayers.size());
+}
+
+TEST_F(DisplaySetReleasedLayersTest, doesNothingIfNoLayersWithQueuedFrames) {
+    sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
+
+    {
+        Output::ReleasedLayers releasedLayers;
+        releasedLayers.emplace_back(layerXLayerFE);
+        mDisplay->setReleasedLayers(std::move(releasedLayers));
+    }
+
+    CompositionRefreshArgs refreshArgs;
+    mDisplay->setReleasedLayers(refreshArgs);
+
+    const auto& releasedLayers = mDisplay->getReleasedLayersForTest();
+    ASSERT_EQ(1, releasedLayers.size());
+}
+
+TEST_F(DisplaySetReleasedLayersTest, setReleasedLayers) {
+    sp<mock::LayerFE> unknownLayer = new StrictMock<mock::LayerFE>();
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.layersWithQueuedFrames.push_back(mLayer1.layerFE);
+    refreshArgs.layersWithQueuedFrames.push_back(mLayer2.layerFE);
+    refreshArgs.layersWithQueuedFrames.push_back(unknownLayer);
+
+    mDisplay->setReleasedLayers(refreshArgs);
+
+    const auto& releasedLayers = mDisplay->getReleasedLayersForTest();
+    ASSERT_EQ(2, releasedLayers.size());
+    ASSERT_EQ(mLayer1.layerFE.get(), releasedLayers[0].promote().get());
+    ASSERT_EQ(mLayer2.layerFE.get(), releasedLayers[1].promote().get());
+}
+
+/*
+ * Display::chooseCompositionStrategy()
+ */
+
+using DisplayChooseCompositionStrategyTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfNotAHwcDisplay) {
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    std::shared_ptr<Display> nonHwcDisplay =
+            createPartialMockDisplay<Display>(mCompositionEngine, args);
+    EXPECT_FALSE(nonHwcDisplay->getId());
+
+    nonHwcDisplay->chooseCompositionStrategy();
+
+    auto& state = nonHwcDisplay->getState();
+    EXPECT_TRUE(state.usesClientComposition);
+    EXPECT_FALSE(state.usesDeviceComposition);
+}
+
+TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, false, _))
+            .WillOnce(Return(INVALID_OPERATION));
+
+    mDisplay->chooseCompositionStrategy();
+
+    auto& state = mDisplay->getState();
+    EXPECT_TRUE(state.usesClientComposition);
+    EXPECT_FALSE(state.usesDeviceComposition);
+}
+
+TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) {
+    // Since two calls are made to anyLayersRequireClientComposition with different return
+    // values, use a Sequence to control the matching so the values are returned in a known
+    // order.
+    Sequence s;
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
+            .InSequence(s)
+            .WillOnce(Return(true));
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
+            .InSequence(s)
+            .WillOnce(Return(false));
+
+    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+
+    mDisplay->chooseCompositionStrategy();
+
+    auto& state = mDisplay->getState();
+    EXPECT_FALSE(state.usesClientComposition);
+    EXPECT_TRUE(state.usesDeviceComposition);
+}
+
+TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) {
+    android::HWComposer::DeviceRequestedChanges changes{
+            {{nullptr, hal::Composition::CLIENT}},
+            hal::DisplayRequest::FLIP_CLIENT_TARGET,
+            {{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
+            {hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
+    };
+
+    // Since two calls are made to anyLayersRequireClientComposition with different return
+    // values, use a Sequence to control the matching so the values are returned in a known
+    // order.
+    Sequence s;
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
+            .InSequence(s)
+            .WillOnce(Return(true));
+    EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
+            .InSequence(s)
+            .WillOnce(Return(false));
+
+    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
+            .WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR)));
+    EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
+    EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
+    EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
+    EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+
+    mDisplay->chooseCompositionStrategy();
+
+    auto& state = mDisplay->getState();
+    EXPECT_FALSE(state.usesClientComposition);
+    EXPECT_TRUE(state.usesDeviceComposition);
+}
+
+/*
+ * Display::getSkipColorTransform()
+ */
+
+using DisplayGetSkipColorTransformTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayGetSkipColorTransformTest, checksCapabilityIfNonHwcDisplay) {
+    EXPECT_CALL(mHwComposer, hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM))
+            .WillOnce(Return(true));
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    auto nonHwcDisplay{impl::createDisplay(mCompositionEngine, args)};
+    EXPECT_TRUE(nonHwcDisplay->getSkipColorTransform());
+}
+
+TEST_F(DisplayGetSkipColorTransformTest, checksDisplayCapability) {
+    EXPECT_CALL(mHwComposer,
+                hasDisplayCapability(DEFAULT_DISPLAY_ID,
+                                     hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM))
+            .WillOnce(Return(true));
+    EXPECT_TRUE(mDisplay->getSkipColorTransform());
+}
+
+/*
+ * Display::anyLayersRequireClientComposition()
+ */
+
+using DisplayAnyLayersRequireClientCompositionTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayAnyLayersRequireClientCompositionTest, returnsFalse) {
+    EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3.outputLayer, requiresClientComposition()).WillOnce(Return(false));
+
+    EXPECT_FALSE(mDisplay->anyLayersRequireClientComposition());
+}
+
+TEST_F(DisplayAnyLayersRequireClientCompositionTest, returnsTrue) {
+    EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+    EXPECT_TRUE(mDisplay->anyLayersRequireClientComposition());
+}
+
+/*
+ * Display::allLayersRequireClientComposition()
+ */
+
+using DisplayAllLayersRequireClientCompositionTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayAllLayersRequireClientCompositionTest, returnsTrue) {
+    EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(true));
+    EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(true));
+    EXPECT_CALL(*mLayer3.outputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+    EXPECT_TRUE(mDisplay->allLayersRequireClientComposition());
+}
+
+TEST_F(DisplayAllLayersRequireClientCompositionTest, returnsFalse) {
+    EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(true));
+    EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(false));
+
+    EXPECT_FALSE(mDisplay->allLayersRequireClientComposition());
+}
+
+/*
+ * Display::applyChangedTypesToLayers()
+ */
+
+using DisplayApplyChangedTypesToLayersTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayApplyChangedTypesToLayersTest, takesEarlyOutIfNoChangedLayers) {
+    mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes());
+}
+
+TEST_F(DisplayApplyChangedTypesToLayersTest, appliesChanges) {
+    EXPECT_CALL(*mLayer1.outputLayer,
+                applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT))
+            .Times(1);
+    EXPECT_CALL(*mLayer2.outputLayer,
+                applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::DEVICE))
+            .Times(1);
+
+    mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes{
+            {&mLayer1.hwc2Layer, hal::Composition::CLIENT},
+            {&mLayer2.hwc2Layer, hal::Composition::DEVICE},
+            {&hwc2LayerUnknown, hal::Composition::SOLID_COLOR},
+    });
+}
+
+/*
+ * Display::applyDisplayRequests()
+ */
+
+using DisplayApplyDisplayRequestsTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayApplyDisplayRequestsTest, handlesNoRequests) {
+    mDisplay->applyDisplayRequests(static_cast<hal::DisplayRequest>(0));
+
+    auto& state = mDisplay->getState();
+    EXPECT_FALSE(state.flipClientTarget);
+}
+
+TEST_F(DisplayApplyDisplayRequestsTest, handlesFlipClientTarget) {
+    mDisplay->applyDisplayRequests(hal::DisplayRequest::FLIP_CLIENT_TARGET);
+
+    auto& state = mDisplay->getState();
+    EXPECT_TRUE(state.flipClientTarget);
+}
+
+TEST_F(DisplayApplyDisplayRequestsTest, handlesWriteClientTargetToOutput) {
+    mDisplay->applyDisplayRequests(hal::DisplayRequest::WRITE_CLIENT_TARGET_TO_OUTPUT);
+
+    auto& state = mDisplay->getState();
+    EXPECT_FALSE(state.flipClientTarget);
+}
+
+TEST_F(DisplayApplyDisplayRequestsTest, handlesAllRequestFlagsSet) {
+    mDisplay->applyDisplayRequests(static_cast<hal::DisplayRequest>(~0));
+
+    auto& state = mDisplay->getState();
+    EXPECT_TRUE(state.flipClientTarget);
+}
+
+/*
+ * Display::applyLayerRequestsToLayers()
+ */
+
+using DisplayApplyLayerRequestsToLayersTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayApplyLayerRequestsToLayersTest, preparesAllLayers) {
+    EXPECT_CALL(*mLayer1.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+    EXPECT_CALL(*mLayer2.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+    EXPECT_CALL(*mLayer3.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+
+    mDisplay->applyLayerRequestsToLayers(impl::Display::LayerRequests());
+}
+
+TEST_F(DisplayApplyLayerRequestsToLayersTest, appliesDeviceLayerRequests) {
+    EXPECT_CALL(*mLayer1.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+    EXPECT_CALL(*mLayer2.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+    EXPECT_CALL(*mLayer3.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+
+    EXPECT_CALL(*mLayer1.outputLayer,
+                applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest::CLEAR_CLIENT_TARGET))
+            .Times(1);
+
+    mDisplay->applyLayerRequestsToLayers(impl::Display::LayerRequests{
+            {&mLayer1.hwc2Layer, hal::LayerRequest::CLEAR_CLIENT_TARGET},
+            {&hwc2LayerUnknown, hal::LayerRequest::CLEAR_CLIENT_TARGET},
+    });
+}
+
+/*
+ * Display::presentAndGetFrameFences()
+ */
+
+using DisplayPresentAndGetFrameFencesTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayPresentAndGetFrameFencesTest, returnsNoFencesOnNonHwcDisplay) {
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    auto nonHwcDisplay{impl::createDisplay(mCompositionEngine, args)};
+
+    auto result = nonHwcDisplay->presentAndGetFrameFences();
+
+    ASSERT_TRUE(result.presentFence.get());
+    EXPECT_FALSE(result.presentFence->isValid());
+    EXPECT_EQ(0u, result.layerFences.size());
+}
+
+TEST_F(DisplayPresentAndGetFrameFencesTest, returnsPresentAndLayerFences) {
+    sp<Fence> presentFence = new Fence();
+    sp<Fence> layer1Fence = new Fence();
+    sp<Fence> layer2Fence = new Fence();
+
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
+    EXPECT_CALL(mHwComposer, getPresentFence(DEFAULT_DISPLAY_ID)).WillOnce(Return(presentFence));
+    EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer1.hwc2Layer))
+            .WillOnce(Return(layer1Fence));
+    EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer2.hwc2Layer))
+            .WillOnce(Return(layer2Fence));
+    EXPECT_CALL(mHwComposer, clearReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
+
+    auto result = mDisplay->presentAndGetFrameFences();
+
+    EXPECT_EQ(presentFence, result.presentFence);
+
+    EXPECT_EQ(2u, result.layerFences.size());
+    ASSERT_EQ(1, result.layerFences.count(&mLayer1.hwc2Layer));
+    EXPECT_EQ(layer1Fence, result.layerFences[&mLayer1.hwc2Layer]);
+    ASSERT_EQ(1, result.layerFences.count(&mLayer2.hwc2Layer));
+    EXPECT_EQ(layer2Fence, result.layerFences[&mLayer2.hwc2Layer]);
+}
+
+/*
+ * Display::setExpensiveRenderingExpected()
+ */
+
+using DisplaySetExpensiveRenderingExpectedTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplaySetExpensiveRenderingExpectedTest, forwardsToPowerAdvisor) {
+    EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, true)).Times(1);
+    mDisplay->setExpensiveRenderingExpected(true);
+
+    EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, false)).Times(1);
+    mDisplay->setExpensiveRenderingExpected(false);
+}
+
+/*
+ * Display::finishFrame()
+ */
+
+using DisplayFinishFrameTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayFinishFrameTest, doesNotSkipCompositionIfNotDirtyOnHwcDisplay) {
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+    // We expect no calls to queueBuffer if composition was skipped.
+    EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
+
+    // Expect a call to signal no expensive rendering since there is no client composition.
+    EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, false));
+
+    mDisplay->editState().isEnabled = true;
+    mDisplay->editState().usesClientComposition = false;
+    mDisplay->editState().viewport = Rect(0, 0, 1, 1);
+    mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.repaintEverything = false;
+
+    mDisplay->finishFrame(refreshArgs);
+}
+
+TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) {
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
+
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+    // We expect no calls to queueBuffer if composition was skipped.
+    EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0);
+
+    nonHwcDisplay->editState().isEnabled = true;
+    nonHwcDisplay->editState().usesClientComposition = false;
+    nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+    nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.repaintEverything = false;
+
+    nonHwcDisplay->finishFrame(refreshArgs);
+}
+
+TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
+
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+    // We expect a single call to queueBuffer when composition is not skipped.
+    EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
+
+    nonHwcDisplay->editState().isEnabled = true;
+    nonHwcDisplay->editState().usesClientComposition = false;
+    nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+    nonHwcDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.repaintEverything = false;
+
+    nonHwcDisplay->finishFrame(refreshArgs);
+}
+
+TEST_F(DisplayFinishFrameTest, performsCompositionIfRepaintEverything) {
+    auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+    std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
+
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+    // We expect a single call to queueBuffer when composition is not skipped.
+    EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
+
+    nonHwcDisplay->editState().isEnabled = true;
+    nonHwcDisplay->editState().usesClientComposition = false;
+    nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+    nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.repaintEverything = true;
+
+    nonHwcDisplay->finishFrame(refreshArgs);
+}
+
+/*
+ * Display functional tests
+ */
+
+struct DisplayFunctionalTest : public testing::Test {
+    class Display : public impl::Display {
+    public:
+        using impl::Display::injectOutputLayerForTest;
+        virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+    };
+
+    DisplayFunctionalTest() {
+        EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+        mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+
+    NiceMock<android::mock::HWComposer> mHwComposer;
+    NiceMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+    NiceMock<mock::CompositionEngine> mCompositionEngine;
+    sp<mock::NativeWindow> mNativeWindow = new NiceMock<mock::NativeWindow>();
+    sp<mock::DisplaySurface> mDisplaySurface = new NiceMock<mock::DisplaySurface>();
+    std::shared_ptr<Display> mDisplay = impl::createDisplayTemplated<
+            Display>(mCompositionEngine,
+                     DisplayCreationArgsBuilder()
+                             .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+                             .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+                             .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+                             .setIsSecure(true)
+                             .setLayerStackId(DEFAULT_LAYER_STACK)
+                             .setPowerAdvisor(&mPowerAdvisor)
+                             .build()
+
+    );
+    impl::RenderSurface* mRenderSurface =
+            new impl::RenderSurface{mCompositionEngine, *mDisplay,
+                                    RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
+                                                              DEFAULT_DISPLAY_HEIGHT, mNativeWindow,
+                                                              mDisplaySurface}};
+};
+
+TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) {
+    InSequence seq;
+
+    mDisplay->editState().isEnabled = true;
+
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_));
+    EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
+
+    mDisplay->postFramebuffer();
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
deleted file mode 100644
index 26115a3..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-
-#include <gtest/gtest.h>
-
-#include <compositionengine/LayerCreationArgs.h>
-#include <compositionengine/impl/Layer.h>
-#include <compositionengine/mock/CompositionEngine.h>
-#include <compositionengine/mock/LayerFE.h>
-
-namespace android::compositionengine {
-namespace {
-
-using testing::StrictMock;
-
-class LayerTest : public testing::Test {
-public:
-    ~LayerTest() override = default;
-
-    StrictMock<mock::CompositionEngine> mCompositionEngine;
-    sp<LayerFE> mLayerFE = new StrictMock<mock::LayerFE>();
-    impl::Layer mLayer{mCompositionEngine, LayerCreationArgs{mLayerFE}};
-};
-
-/* ------------------------------------------------------------------------
- * Basic construction
- */
-
-TEST_F(LayerTest, canInstantiateLayer) {}
-
-} // namespace
-} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp
index 8c10341..0baa79d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp
@@ -16,7 +16,7 @@
 
 #include "MockHWC2.h"
 
-namespace HWC2 {
+namespace android::HWC2 {
 
 // This will go away once HWC2::Layer is moved into the "backend" library
 Layer::~Layer() = default;
@@ -29,4 +29,4 @@
 Layer::~Layer() = default;
 
 } // namespace mock
-} // namespace HWC2
+} // namespace android::HWC2
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index 7fd6541..d21b97e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -20,44 +20,59 @@
 #include <ui/Fence.h>
 #include <ui/FloatRect.h>
 #include <ui/GraphicBuffer.h>
-#include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <ui/GraphicTypes.h>
 #include "DisplayHardware/HWC2.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
+namespace android {
 namespace HWC2 {
 namespace mock {
 
+namespace hal = android::hardware::graphics::composer::hal;
+
+using Error = hal::Error;
+
 class Layer : public HWC2::Layer {
 public:
     Layer();
     ~Layer() override;
 
-    MOCK_CONST_METHOD0(getId, hwc2_layer_t());
+    MOCK_CONST_METHOD0(getId, hal::HWLayerId());
 
     MOCK_METHOD2(setCursorPosition, Error(int32_t, int32_t));
     MOCK_METHOD3(setBuffer,
                  Error(uint32_t, const android::sp<android::GraphicBuffer>&,
                        const android::sp<android::Fence>&));
     MOCK_METHOD1(setSurfaceDamage, Error(const android::Region&));
-    MOCK_METHOD1(setBlendMode, Error(BlendMode));
-    MOCK_METHOD1(setColor, Error(hwc_color_t));
-    MOCK_METHOD1(setCompositionType, Error(Composition));
+    MOCK_METHOD1(setBlendMode, Error(hal::BlendMode));
+    MOCK_METHOD1(setColor, Error(hal::Color));
+    MOCK_METHOD1(setCompositionType, Error(hal::Composition));
     MOCK_METHOD1(setDataspace, Error(android::ui::Dataspace));
     MOCK_METHOD2(setPerFrameMetadata, Error(const int32_t, const android::HdrMetadata&));
     MOCK_METHOD1(setDisplayFrame, Error(const android::Rect&));
     MOCK_METHOD1(setPlaneAlpha, Error(float));
     MOCK_METHOD1(setSidebandStream, Error(const native_handle_t*));
     MOCK_METHOD1(setSourceCrop, Error(const android::FloatRect&));
-    MOCK_METHOD1(setTransform, Error(Transform));
+    MOCK_METHOD1(setTransform, Error(hal::Transform));
     MOCK_METHOD1(setVisibleRegion, Error(const android::Region&));
     MOCK_METHOD1(setZOrder, Error(uint32_t));
     MOCK_METHOD2(setInfo, Error(uint32_t, uint32_t));
 
     MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
+    MOCK_METHOD3(setLayerGenericMetadata,
+                 Error(const std::string&, bool, const std::vector<uint8_t>&));
 };
 
 } // namespace mock
 } // namespace HWC2
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 94349de..75a4fec 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -19,39 +19,48 @@
 #include <compositionengine/Output.h>
 #include <gmock/gmock.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "DisplayHardware/HWComposer.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android {
 namespace mock {
 
+namespace hal = android::hardware::graphics::composer::hal;
+
 class HWComposer : public android::HWComposer {
 public:
     HWComposer();
     ~HWComposer() override;
 
-    MOCK_METHOD2(registerCallback, void(HWC2::ComposerCallback*, int32_t));
+    MOCK_METHOD2(setConfiguration, void(HWC2::ComposerCallback*, int32_t));
     MOCK_CONST_METHOD3(getDisplayIdentificationData,
-                       bool(hwc2_display_t, uint8_t*, DisplayIdentificationData*));
-    MOCK_CONST_METHOD1(hasCapability, bool(HWC2::Capability));
-    MOCK_CONST_METHOD2(hasDisplayCapability,
-                       bool(const std::optional<DisplayId>&, HWC2::DisplayCapability));
+                       bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*));
+    MOCK_CONST_METHOD1(hasCapability, bool(hal::Capability));
+    MOCK_CONST_METHOD2(hasDisplayCapability, bool(DisplayId, hal::DisplayCapability));
 
     MOCK_METHOD3(allocateVirtualDisplay,
                  std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*));
+    MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, DisplayId));
     MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId));
     MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*));
-    MOCK_METHOD2(prepare, status_t(DisplayId, const compositionengine::Output&));
+    MOCK_METHOD3(getDeviceCompositionChanges,
+                 status_t(DisplayId, bool,
+                          std::optional<android::HWComposer::DeviceRequestedChanges>*));
     MOCK_METHOD5(setClientTarget,
                  status_t(DisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
                           ui::Dataspace));
     MOCK_METHOD1(presentAndGetReleaseFences, status_t(DisplayId));
-    MOCK_METHOD2(setPowerMode, status_t(DisplayId, int));
+    MOCK_METHOD2(setPowerMode, status_t(DisplayId, hal::PowerMode));
     MOCK_METHOD2(setActiveConfig, status_t(DisplayId, size_t));
     MOCK_METHOD2(setColorTransform, status_t(DisplayId, const mat4&));
     MOCK_METHOD1(disconnectDisplay, void(DisplayId));
     MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&));
-    MOCK_CONST_METHOD1(hasFlipClientTargetRequest, bool(const std::optional<DisplayId>&));
-    MOCK_CONST_METHOD1(hasClientComposition, bool(const std::optional<DisplayId>&));
     MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(DisplayId));
     MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(DisplayId, HWC2::Layer*));
     MOCK_METHOD3(setOutputBuffer, status_t(DisplayId, const sp<Fence>&, const sp<GraphicBuffer>&));
@@ -65,13 +74,13 @@
     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,
-                 std::optional<DisplayIdentificationInfo>(hwc2_display_t, HWC2::Connection));
-    MOCK_METHOD2(onVsync, bool(hwc2_display_t, int64_t));
-    MOCK_METHOD2(setVsyncEnabled, void(DisplayId, HWC2::Vsync));
+                 std::optional<DisplayIdentificationInfo>(hal::HWDisplayId, hal::Connection));
+    MOCK_METHOD2(onVsync, bool(hal::HWDisplayId, int64_t));
+    MOCK_METHOD2(setVsyncEnabled, void(DisplayId, hal::Vsync));
     MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(DisplayId));
     MOCK_CONST_METHOD1(isConnected, bool(DisplayId));
     MOCK_CONST_METHOD1(getConfigs,
@@ -81,14 +90,25 @@
     MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(DisplayId));
     MOCK_METHOD3(setActiveColorMode, status_t(DisplayId, ui::ColorMode, ui::RenderIntent));
     MOCK_CONST_METHOD0(isUsingVrComposer, bool());
+    MOCK_CONST_METHOD1(getDisplayConnectionType, DisplayConnectionType(DisplayId));
+    MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(DisplayId));
+    MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(DisplayId));
+    MOCK_METHOD4(setActiveConfigWithConstraints,
+                 status_t(DisplayId, size_t, const hal::VsyncPeriodChangeConstraints&,
+                          hal::VsyncPeriodChangeTimeline*));
+    MOCK_METHOD2(setAutoLowLatencyMode, status_t(DisplayId, bool));
+    MOCK_METHOD2(getSupportedContentTypes, status_t(DisplayId, std::vector<hal::ContentType>*));
+    MOCK_METHOD2(setContentType, status_t(DisplayId, hal::ContentType));
+    MOCK_CONST_METHOD0(getSupportedLayerGenericMetadata,
+                       const std::unordered_map<std::string, bool>&());
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
-    MOCK_CONST_METHOD1(getHwcDisplayId, std::optional<hwc2_display_t>(int32_t));
-    MOCK_CONST_METHOD0(getInternalHwcDisplayId, std::optional<hwc2_display_t>());
-    MOCK_CONST_METHOD0(getExternalHwcDisplayId, std::optional<hwc2_display_t>());
-    MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<DisplayId>(hwc2_display_t));
-    MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hwc2_display_t>(DisplayId));
+    MOCK_CONST_METHOD1(getHwcDisplayId, std::optional<hal::HWDisplayId>(int32_t));
+    MOCK_CONST_METHOD0(getInternalHwcDisplayId, std::optional<hal::HWDisplayId>());
+    MOCK_CONST_METHOD0(getExternalHwcDisplayId, std::optional<hal::HWDisplayId>());
+    MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<DisplayId>(hal::HWDisplayId));
+    MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(DisplayId));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp
similarity index 67%
rename from services/surfaceflinger/CompositionEngine/mock/Layer.cpp
rename to services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp
index 08483cb..85b9403 100644
--- a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp
@@ -1,6 +1,6 @@
 /*
  * 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
@@ -14,13 +14,21 @@
  * limitations under the License.
  */
 
-#include <compositionengine/mock/Layer.h>
+#include "MockPowerAdvisor.h"
 
-namespace android::compositionengine::mock {
+namespace android {
+namespace Hwc2 {
+
+// This will go away once PowerAdvisor is moved into the "backend" library
+PowerAdvisor::~PowerAdvisor() = default;
+
+namespace mock {
 
 // The Google Mock documentation recommends explicit non-header instantiations
 // for better compile time performance.
-Layer::Layer() = default;
-Layer::~Layer() = default;
+PowerAdvisor::PowerAdvisor() = default;
+PowerAdvisor::~PowerAdvisor() = default;
 
-} // namespace android::compositionengine::mock
+} // namespace mock
+} // namespace Hwc2
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
similarity index 60%
rename from services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h
rename to services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index ab01c20..b738096 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -1,6 +1,6 @@
 /*
  * 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
@@ -16,26 +16,24 @@
 
 #pragma once
 
-#include <cstdint>
-#include <string>
+#include <gmock/gmock.h>
 
-#include <compositionengine/LayerFECompositionState.h>
-#include <renderengine/Mesh.h>
+#include "DisplayHardware/PowerAdvisor.h"
 
 namespace android {
+namespace Hwc2 {
+namespace mock {
 
-namespace compositionengine::impl {
+class PowerAdvisor : public android::Hwc2::PowerAdvisor {
+public:
+    PowerAdvisor();
+    ~PowerAdvisor() override;
 
-struct LayerCompositionState {
-    /*
-     * State intended to be set by LayerFE::getCompositionState
-     */
-
-    LayerFECompositionState frontEnd;
-
-    // Debugging
-    void dump(std::string& result) const;
+    MOCK_METHOD0(onBootFinished, void());
+    MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
+    MOCK_METHOD0(notifyDisplayUpdateImminent, void());
 };
 
-} // namespace compositionengine::impl
+} // namespace mock
+} // namespace Hwc2
 } // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index c9d8b5b..020f93a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -15,26 +15,28 @@
  */
 
 #include <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/mock/CompositionEngine.h>
-#include <compositionengine/mock/Layer.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
 #include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/Output.h>
 #include <gtest/gtest.h>
 
 #include "MockHWC2.h"
 #include "MockHWComposer.h"
-#include "RectMatcher.h"
+#include "RegionMatcher.h"
 
 namespace android::compositionengine {
 namespace {
 
+namespace hal = android::hardware::graphics::composer::hal;
+
 using testing::_;
+using testing::InSequence;
 using testing::Return;
 using testing::ReturnRef;
 using testing::StrictMock;
 
-constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
-
 constexpr auto TR_IDENT = 0u;
 constexpr auto TR_FLP_H = HAL_TRANSFORM_FLIP_H;
 constexpr auto TR_FLP_V = HAL_TRANSFORM_FLIP_V;
@@ -44,26 +46,49 @@
 
 const std::string kOutputName{"Test Output"};
 
-class OutputLayerTest : public testing::Test {
-public:
+MATCHER_P(ColorEq, expected, "") {
+    *result_listener << "Colors are not equal\n";
+    *result_listener << "expected " << expected.r << " " << expected.g << " " << expected.b << " "
+                     << expected.a << "\n";
+    *result_listener << "actual " << arg.r << " " << arg.g << " " << arg.b << " " << arg.a << "\n";
+
+    return expected.r == arg.r && expected.g == arg.g && expected.b == arg.b && expected.a == arg.a;
+}
+
+struct OutputLayerTest : public testing::Test {
+    struct OutputLayer final : public impl::OutputLayer {
+        OutputLayer(const compositionengine::Output& output, sp<compositionengine::LayerFE> layerFE)
+              : mOutput(output), mLayerFE(layerFE) {}
+        ~OutputLayer() override = default;
+
+        // compositionengine::OutputLayer overrides
+        const compositionengine::Output& getOutput() const override { return mOutput; }
+        compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; }
+        const impl::OutputLayerCompositionState& getState() const override { return mState; }
+        impl::OutputLayerCompositionState& editState() override { return mState; }
+
+        // compositionengine::impl::OutputLayer overrides
+        void dumpState(std::string& out) const override { mState.dump(out); }
+
+        const compositionengine::Output& mOutput;
+        sp<compositionengine::LayerFE> mLayerFE;
+        impl::OutputLayerCompositionState mState;
+    };
+
     OutputLayerTest() {
         EXPECT_CALL(*mLayerFE, getDebugName()).WillRepeatedly(Return("Test LayerFE"));
         EXPECT_CALL(mOutput, getName()).WillRepeatedly(ReturnRef(kOutputName));
 
-        EXPECT_CALL(*mLayer, getState()).WillRepeatedly(ReturnRef(mLayerState));
+        EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
         EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
     }
 
-    ~OutputLayerTest() override = default;
-
     compositionengine::mock::Output mOutput;
-    std::shared_ptr<compositionengine::mock::Layer> mLayer{
-            new StrictMock<compositionengine::mock::Layer>()};
     sp<compositionengine::mock::LayerFE> mLayerFE{
             new StrictMock<compositionengine::mock::LayerFE>()};
-    impl::OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE};
+    OutputLayer mOutputLayer{mOutput, mLayerFE};
 
-    impl::LayerCompositionState mLayerState;
+    LayerFECompositionState mLayerFEState;
     impl::OutputCompositionState mOutputState;
 };
 
@@ -74,35 +99,134 @@
 TEST_F(OutputLayerTest, canInstantiateOutputLayer) {}
 
 /*
- * OutputLayer::initialize()
+ * OutputLayer::setHwcLayer()
  */
 
-TEST_F(OutputLayerTest, initializingOutputLayerWithoutHwcDoesNothingInteresting) {
+TEST_F(OutputLayerTest, settingNullHwcLayerSetsEmptyHwcState) {
     StrictMock<compositionengine::mock::CompositionEngine> compositionEngine;
 
-    mOutputLayer.initialize(compositionEngine, std::nullopt);
+    mOutputLayer.setHwcLayer(nullptr);
 
     EXPECT_FALSE(mOutputLayer.getState().hwc);
 }
 
-TEST_F(OutputLayerTest, initializingOutputLayerWithHwcDisplayCreatesHwcLayer) {
-    StrictMock<compositionengine::mock::CompositionEngine> compositionEngine;
-    StrictMock<android::mock::HWComposer> hwc;
-    StrictMock<HWC2::mock::Layer> hwcLayer;
+TEST_F(OutputLayerTest, settingHwcLayerSetsHwcState) {
+    auto hwcLayer = std::make_shared<StrictMock<HWC2::mock::Layer>>();
 
-    EXPECT_CALL(compositionEngine, getHwComposer()).WillOnce(ReturnRef(hwc));
-    EXPECT_CALL(hwc, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer));
-
-    mOutputLayer.initialize(compositionEngine, DEFAULT_DISPLAY_ID);
+    mOutputLayer.setHwcLayer(hwcLayer);
 
     const auto& outputLayerState = mOutputLayer.getState();
     ASSERT_TRUE(outputLayerState.hwc);
 
     const auto& hwcState = *outputLayerState.hwc;
-    EXPECT_EQ(&hwcLayer, hwcState.hwcLayer.get());
+    EXPECT_EQ(hwcLayer, hwcState.hwcLayer);
+}
 
-    EXPECT_CALL(hwc, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer));
-    mOutputLayer.editState().hwc.reset();
+/*
+ * OutputLayer::calculateOutputSourceCrop()
+ */
+
+struct OutputLayerSourceCropTest : public OutputLayerTest {
+    OutputLayerSourceCropTest() {
+        // Set reasonable default values for a simple case. Each test will
+        // set one specific value to something different.
+        mLayerFEState.geomUsesSourceCrop = true;
+        mLayerFEState.geomContentCrop = Rect{0, 0, 1920, 1080};
+        mLayerFEState.transparentRegionHint = Region{};
+        mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
+        mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT};
+        mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
+        mLayerFEState.geomBufferTransform = TR_IDENT;
+
+        mOutputState.viewport = Rect{0, 0, 1920, 1080};
+    }
+
+    FloatRect calculateOutputSourceCrop() {
+        mLayerFEState.geomInverseLayerTransform = mLayerFEState.geomLayerTransform.inverse();
+
+        return mOutputLayer.calculateOutputSourceCrop();
+    }
+};
+
+TEST_F(OutputLayerSourceCropTest, computesEmptyIfSourceCropNotUsed) {
+    mLayerFEState.geomUsesSourceCrop = false;
+
+    const FloatRect expected{};
+    EXPECT_THAT(calculateOutputSourceCrop(), expected);
+}
+
+TEST_F(OutputLayerSourceCropTest, correctForSimpleDefaultCase) {
+    const FloatRect expected{0.f, 0.f, 1920.f, 1080.f};
+    EXPECT_THAT(calculateOutputSourceCrop(), expected);
+}
+
+TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewport) {
+    mLayerFEState.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f};
+
+    const FloatRect expected{0.f, 0.f, 1920.f, 1080.f};
+    EXPECT_THAT(calculateOutputSourceCrop(), expected);
+}
+
+TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewportRotated) {
+    mLayerFEState.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f};
+    mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
+
+    const FloatRect expected{0.f, 0.f, 1080.f, 1080.f};
+    EXPECT_THAT(calculateOutputSourceCrop(), expected);
+}
+
+TEST_F(OutputLayerSourceCropTest, calculateOutputSourceCropWorksWithATransformedBuffer) {
+    struct Entry {
+        uint32_t bufferInvDisplay;
+        uint32_t buffer;
+        uint32_t display;
+        FloatRect expected;
+    };
+    // Not an exhaustive list of cases, but hopefully enough.
+    const std::array<Entry, 12> testData = {
+            // clang-format off
+            //             inv      buffer      display     expected
+            /*  0 */ Entry{false,   TR_IDENT,   TR_IDENT,   FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  1 */ Entry{false,   TR_IDENT,   TR_ROT_90,  FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  2 */ Entry{false,   TR_IDENT,   TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  3 */ Entry{false,   TR_IDENT,   TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+
+            /*  4 */ Entry{true,    TR_IDENT,   TR_IDENT,   FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  5 */ Entry{true,    TR_IDENT,   TR_ROT_90,  FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  6 */ Entry{true,    TR_IDENT,   TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  7 */ Entry{true,    TR_IDENT,   TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+
+            /*  8 */ Entry{false,   TR_IDENT,   TR_IDENT,   FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  9 */ Entry{false,   TR_ROT_90,  TR_ROT_90,  FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /* 10 */ Entry{false,   TR_ROT_180, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /* 11 */ Entry{false,   TR_ROT_270, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+
+            // clang-format on
+    };
+
+    for (size_t i = 0; i < testData.size(); i++) {
+        const auto& entry = testData[i];
+
+        mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay;
+        mLayerFEState.geomBufferTransform = entry.buffer;
+        mOutputState.orientation = entry.display;
+
+        EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i;
+    }
+}
+
+TEST_F(OutputLayerSourceCropTest, geomContentCropAffectsCrop) {
+    mLayerFEState.geomContentCrop = Rect{0, 0, 960, 540};
+
+    const FloatRect expected{0.f, 0.f, 960.f, 540.f};
+    EXPECT_THAT(calculateOutputSourceCrop(), expected);
+}
+
+TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) {
+    mOutputState.viewport = Rect{0, 0, 960, 540};
+
+    const FloatRect expected{0.f, 0.f, 960.f, 540.f};
+    EXPECT_THAT(calculateOutputSourceCrop(), expected);
 }
 
 /*
@@ -114,20 +238,19 @@
         // Set reasonable default values for a simple case. Each test will
         // set one specific value to something different.
 
-        mLayerState.frontEnd.geomActiveTransparentRegion = Region{};
-        mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT};
-        mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080};
-        mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false;
-        mLayerState.frontEnd.geomCrop = Rect{0, 0, 1920, 1080};
-        mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
+        mLayerFEState.transparentRegionHint = Region{};
+        mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT};
+        mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
+        mLayerFEState.geomBufferUsesDisplayInverseTransform = false;
+        mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
+        mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
 
         mOutputState.viewport = Rect{0, 0, 1920, 1080};
         mOutputState.transform = ui::Transform{TR_IDENT};
     }
 
     Rect calculateOutputDisplayFrame() {
-        mLayerState.frontEnd.geomInverseLayerTransform =
-                mLayerState.frontEnd.geomLayerTransform.inverse();
+        mLayerFEState.geomInverseLayerTransform = mLayerFEState.geomLayerTransform.inverse();
 
         return mOutputLayer.calculateOutputDisplayFrame();
     }
@@ -135,50 +258,50 @@
 
 TEST_F(OutputLayerDisplayFrameTest, correctForSimpleDefaultCase) {
     const Rect expected{0, 0, 1920, 1080};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, fullActiveTransparentRegionReturnsEmptyFrame) {
-    mLayerState.frontEnd.geomActiveTransparentRegion = Region{Rect{0, 0, 1920, 1080}};
+    mLayerFEState.transparentRegionHint = Region{Rect{0, 0, 1920, 1080}};
     const Rect expected{0, 0, 0, 0};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) {
-    mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500};
+    mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
     const Rect expected{100, 200, 300, 500};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) {
-    mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500};
-    mLayerState.frontEnd.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
+    mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
+    mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
     const Rect expected{1420, 100, 1720, 300};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) {
-    mLayerState.frontEnd.geomCrop = Rect{};
+    mLayerFEState.geomCrop = Rect{};
     const Rect expected{0, 0, 1920, 1080};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
-TEST_F(OutputLayerDisplayFrameTest, geomLayerSnapToBoundsAffectsFrame) {
-    mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f};
+TEST_F(OutputLayerDisplayFrameTest, geomLayerBoundsAffectsFrame) {
+    mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f};
     const Rect expected{0, 0, 960, 540};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
     mOutputState.viewport = Rect{0, 0, 960, 540};
     const Rect expected{0, 0, 960, 540};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, outputTransformAffectsDisplayFrame) {
     mOutputState.transform = ui::Transform{HAL_TRANSFORM_ROT_90};
     const Rect expected{-1080, 0, 0, 1920};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 /*
@@ -186,7 +309,7 @@
  */
 
 TEST_F(OutputLayerTest, calculateOutputRelativeBufferTransformTestsNeeded) {
-    mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false;
+    mLayerFEState.geomBufferUsesDisplayInverseTransform = false;
 
     struct Entry {
         uint32_t layer;
@@ -233,22 +356,320 @@
     for (size_t i = 0; i < testData.size(); i++) {
         const auto& entry = testData[i];
 
-        mLayerState.frontEnd.geomLayerTransform.set(entry.layer, 1920, 1080);
-        mLayerState.frontEnd.geomBufferTransform = entry.buffer;
+        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.display);
         EXPECT_EQ(entry.expected, actual) << "entry " << i;
     }
 }
 
+TEST_F(OutputLayerTest,
+       calculateOutputRelativeBufferTransformTestWithOfBufferUsesDisplayInverseTransform) {
+    mLayerFEState.geomBufferUsesDisplayInverseTransform = true;
+
+    struct Entry {
+        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;
+    };
+    const std::array<Entry, 64> testData = {
+            // clang-format off
+            //    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},
+
+            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},
+
+            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},
+
+            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},
+
+            //    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},
+
+            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.set(entry.layer, 1920, 1080);
+        mLayerFEState.geomBufferTransform = entry.buffer;
+        mOutputState.orientation = entry.display;
+        mOutputState.transform = ui::Transform{entry.display};
+
+        const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.internal);
+        EXPECT_EQ(entry.expected, actual) << "entry " << i;
+    }
+}
+
+/*
+ * OutputLayer::updateCompositionState()
+ */
+
+struct OutputLayerPartialMockForUpdateCompositionState : public impl::OutputLayer {
+    OutputLayerPartialMockForUpdateCompositionState(const compositionengine::Output& output,
+                                                    sp<compositionengine::LayerFE> layerFE)
+          : mOutput(output), mLayerFE(layerFE) {}
+    // Mock everything called by updateCompositionState to simplify testing it.
+    MOCK_CONST_METHOD0(calculateOutputSourceCrop, FloatRect());
+    MOCK_CONST_METHOD0(calculateOutputDisplayFrame, Rect());
+    MOCK_CONST_METHOD1(calculateOutputRelativeBufferTransform, uint32_t(uint32_t));
+
+    // compositionengine::OutputLayer overrides
+    const compositionengine::Output& getOutput() const override { return mOutput; }
+    compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; }
+    const impl::OutputLayerCompositionState& getState() const override { return mState; }
+    impl::OutputLayerCompositionState& editState() override { return mState; }
+
+    // These need implementations though are not expected to be called.
+    MOCK_CONST_METHOD1(dumpState, void(std::string&));
+
+    const compositionengine::Output& mOutput;
+    sp<compositionengine::LayerFE> mLayerFE;
+    impl::OutputLayerCompositionState mState;
+};
+
+struct OutputLayerUpdateCompositionStateTest : public OutputLayerTest {
+public:
+    OutputLayerUpdateCompositionStateTest() {
+        EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
+        EXPECT_CALL(mOutput, getDisplayColorProfile())
+                .WillRepeatedly(Return(&mDisplayColorProfile));
+        EXPECT_CALL(mDisplayColorProfile, isDataspaceSupported(_)).WillRepeatedly(Return(true));
+    }
+
+    ~OutputLayerUpdateCompositionStateTest() = default;
+
+    void setupGeometryChildCallValues(ui::Transform::RotationFlags internalDisplayRotationFlags) {
+        EXPECT_CALL(mOutputLayer, calculateOutputSourceCrop()).WillOnce(Return(kSourceCrop));
+        EXPECT_CALL(mOutputLayer, calculateOutputDisplayFrame()).WillOnce(Return(kDisplayFrame));
+        EXPECT_CALL(mOutputLayer,
+                    calculateOutputRelativeBufferTransform(internalDisplayRotationFlags))
+                .WillOnce(Return(mBufferTransform));
+    }
+
+    void validateComputedGeometryState() {
+        const auto& state = mOutputLayer.getState();
+        EXPECT_EQ(kSourceCrop, state.sourceCrop);
+        EXPECT_EQ(kDisplayFrame, state.displayFrame);
+        EXPECT_EQ(static_cast<Hwc2::Transform>(mBufferTransform), state.bufferTransform);
+    }
+
+    const FloatRect kSourceCrop{1.f, 2.f, 3.f, 4.f};
+    const Rect kDisplayFrame{11, 12, 13, 14};
+    uint32_t mBufferTransform{21};
+
+    using OutputLayer = OutputLayerPartialMockForUpdateCompositionState;
+    StrictMock<OutputLayer> mOutputLayer{mOutput, mLayerFE};
+    StrictMock<mock::DisplayColorProfile> mDisplayColorProfile;
+};
+
+TEST_F(OutputLayerUpdateCompositionStateTest, doesNothingIfNoFECompositionState) {
+    EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+
+    mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_90);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest, setsStateNormally) {
+    mLayerFEState.isSecure = true;
+    mOutputState.isSecure = true;
+    mOutputLayer.editState().forceClientComposition = true;
+
+    setupGeometryChildCallValues(ui::Transform::RotationFlags::ROT_90);
+
+    mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_90);
+
+    validateComputedGeometryState();
+
+    EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest,
+       alsoSetsForceCompositionIfSecureLayerOnNonsecureOutput) {
+    mLayerFEState.isSecure = true;
+    mOutputState.isSecure = false;
+
+    setupGeometryChildCallValues(ui::Transform::RotationFlags::ROT_0);
+
+    mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_0);
+
+    validateComputedGeometryState();
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest,
+       alsoSetsForceCompositionIfUnsupportedBufferTransform) {
+    mLayerFEState.isSecure = true;
+    mOutputState.isSecure = true;
+
+    mBufferTransform = ui::Transform::ROT_INVALID;
+
+    setupGeometryChildCallValues(ui::Transform::RotationFlags::ROT_0);
+
+    mOutputLayer.updateCompositionState(true, false, ui::Transform::RotationFlags::ROT_0);
+
+    validateComputedGeometryState();
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest, setsOutputLayerColorspaceCorrectly) {
+    mLayerFEState.dataspace = ui::Dataspace::DISPLAY_P3;
+    mOutputState.targetDataspace = ui::Dataspace::V0_SCRGB;
+
+    // If the layer is not colorspace agnostic, the output layer dataspace
+    // should use the layers requested colorspace.
+    mLayerFEState.isColorspaceAgnostic = false;
+
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+
+    EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutputLayer.getState().dataspace);
+
+    // If the layer is colorspace agnostic, the output layer dataspace
+    // should use the colorspace chosen for the whole output.
+    mLayerFEState.isColorspaceAgnostic = true;
+
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+
+    EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
+    mOutputLayer.editState().forceClientComposition = false;
+
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+
+    EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest,
+       doesNotClearForceClientCompositionIfNotDoingGeometry) {
+    mOutputLayer.editState().forceClientComposition = true;
+
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest, clientCompositionForcedFromFrontEndFlagAtAnyTime) {
+    mLayerFEState.forceClientComposition = true;
+    mOutputLayer.editState().forceClientComposition = false;
+
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest,
+       clientCompositionForcedFromUnsupportedDataspaceAtAnyTime) {
+    mOutputLayer.editState().forceClientComposition = false;
+    EXPECT_CALL(mDisplayColorProfile, isDataspaceSupported(_)).WillRepeatedly(Return(false));
+
+    mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest, clientCompositionForcedFromArgumentFlag) {
+    mLayerFEState.forceClientComposition = false;
+    mOutputLayer.editState().forceClientComposition = false;
+
+    mOutputLayer.updateCompositionState(false, true, ui::Transform::RotationFlags::ROT_0);
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+
+    mOutputLayer.editState().forceClientComposition = false;
+
+    setupGeometryChildCallValues(ui::Transform::RotationFlags::ROT_0);
+
+    mOutputLayer.updateCompositionState(true, true, ui::Transform::RotationFlags::ROT_0);
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
 /*
  * OutputLayer::writeStateToHWC()
  */
 
 struct OutputLayerWriteStateToHWCTest : public OutputLayerTest {
-    static constexpr HWC2::Error kError = HWC2::Error::Unsupported;
+    static constexpr hal::Error kError = hal::Error::UNSUPPORTED;
     static constexpr FloatRect kSourceCrop{11.f, 12.f, 13.f, 14.f};
     static constexpr uint32_t kZOrder = 21u;
     static constexpr Hwc2::Transform kBufferTransform = static_cast<Hwc2::Transform>(31);
@@ -257,8 +678,25 @@
     static constexpr float kAlpha = 51.f;
     static constexpr uint32_t kType = 61u;
     static constexpr uint32_t kAppId = 62u;
+    static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71);
+    static constexpr int kSupportedPerFrameMetadata = 101;
+    static constexpr int kExpectedHwcSlot = 0;
+    static constexpr bool kLayerGenericMetadata1Mandatory = true;
+    static constexpr bool kLayerGenericMetadata2Mandatory = true;
 
+    static const half4 kColor;
     static const Rect kDisplayFrame;
+    static const Region kOutputSpaceVisibleRegion;
+    static const mat4 kColorTransform;
+    static const Region kSurfaceDamage;
+    static const HdrMetadata kHdrMetadata;
+    static native_handle_t* kSidebandStreamHandle;
+    static const sp<GraphicBuffer> kBuffer;
+    static const sp<Fence> kFence;
+    static const std::string kLayerGenericMetadata1Key;
+    static const std::vector<uint8_t> kLayerGenericMetadata1Value;
+    static const std::string kLayerGenericMetadata2Key;
+    static const std::vector<uint8_t> kLayerGenericMetadata2Value;
 
     OutputLayerWriteStateToHWCTest() {
         auto& outputLayerState = mOutputLayer.editState();
@@ -268,30 +706,130 @@
         outputLayerState.sourceCrop = kSourceCrop;
         outputLayerState.z = kZOrder;
         outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform);
+        outputLayerState.outputSpaceVisibleRegion = kOutputSpaceVisibleRegion;
+        outputLayerState.dataspace = kDataspace;
 
-        mLayerState.frontEnd.blendMode = kBlendMode;
-        mLayerState.frontEnd.alpha = kAlpha;
-        mLayerState.frontEnd.type = kType;
-        mLayerState.frontEnd.appId = kAppId;
+        mLayerFEState.blendMode = kBlendMode;
+        mLayerFEState.alpha = kAlpha;
+        mLayerFEState.type = kType;
+        mLayerFEState.appId = kAppId;
+        mLayerFEState.colorTransform = kColorTransform;
+        mLayerFEState.color = kColor;
+        mLayerFEState.surfaceDamage = kSurfaceDamage;
+        mLayerFEState.hdrMetadata = kHdrMetadata;
+        mLayerFEState.sidebandStream = NativeHandle::create(kSidebandStreamHandle, false);
+        mLayerFEState.buffer = kBuffer;
+        mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
+        mLayerFEState.acquireFence = kFence;
+
+        EXPECT_CALL(mOutput, getDisplayColorProfile())
+                .WillRepeatedly(Return(&mDisplayColorProfile));
+        EXPECT_CALL(mDisplayColorProfile, getSupportedPerFrameMetadata())
+                .WillRepeatedly(Return(kSupportedPerFrameMetadata));
+    }
+
+    // Some tests may need to simulate unsupported HWC calls
+    enum class SimulateUnsupported { None, ColorTransform };
+
+    void includeGenericLayerMetadataInState() {
+        mLayerFEState.metadata[kLayerGenericMetadata1Key] = {kLayerGenericMetadata1Mandatory,
+                                                             kLayerGenericMetadata1Value};
+        mLayerFEState.metadata[kLayerGenericMetadata2Key] = {kLayerGenericMetadata2Mandatory,
+                                                             kLayerGenericMetadata2Value};
     }
 
     void expectGeometryCommonCalls() {
         EXPECT_CALL(*mHwcLayer, setDisplayFrame(kDisplayFrame)).WillOnce(Return(kError));
         EXPECT_CALL(*mHwcLayer, setSourceCrop(kSourceCrop)).WillOnce(Return(kError));
         EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError));
-        EXPECT_CALL(*mHwcLayer, setTransform(static_cast<HWC2::Transform>(kBufferTransform)))
-                .WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setTransform(kBufferTransform)).WillOnce(Return(kError));
 
-        EXPECT_CALL(*mHwcLayer, setBlendMode(static_cast<HWC2::BlendMode>(kBlendMode)))
-                .WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setBlendMode(kBlendMode)).WillOnce(Return(kError));
         EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError));
         EXPECT_CALL(*mHwcLayer, setInfo(kType, kAppId)).WillOnce(Return(kError));
     }
 
+    void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None) {
+        EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(kOutputSpaceVisibleRegion)))
+                .WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setDataspace(kDataspace)).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setColorTransform(kColorTransform))
+                .WillOnce(Return(unsupported == SimulateUnsupported::ColorTransform
+                                         ? hal::Error::UNSUPPORTED
+                                         : hal::Error::NONE));
+        EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(kSurfaceDamage)))
+                .WillOnce(Return(kError));
+    }
+
+    void expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition compositionType) {
+        EXPECT_CALL(*mHwcLayer, setCompositionType(compositionType)).WillOnce(Return(kError));
+    }
+
+    void expectNoSetCompositionTypeCall() {
+        EXPECT_CALL(*mHwcLayer, setCompositionType(_)).Times(0);
+    }
+
+    void expectSetColorCall() {
+        const hal::Color color = {static_cast<uint8_t>(std::round(kColor.r * 255)),
+                                  static_cast<uint8_t>(std::round(kColor.g * 255)),
+                                  static_cast<uint8_t>(std::round(kColor.b * 255)), 255};
+
+        EXPECT_CALL(*mHwcLayer, setColor(ColorEq(color))).WillOnce(Return(kError));
+    }
+
+    void expectSetSidebandHandleCall() {
+        EXPECT_CALL(*mHwcLayer, setSidebandStream(kSidebandStreamHandle));
+    }
+
+    void expectSetHdrMetadataAndBufferCalls() {
+        EXPECT_CALL(*mHwcLayer, setPerFrameMetadata(kSupportedPerFrameMetadata, kHdrMetadata));
+        EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, kBuffer, kFence));
+    }
+
+    void expectGenericLayerMetadataCalls() {
+        // Note: Can be in any order.
+        EXPECT_CALL(*mHwcLayer,
+                    setLayerGenericMetadata(kLayerGenericMetadata1Key,
+                                            kLayerGenericMetadata1Mandatory,
+                                            kLayerGenericMetadata1Value));
+        EXPECT_CALL(*mHwcLayer,
+                    setLayerGenericMetadata(kLayerGenericMetadata2Key,
+                                            kLayerGenericMetadata2Mandatory,
+                                            kLayerGenericMetadata2Value));
+    }
+
     std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()};
+    StrictMock<mock::DisplayColorProfile> mDisplayColorProfile;
 };
 
+const half4 OutputLayerWriteStateToHWCTest::kColor{81.f / 255.f, 82.f / 255.f, 83.f / 255.f,
+                                                   84.f / 255.f};
 const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044};
+const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{
+        Rect{1005, 1006, 1007, 1008}};
+const mat4 OutputLayerWriteStateToHWCTest::kColorTransform{
+        1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016,
+        1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024,
+};
+const Region OutputLayerWriteStateToHWCTest::kSurfaceDamage{Rect{1025, 1026, 1027, 1028}};
+const HdrMetadata OutputLayerWriteStateToHWCTest::kHdrMetadata{{/* LightFlattenable */}, 1029};
+native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle =
+        reinterpret_cast<native_handle_t*>(1031);
+const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
+const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
+const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key =
+        "com.example.metadata.1";
+const std::vector<uint8_t> OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Value{{1, 2, 3}};
+const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata2Key =
+        "com.example.metadata.2";
+const std::vector<uint8_t> OutputLayerWriteStateToHWCTest::kLayerGenericMetadata2Value{
+        {4, 5, 6, 7}};
+
+TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) {
+    EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+
+    mOutputLayer.writeStateToHWC(true);
+}
 
 TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
     mOutputLayer.editState().hwc.reset();
@@ -305,15 +843,18 @@
     mOutputLayer.writeStateToHWC(true);
 }
 
-TEST_F(OutputLayerWriteStateToHWCTest, canSetsAllState) {
+TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) {
     expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+
+    expectNoSetCompositionTypeCall();
 
     mOutputLayer.writeStateToHWC(true);
 }
 
 TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
-    mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false;
-    mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT};
+    mLayerFEState.geomBufferUsesDisplayInverseTransform = false;
+    mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT};
     // This test simulates a scenario where displayInstallOrientation is set to
     // ROT_90. This only has an effect on the transform; orientation stays 0 (see
     // DisplayDevice::setProjection).
@@ -321,9 +862,310 @@
     mOutputState.transform = ui::Transform{TR_ROT_90};
     // Buffers are pre-rotated based on the transform hint (ROT_90); their
     // geomBufferTransform is set to the inverse transform.
-    mLayerState.frontEnd.geomBufferTransform = TR_ROT_270;
+    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) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    expectPerFrameCommonCalls();
+
+    // Setting the composition type should happen before setting the color. We
+    // check this in this test only by setting up an testing::InSeqeuence
+    // instance before setting up the two expectations.
+    InSequence s;
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
+    expectSetColorCall();
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
+
+    expectPerFrameCommonCalls();
+    expectSetSidebandHandleCall();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::CURSOR;
+
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
+    (*mOutputLayer.editState().hwc).hwcCompositionType =
+            Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    expectPerFrameCommonCalls();
+    expectSetColorCall();
+    expectNoSetCompositionTypeCall();
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    expectPerFrameCommonCalls(SimulateUnsupported::ColorTransform);
+    expectSetColorCall();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
+    mOutputLayer.editState().forceClientComposition = true;
+
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    expectPerFrameCommonCalls();
+    expectSetColorCall();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    includeGenericLayerMetadataInState();
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectGenericLayerMetadataCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+    mOutputLayer.writeStateToHWC(true);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    includeGenericLayerMetadataInState();
+
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+/*
+ * OutputLayer::writeCursorPositionToHWC()
+ */
+
+struct OutputLayerWriteCursorPositionToHWCTest : public OutputLayerTest {
+    static constexpr int kDefaultTransform = TR_IDENT;
+    static constexpr hal::Error kDefaultError = hal::Error::UNSUPPORTED;
+
+    static const Rect kDefaultDisplayViewport;
+    static const Rect kDefaultCursorFrame;
+
+    OutputLayerWriteCursorPositionToHWCTest() {
+        auto& outputLayerState = mOutputLayer.editState();
+        outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer);
+
+        mLayerFEState.cursorFrame = kDefaultCursorFrame;
+
+        mOutputState.viewport = kDefaultDisplayViewport;
+        mOutputState.transform = ui::Transform{kDefaultTransform};
+    }
+
+    std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()};
+};
+
+const Rect OutputLayerWriteCursorPositionToHWCTest::kDefaultDisplayViewport{0, 0, 1920, 1080};
+const Rect OutputLayerWriteCursorPositionToHWCTest::kDefaultCursorFrame{1, 2, 3, 4};
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, doesNothingIfNoFECompositionState) {
+    EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCHandlesNoHwcState) {
+    mOutputLayer.editState().hwc.reset();
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCWritesStateToHWC) {
+    EXPECT_CALL(*mHwcLayer, setCursorPosition(1, 2)).WillOnce(Return(kDefaultError));
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCIntersectedWithViewport) {
+    mLayerFEState.cursorFrame = Rect{3000, 3000, 3016, 3016};
+
+    EXPECT_CALL(*mHwcLayer, setCursorPosition(1920, 1080)).WillOnce(Return(kDefaultError));
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCRotatedByTransform) {
+    mOutputState.transform = ui::Transform{TR_ROT_90};
+
+    EXPECT_CALL(*mHwcLayer, setCursorPosition(-4, 1)).WillOnce(Return(kDefaultError));
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+/*
+ * OutputLayer::getHwcLayer()
+ */
+
+TEST_F(OutputLayerTest, getHwcLayerHandlesNoHwcState) {
+    mOutputLayer.editState().hwc.reset();
+
+    EXPECT_TRUE(mOutputLayer.getHwcLayer() == nullptr);
+}
+
+TEST_F(OutputLayerTest, getHwcLayerHandlesNoHwcLayer) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+
+    EXPECT_TRUE(mOutputLayer.getHwcLayer() == nullptr);
+}
+
+TEST_F(OutputLayerTest, getHwcLayerReturnsHwcLayer) {
+    auto hwcLayer = std::make_shared<StrictMock<HWC2::mock::Layer>>();
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{hwcLayer};
+
+    EXPECT_EQ(hwcLayer.get(), mOutputLayer.getHwcLayer());
+}
+
+/*
+ * OutputLayer::requiresClientComposition()
+ */
+
+TEST_F(OutputLayerTest, requiresClientCompositionReturnsTrueIfNoHWC2State) {
+    mOutputLayer.editState().hwc.reset();
+
+    EXPECT_TRUE(mOutputLayer.requiresClientComposition());
+}
+
+TEST_F(OutputLayerTest, requiresClientCompositionReturnsTrueIfSetToClientComposition) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+
+    EXPECT_TRUE(mOutputLayer.requiresClientComposition());
+}
+
+TEST_F(OutputLayerTest, requiresClientCompositionReturnsFalseIfSetToDeviceComposition) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+    EXPECT_FALSE(mOutputLayer.requiresClientComposition());
+}
+
+/*
+ * OutputLayer::isHardwareCursor()
+ */
+
+TEST_F(OutputLayerTest, isHardwareCursorReturnsFalseIfNoHWC2State) {
+    mOutputLayer.editState().hwc.reset();
+
+    EXPECT_FALSE(mOutputLayer.isHardwareCursor());
+}
+
+TEST_F(OutputLayerTest, isHardwareCursorReturnsTrueIfSetToCursorComposition) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CURSOR;
+
+    EXPECT_TRUE(mOutputLayer.isHardwareCursor());
+}
+
+TEST_F(OutputLayerTest, isHardwareCursorReturnsFalseIfSetToDeviceComposition) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+    EXPECT_FALSE(mOutputLayer.isHardwareCursor());
+}
+
+/*
+ * OutputLayer::applyDeviceCompositionTypeChange()
+ */
+
+TEST_F(OutputLayerTest, applyDeviceCompositionTypeChangeSetsNewType) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+    mOutputLayer.applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT);
+
+    ASSERT_TRUE(mOutputLayer.getState().hwc);
+    EXPECT_EQ(Hwc2::IComposerClient::Composition::CLIENT,
+              mOutputLayer.getState().hwc->hwcCompositionType);
+}
+
+/*
+ * OutputLayer::prepareForDeviceLayerRequests()
+ */
+
+TEST_F(OutputLayerTest, prepareForDeviceLayerRequestsResetsRequestState) {
+    mOutputLayer.editState().clearClientTarget = true;
+
+    mOutputLayer.prepareForDeviceLayerRequests();
+
+    EXPECT_FALSE(mOutputLayer.getState().clearClientTarget);
+}
+
+/*
+ * OutputLayer::applyDeviceLayerRequest()
+ */
+
+TEST_F(OutputLayerTest, applyDeviceLayerRequestHandlesClearClientTarget) {
+    mOutputLayer.editState().clearClientTarget = false;
+
+    mOutputLayer.applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest::CLEAR_CLIENT_TARGET);
+
+    EXPECT_TRUE(mOutputLayer.getState().clearClientTarget);
+}
+
+TEST_F(OutputLayerTest, applyDeviceLayerRequestHandlesUnknownRequest) {
+    mOutputLayer.editState().clearClientTarget = false;
+
+    mOutputLayer.applyDeviceLayerRequest(static_cast<Hwc2::IComposerClient::LayerRequest>(0));
+
+    EXPECT_FALSE(mOutputLayer.getState().clearClientTarget);
+}
+
+/*
+ * OutputLayer::needsFiltering()
+ */
+
+TEST_F(OutputLayerTest, needsFilteringReturnsFalseIfDisplaySizeSameAsSourceSize) {
+    mOutputLayer.editState().displayFrame = Rect(100, 100, 200, 200);
+    mOutputLayer.editState().sourceCrop = FloatRect{0.f, 0.f, 100.f, 100.f};
+
+    EXPECT_FALSE(mOutputLayer.needsFiltering());
+}
+
+TEST_F(OutputLayerTest, needsFilteringReturnsTrueIfDisplaySizeDifferentFromSourceSize) {
+    mOutputLayer.editState().displayFrame = Rect(100, 100, 200, 200);
+    mOutputLayer.editState().sourceCrop = FloatRect{0.f, 0.f, 100.1f, 100.1f};
+
+    EXPECT_TRUE(mOutputLayer.needsFiltering());
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index fee0c11..7a06400 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -16,49 +16,172 @@
 
 #include <cmath>
 
+#include <android-base/stringprintf.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/impl/Output.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/mock/CompositionEngine.h>
 #include <compositionengine/mock/DisplayColorProfile.h>
-#include <compositionengine/mock/Layer.h>
 #include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/OutputLayer.h>
 #include <compositionengine/mock/RenderSurface.h>
 #include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
+#include "CallOrderStateMachineHelper.h"
+#include "MockHWC2.h"
 #include "RegionMatcher.h"
-#include "TransformMatcher.h"
 
 namespace android::compositionengine {
 namespace {
 
+using testing::_;
+using testing::ByMove;
+using testing::ByRef;
+using testing::DoAll;
+using testing::ElementsAre;
+using testing::ElementsAreArray;
+using testing::Eq;
+using testing::InSequence;
+using testing::Invoke;
+using testing::IsEmpty;
+using testing::Mock;
+using testing::Pointee;
+using testing::Property;
+using testing::Ref;
 using testing::Return;
 using testing::ReturnRef;
+using testing::SetArgPointee;
 using testing::StrictMock;
 
-class OutputTest : public testing::Test {
-public:
-    OutputTest() {
-        mOutput.setDisplayColorProfileForTest(
-                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
-        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+constexpr auto TR_IDENT = 0u;
+constexpr auto TR_ROT_90 = HAL_TRANSFORM_ROT_90;
+constexpr auto MAX_CLIENT_COMPOSITION_CACHE_SIZE = 3;
 
-        mOutput.editState().bounds = kDefaultDisplaySize;
+const mat4 kIdentity;
+const mat4 kNonIdentityHalf = mat4() * 0.5f;
+const mat4 kNonIdentityQuarter = mat4() * 0.25f;
+
+constexpr OutputColorSetting kVendorSpecifiedOutputColorSetting =
+        static_cast<OutputColorSetting>(0x100);
+
+struct OutputPartialMockBase : public impl::Output {
+    // compositionengine::Output overrides
+    const OutputCompositionState& getState() const override { return mState; }
+    OutputCompositionState& editState() override { return mState; }
+
+    // Use mocks for all the remaining virtual functions
+    // not implemented by the base implementation class.
+    MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+    MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, compositionengine::OutputLayer*(size_t));
+    MOCK_METHOD2(ensureOutputLayer,
+                 compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
+    MOCK_METHOD0(finalizePendingOutputLayers, void());
+    MOCK_METHOD0(clearOutputLayers, void());
+    MOCK_CONST_METHOD1(dumpState, void(std::string&));
+    MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&());
+    MOCK_METHOD1(injectOutputLayerForTest, compositionengine::OutputLayer*(const sp<LayerFE>&));
+    MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
+
+    impl::OutputCompositionState mState;
+};
+
+struct InjectedLayer {
+    InjectedLayer() {
+        EXPECT_CALL(*outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE.get()));
+        EXPECT_CALL(*outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+        EXPECT_CALL(*outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
+
+        EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
     }
-    ~OutputTest() override = default;
+
+    mock::OutputLayer* outputLayer = {new StrictMock<mock::OutputLayer>};
+    sp<StrictMock<mock::LayerFE>> layerFE = new StrictMock<mock::LayerFE>();
+    LayerFECompositionState layerFEState;
+    impl::OutputLayerCompositionState outputLayerState;
+};
+
+struct NonInjectedLayer {
+    NonInjectedLayer() {
+        EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE.get()));
+        EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+        EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
+
+        EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+    }
+
+    mock::OutputLayer outputLayer;
+    sp<StrictMock<mock::LayerFE>> layerFE = new StrictMock<mock::LayerFE>();
+    LayerFECompositionState layerFEState;
+    impl::OutputLayerCompositionState outputLayerState;
+};
+
+struct OutputTest : public testing::Test {
+    class Output : public impl::Output {
+    public:
+        using impl::Output::injectOutputLayerForTest;
+        virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+    };
+
+    static std::shared_ptr<Output> createOutput(
+            const compositionengine::CompositionEngine& compositionEngine) {
+        return impl::createOutputTemplated<Output>(compositionEngine);
+    }
+
+    OutputTest() {
+        mOutput->setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+        mOutput->editState().bounds = kDefaultDisplaySize;
+    }
+
+    void injectOutputLayer(InjectedLayer& layer) {
+        mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(layer.outputLayer));
+    }
+
+    void injectNullOutputLayer() {
+        mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(nullptr));
+    }
 
     static const Rect kDefaultDisplaySize;
 
     StrictMock<mock::CompositionEngine> mCompositionEngine;
     mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
     mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
-    impl::Output mOutput{mCompositionEngine};
+    std::shared_ptr<Output> mOutput = createOutput(mCompositionEngine);
 };
 
 const Rect OutputTest::kDefaultDisplaySize{100, 200};
 
-/* ------------------------------------------------------------------------
+using ColorProfile = compositionengine::Output::ColorProfile;
+
+void dumpColorProfile(ColorProfile profile, std::string& result, const char* name) {
+    android::base::StringAppendF(&result, "%s (%s[%d] %s[%d] %s[%d] %s[%d]) ", name,
+                                 toString(profile.mode).c_str(), profile.mode,
+                                 toString(profile.dataspace).c_str(), profile.dataspace,
+                                 toString(profile.renderIntent).c_str(), profile.renderIntent,
+                                 toString(profile.colorSpaceAgnosticDataspace).c_str(),
+                                 profile.colorSpaceAgnosticDataspace);
+}
+
+// Checks for a ColorProfile match
+MATCHER_P(ColorProfileEq, expected, "") {
+    std::string buf;
+    buf.append("ColorProfiles are not equal\n");
+    dumpColorProfile(expected, buf, "expected value");
+    dumpColorProfile(arg, buf, "actual value");
+    *result_listener << buf;
+
+    return (expected.mode == arg.mode) && (expected.dataspace == arg.dataspace) &&
+            (expected.renderIntent == arg.renderIntent) &&
+            (expected.colorSpaceAgnosticDataspace == arg.colorSpaceAgnosticDataspace);
+}
+
+/*
  * Basic construction
  */
 
@@ -67,48 +190,48 @@
     EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
     EXPECT_CALL(*mRenderSurface, isValid()).WillOnce(Return(true));
 
-    EXPECT_TRUE(mOutput.isValid());
+    EXPECT_TRUE(mOutput->isValid());
 
     // If we take away the required components, it is no longer valid.
-    mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>());
+    mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>());
 
     EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
 
-    EXPECT_FALSE(mOutput.isValid());
+    EXPECT_FALSE(mOutput->isValid());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::setCompositionEnabled()
  */
 
 TEST_F(OutputTest, setCompositionEnabledDoesNothingIfAlreadyEnabled) {
-    mOutput.editState().isEnabled = true;
+    mOutput->editState().isEnabled = true;
 
-    mOutput.setCompositionEnabled(true);
+    mOutput->setCompositionEnabled(true);
 
-    EXPECT_TRUE(mOutput.getState().isEnabled);
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+    EXPECT_TRUE(mOutput->getState().isEnabled);
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
 }
 
 TEST_F(OutputTest, setCompositionEnabledSetsEnabledAndDirtiesEntireOutput) {
-    mOutput.editState().isEnabled = false;
+    mOutput->editState().isEnabled = false;
 
-    mOutput.setCompositionEnabled(true);
+    mOutput->setCompositionEnabled(true);
 
-    EXPECT_TRUE(mOutput.getState().isEnabled);
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+    EXPECT_TRUE(mOutput->getState().isEnabled);
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
 }
 
 TEST_F(OutputTest, setCompositionEnabledSetsDisabledAndDirtiesEntireOutput) {
-    mOutput.editState().isEnabled = true;
+    mOutput->editState().isEnabled = true;
 
-    mOutput.setCompositionEnabled(false);
+    mOutput->setCompositionEnabled(false);
 
-    EXPECT_FALSE(mOutput.getState().isEnabled);
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+    EXPECT_FALSE(mOutput->getState().isEnabled);
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::setProjection()
  */
 
@@ -117,20 +240,23 @@
     const int32_t orientation = 123;
     const Rect frame{1, 2, 3, 4};
     const Rect viewport{5, 6, 7, 8};
-    const Rect scissor{9, 10, 11, 12};
+    const Rect sourceClip{9, 10, 11, 12};
+    const Rect destinationClip{13, 14, 15, 16};
     const bool needsFiltering = true;
 
-    mOutput.setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
+    mOutput->setProjection(transform, orientation, frame, viewport, sourceClip, destinationClip,
+                           needsFiltering);
 
-    EXPECT_THAT(mOutput.getState().transform, TransformEq(transform));
-    EXPECT_EQ(orientation, mOutput.getState().orientation);
-    EXPECT_EQ(frame, mOutput.getState().frame);
-    EXPECT_EQ(viewport, mOutput.getState().viewport);
-    EXPECT_EQ(scissor, mOutput.getState().scissor);
-    EXPECT_EQ(needsFiltering, mOutput.getState().needsFiltering);
+    EXPECT_THAT(mOutput->getState().transform, transform);
+    EXPECT_EQ(orientation, mOutput->getState().orientation);
+    EXPECT_EQ(frame, mOutput->getState().frame);
+    EXPECT_EQ(viewport, mOutput->getState().viewport);
+    EXPECT_EQ(sourceClip, mOutput->getState().sourceClip);
+    EXPECT_EQ(destinationClip, mOutput->getState().destinationClip);
+    EXPECT_EQ(needsFiltering, mOutput->getState().needsFiltering);
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::setBounds()
  */
 
@@ -140,94 +266,160 @@
     EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
     EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
 
-    mOutput.setBounds(displaySize);
+    mOutput->setBounds(displaySize);
 
-    EXPECT_EQ(Rect(displaySize), mOutput.getState().bounds);
+    EXPECT_EQ(Rect(displaySize), mOutput->getState().bounds);
 
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::setLayerStackFilter()
  */
 
 TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) {
     const uint32_t layerStack = 123u;
-    mOutput.setLayerStackFilter(layerStack, true);
+    mOutput->setLayerStackFilter(layerStack, true);
 
-    EXPECT_TRUE(mOutput.getState().layerStackInternal);
-    EXPECT_EQ(layerStack, mOutput.getState().layerStackId);
+    EXPECT_TRUE(mOutput->getState().layerStackInternal);
+    EXPECT_EQ(layerStack, mOutput->getState().layerStackId);
 
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::setColorTransform
  */
 
-TEST_F(OutputTest, setColorTransformSetsTransform) {
-    // Identity matrix sets an identity state value
-    const mat4 identity;
+TEST_F(OutputTest, setColorTransformWithNoChangeFlaggedSkipsUpdates) {
+    mOutput->editState().colorTransformMatrix = kIdentity;
 
-    mOutput.setColorTransform(identity);
+    // If no colorTransformMatrix is set the update should be skipped.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = std::nullopt;
 
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mOutput.getState().colorTransform);
-    EXPECT_EQ(identity, mOutput.getState().colorTransformMat);
+    mOutput->setColorTransform(refreshArgs);
 
-    // Since identity is the default, the dirty region should be unchanged (empty)
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+    // The internal state should be unchanged
+    EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix);
 
-    // Non-identity matrix sets a non-identity state value
-    const mat4 nonIdentityHalf = mat4() * 0.5;
-
-    mOutput.setColorTransform(nonIdentityHalf);
-
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
-    EXPECT_EQ(nonIdentityHalf, mOutput.getState().colorTransformMat);
-
-    // Since this is a state change, the entire output should now be dirty.
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
-
-    // Non-identity matrix sets a non-identity state value
-    const mat4 nonIdentityQuarter = mat4() * 0.25;
-
-    mOutput.setColorTransform(nonIdentityQuarter);
-
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
-    EXPECT_EQ(nonIdentityQuarter, mOutput.getState().colorTransformMat);
-
-    // Since this is a state change, the entire output should now be dirty.
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+    // No dirty region should be set
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
 }
 
-/* ------------------------------------------------------------------------
- * Output::setColorMode
+TEST_F(OutputTest, setColorTransformWithNoActualChangeSkipsUpdates) {
+    mOutput->editState().colorTransformMatrix = kIdentity;
+
+    // Attempting to set the same colorTransformMatrix that is already set should
+    // also skip the update.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = kIdentity;
+
+    mOutput->setColorTransform(refreshArgs);
+
+    // The internal state should be unchanged
+    EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix);
+
+    // No dirty region should be set
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
+}
+
+TEST_F(OutputTest, setColorTransformPerformsUpdateToIdentity) {
+    mOutput->editState().colorTransformMatrix = kNonIdentityHalf;
+
+    // Setting a different colorTransformMatrix should perform the update.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = kIdentity;
+
+    mOutput->setColorTransform(refreshArgs);
+
+    // The internal state should have been updated
+    EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix);
+
+    // The dirtyRegion should be set to the full display size
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
+
+TEST_F(OutputTest, setColorTransformPerformsUpdateForIdentityToHalf) {
+    mOutput->editState().colorTransformMatrix = kIdentity;
+
+    // Setting a different colorTransformMatrix should perform the update.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = kNonIdentityHalf;
+
+    mOutput->setColorTransform(refreshArgs);
+
+    // The internal state should have been updated
+    EXPECT_EQ(kNonIdentityHalf, mOutput->getState().colorTransformMatrix);
+
+    // The dirtyRegion should be set to the full display size
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
+
+TEST_F(OutputTest, setColorTransformPerformsUpdateForHalfToQuarter) {
+    mOutput->editState().colorTransformMatrix = kNonIdentityHalf;
+
+    // Setting a different colorTransformMatrix should perform the update.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = kNonIdentityQuarter;
+
+    mOutput->setColorTransform(refreshArgs);
+
+    // The internal state should have been updated
+    EXPECT_EQ(kNonIdentityQuarter, mOutput->getState().colorTransformMatrix);
+
+    // The dirtyRegion should be set to the full display size
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
+
+/*
+ * Output::setColorProfile
  */
 
-TEST_F(OutputTest, setColorModeSetsStateAndDirtiesOutputIfChanged) {
+using OutputSetColorProfileTest = OutputTest;
+
+TEST_F(OutputSetColorProfileTest, setsStateAndDirtiesOutputIfChanged) {
+    using ColorProfile = Output::ColorProfile;
+
+    EXPECT_CALL(*mDisplayColorProfile,
+                getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                   ui::Dataspace::UNKNOWN))
+            .WillOnce(Return(ui::Dataspace::UNKNOWN));
     EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
 
-    mOutput.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
-                         ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+    mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                          ui::RenderIntent::TONE_MAP_COLORIMETRIC,
+                                          ui::Dataspace::UNKNOWN});
 
-    EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput.getState().colorMode);
-    EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput.getState().dataspace);
-    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput.getState().renderIntent);
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+    EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput->getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput->getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput->getState().renderIntent);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mOutput->getState().targetDataspace);
+
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
 }
 
-TEST_F(OutputTest, setColorModeDoesNothingIfNoChange) {
-    mOutput.editState().colorMode = ui::ColorMode::DISPLAY_P3;
-    mOutput.editState().dataspace = ui::Dataspace::DISPLAY_P3;
-    mOutput.editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC;
+TEST_F(OutputSetColorProfileTest, doesNothingIfNoChange) {
+    using ColorProfile = Output::ColorProfile;
 
-    mOutput.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
-                         ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+    EXPECT_CALL(*mDisplayColorProfile,
+                getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                   ui::Dataspace::UNKNOWN))
+            .WillOnce(Return(ui::Dataspace::UNKNOWN));
 
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+    mOutput->editState().colorMode = ui::ColorMode::DISPLAY_P3;
+    mOutput->editState().dataspace = ui::Dataspace::DISPLAY_P3;
+    mOutput->editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC;
+    mOutput->editState().targetDataspace = ui::Dataspace::UNKNOWN;
+
+    mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                          ui::RenderIntent::TONE_MAP_COLORIMETRIC,
+                                          ui::Dataspace::UNKNOWN});
+
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::setRenderSurface()
  */
 
@@ -237,22 +429,22 @@
     mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
     EXPECT_CALL(*renderSurface, getSize()).WillOnce(ReturnRef(newDisplaySize));
 
-    mOutput.setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
+    mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
 
-    EXPECT_EQ(Rect(newDisplaySize), mOutput.getState().bounds);
+    EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().bounds);
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::getDirtyRegion()
  */
 
 TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) {
     const Rect viewport{100, 200};
-    mOutput.editState().viewport = viewport;
-    mOutput.editState().dirtyRegion.set(50, 300);
+    mOutput->editState().viewport = viewport;
+    mOutput->editState().dirtyRegion.set(50, 300);
 
     {
-        Region result = mOutput.getDirtyRegion(true);
+        Region result = mOutput->getDirtyRegion(true);
 
         EXPECT_THAT(result, RegionEq(Region(viewport)));
     }
@@ -260,18 +452,18 @@
 
 TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
     const Rect viewport{100, 200};
-    mOutput.editState().viewport = viewport;
-    mOutput.editState().dirtyRegion.set(50, 300);
+    mOutput->editState().viewport = viewport;
+    mOutput->editState().dirtyRegion.set(50, 300);
 
     {
-        Region result = mOutput.getDirtyRegion(false);
+        Region result = mOutput->getDirtyRegion(false);
 
         // The dirtyRegion should be clipped to the display bounds.
         EXPECT_THAT(result, RegionEq(Region(Rect(50, 200))));
     }
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::belongsInOutput()
  */
 
@@ -280,101 +472,3637 @@
     const uint32_t layerStack2 = 456u;
 
     // If the output accepts layerStack1 and internal-only layers....
-    mOutput.setLayerStackFilter(layerStack1, true);
+    mOutput->setLayerStackFilter(layerStack1, true);
+
+    // A layer with no layerStack does not belong to it, internal-only or not.
+    EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, false));
+    EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, true));
 
     // Any layer with layerStack1 belongs to it, internal-only or not.
-    EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
-    EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, true));
-    EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
-    EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+    EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false));
+    EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, true));
+    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true));
+    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false));
 
     // If the output accepts layerStack21 but not internal-only layers...
-    mOutput.setLayerStackFilter(layerStack1, false);
+    mOutput->setLayerStackFilter(layerStack1, false);
 
     // Only non-internal layers with layerStack1 belong to it.
-    EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false));
-    EXPECT_FALSE(mOutput.belongsInOutput(layerStack1, true));
-    EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true));
-    EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false));
+    EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false));
+    EXPECT_FALSE(mOutput->belongsInOutput(layerStack1, true));
+    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true));
+    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false));
 }
 
-/* ------------------------------------------------------------------------
+TEST_F(OutputTest, belongsInOutputHandlesLayerWithNoCompositionState) {
+    NonInjectedLayer layer;
+    sp<LayerFE> layerFE(layer.layerFE);
+
+    // If the layer has no composition state, it does not belong to any output.
+    EXPECT_CALL(*layer.layerFE, getCompositionState).WillOnce(Return(nullptr));
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+}
+
+TEST_F(OutputTest, belongsInOutputFiltersLayersAsExpected) {
+    NonInjectedLayer layer;
+    sp<LayerFE> layerFE(layer.layerFE);
+
+    const uint32_t layerStack1 = 123u;
+    const uint32_t layerStack2 = 456u;
+
+    // If the output accepts layerStack1 and internal-only layers....
+    mOutput->setLayerStackFilter(layerStack1, true);
+
+    // A layer with no layerStack does not belong to it, internal-only or not.
+    layer.layerFEState.layerStackId = std::nullopt;
+    layer.layerFEState.internalOnly = false;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = std::nullopt;
+    layer.layerFEState.internalOnly = true;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+
+    // Any layer with layerStack1 belongs to it, internal-only or not.
+    layer.layerFEState.layerStackId = layerStack1;
+    layer.layerFEState.internalOnly = false;
+    EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = layerStack1;
+    layer.layerFEState.internalOnly = true;
+    EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = layerStack2;
+    layer.layerFEState.internalOnly = true;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = layerStack2;
+    layer.layerFEState.internalOnly = false;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+
+    // If the output accepts layerStack1 but not internal-only layers...
+    mOutput->setLayerStackFilter(layerStack1, false);
+
+    // Only non-internal layers with layerStack1 belong to it.
+    layer.layerFEState.layerStackId = layerStack1;
+    layer.layerFEState.internalOnly = false;
+    EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = layerStack1;
+    layer.layerFEState.internalOnly = true;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = layerStack2;
+    layer.layerFEState.internalOnly = true;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = layerStack2;
+    layer.layerFEState.internalOnly = false;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+}
+
+/*
  * Output::getOutputLayerForLayer()
  */
 
 TEST_F(OutputTest, getOutputLayerForLayerWorks) {
-    mock::OutputLayer* outputLayer1 = new StrictMock<mock::OutputLayer>();
-    mock::OutputLayer* outputLayer2 = new StrictMock<mock::OutputLayer>();
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    NonInjectedLayer layer3;
 
-    Output::OutputLayers outputLayers;
-    outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer1));
-    outputLayers.emplace_back(nullptr);
-    outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer2));
-    mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
-
-    StrictMock<mock::Layer> layer;
-    StrictMock<mock::Layer> otherLayer;
+    injectOutputLayer(layer1);
+    injectNullOutputLayer();
+    injectOutputLayer(layer2);
 
     // If the input layer matches the first OutputLayer, it will be returned.
-    EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(layer));
-    EXPECT_EQ(outputLayer1, mOutput.getOutputLayerForLayer(&layer));
+    EXPECT_CALL(*layer1.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer1.layerFE.get()));
+    EXPECT_EQ(layer1.outputLayer, mOutput->getOutputLayerForLayer(layer1.layerFE));
 
     // If the input layer matches the second OutputLayer, it will be returned.
-    EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer));
-    EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(layer));
-    EXPECT_EQ(outputLayer2, mOutput.getOutputLayerForLayer(&layer));
+    EXPECT_CALL(*layer1.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer1.layerFE.get()));
+    EXPECT_CALL(*layer2.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer2.layerFE.get()));
+    EXPECT_EQ(layer2.outputLayer, mOutput->getOutputLayerForLayer(layer2.layerFE));
 
     // If the input layer does not match an output layer, null will be returned.
-    EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer));
-    EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(otherLayer));
-    EXPECT_EQ(nullptr, mOutput.getOutputLayerForLayer(&layer));
+    EXPECT_CALL(*layer1.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer1.layerFE.get()));
+    EXPECT_CALL(*layer2.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer2.layerFE.get()));
+    EXPECT_EQ(nullptr, mOutput->getOutputLayerForLayer(layer3.layerFE));
 }
 
-/* ------------------------------------------------------------------------
- * Output::getOrCreateOutputLayer()
+/*
+ * Output::setReleasedLayers()
  */
 
-TEST_F(OutputTest, getOrCreateOutputLayerWorks) {
-    mock::OutputLayer* existingOutputLayer = new StrictMock<mock::OutputLayer>();
+using OutputSetReleasedLayersTest = OutputTest;
 
-    Output::OutputLayers outputLayers;
-    outputLayers.emplace_back(nullptr);
-    outputLayers.emplace_back(std::unique_ptr<OutputLayer>(existingOutputLayer));
-    mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
+TEST_F(OutputSetReleasedLayersTest, setReleasedLayersTakesGivenLayers) {
+    sp<StrictMock<mock::LayerFE>> layer1FE{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> layer2FE{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> layer3FE{new StrictMock<mock::LayerFE>()};
 
-    std::shared_ptr<mock::Layer> layer{new StrictMock<mock::Layer>()};
-    sp<LayerFE> layerFE{new StrictMock<mock::LayerFE>()};
+    Output::ReleasedLayers layers;
+    layers.push_back(layer1FE);
+    layers.push_back(layer2FE);
+    layers.push_back(layer3FE);
 
-    StrictMock<mock::Layer> otherLayer;
+    mOutput->setReleasedLayers(std::move(layers));
 
-    {
-        // If there is no OutputLayer corresponding to the input layer, a
-        // new OutputLayer is constructed and returned.
-        EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(otherLayer));
-        auto result = mOutput.getOrCreateOutputLayer(std::nullopt, layer, layerFE);
-        EXPECT_NE(existingOutputLayer, result.get());
-        EXPECT_TRUE(result.get() != nullptr);
-        EXPECT_EQ(layer.get(), &result->getLayer());
-        EXPECT_EQ(layerFE.get(), &result->getLayerFE());
+    const auto& setLayers = mOutput->getReleasedLayersForTest();
+    ASSERT_EQ(3u, setLayers.size());
+    ASSERT_EQ(layer1FE.get(), setLayers[0].promote().get());
+    ASSERT_EQ(layer2FE.get(), setLayers[1].promote().get());
+    ASSERT_EQ(layer3FE.get(), setLayers[2].promote().get());
+}
 
-        // The entries in the ordered array should be unchanged.
-        auto& outputLayers = mOutput.getOutputLayersOrderedByZ();
-        EXPECT_EQ(nullptr, outputLayers[0].get());
-        EXPECT_EQ(existingOutputLayer, outputLayers[1].get());
+/*
+ * Output::updateLayerStateFromFE()
+ */
+
+using OutputUpdateLayerStateFromFETest = OutputTest;
+
+TEST_F(OutputUpdateLayerStateFromFETest, handlesNoOutputLayerCase) {
+    CompositionRefreshArgs refreshArgs;
+
+    mOutput->updateLayerStateFromFE(refreshArgs);
+}
+
+TEST_F(OutputUpdateLayerStateFromFETest, preparesContentStateForAllContainedLayers) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    EXPECT_CALL(*layer1.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
+    EXPECT_CALL(*layer2.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
+    EXPECT_CALL(*layer3.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.updatingGeometryThisFrame = false;
+
+    mOutput->updateLayerStateFromFE(refreshArgs);
+}
+
+TEST_F(OutputUpdateLayerStateFromFETest, preparesGeometryAndContentStateForAllContainedLayers) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    EXPECT_CALL(*layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
+    EXPECT_CALL(*layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
+    EXPECT_CALL(*layer3.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.updatingGeometryThisFrame = true;
+
+    mOutput->updateLayerStateFromFE(refreshArgs);
+}
+
+/*
+ * Output::updateAndWriteCompositionState()
+ */
+
+using OutputUpdateAndWriteCompositionStateTest = OutputTest;
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfLayers) {
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfOutputNotEnabled) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    mOutput->editState().isEnabled = false;
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    CompositionRefreshArgs args;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerContentForAllLayers) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(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, ui::Transform::ROT_180));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    args.internalDisplayRotationFlags = ui::Transform::ROT_180;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerGeometryAndContentForAllLayers) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(true));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(true));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(true));
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = true;
+    args.devOptForceClientComposition = false;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLayers) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = true;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+/*
+ * Output::prepareFrame()
+ */
+
+struct OutputPrepareFrameTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD0(chooseCompositionStrategy, void());
+    };
+
+    OutputPrepareFrameTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
     }
 
-    {
-        // If there is an existing OutputLayer for the requested layer, an owned
-        // pointer is returned
-        EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(*layer));
-        auto result = mOutput.getOrCreateOutputLayer(std::nullopt, layer, layerFE);
-        EXPECT_EQ(existingOutputLayer, result.get());
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    StrictMock<OutputPartialMock> mOutput;
+};
 
-        // The corresponding entry in the ordered array should be cleared.
-        auto& outputLayers = mOutput.getOutputLayersOrderedByZ();
-        EXPECT_EQ(nullptr, outputLayers[0].get());
-        EXPECT_EQ(nullptr, outputLayers[1].get());
+TEST_F(OutputPrepareFrameTest, takesEarlyOutIfNotEnabled) {
+    mOutput.editState().isEnabled = false;
+
+    mOutput.prepareFrame();
+}
+
+TEST_F(OutputPrepareFrameTest, delegatesToChooseCompositionStrategyAndRenderSurface) {
+    mOutput.editState().isEnabled = true;
+    mOutput.editState().usesClientComposition = false;
+    mOutput.editState().usesDeviceComposition = true;
+
+    EXPECT_CALL(mOutput, chooseCompositionStrategy()).Times(1);
+    EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
+
+    mOutput.prepareFrame();
+}
+
+// Note: Use OutputTest and not OutputPrepareFrameTest, so the real
+// base chooseCompositionStrategy() is invoked.
+TEST_F(OutputTest, prepareFrameSetsClientCompositionOnlyByDefault) {
+    mOutput->editState().isEnabled = true;
+    mOutput->editState().usesClientComposition = false;
+    mOutput->editState().usesDeviceComposition = true;
+
+    EXPECT_CALL(*mRenderSurface, prepareFrame(true, false));
+
+    mOutput->prepareFrame();
+
+    EXPECT_TRUE(mOutput->getState().usesClientComposition);
+    EXPECT_FALSE(mOutput->getState().usesDeviceComposition);
+}
+
+/*
+ * Output::prepare()
+ */
+
+struct OutputPrepareTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD2(rebuildLayerStacks,
+                     void(const compositionengine::CompositionRefreshArgs&,
+                          compositionengine::LayerFESet&));
+    };
+
+    StrictMock<OutputPartialMock> mOutput;
+    CompositionRefreshArgs mRefreshArgs;
+    LayerFESet mGeomSnapshots;
+};
+
+TEST_F(OutputPrepareTest, justInvokesRebuildLayerStacks) {
+    InSequence seq;
+    EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+
+    mOutput.prepare(mRefreshArgs, mGeomSnapshots);
+}
+
+/*
+ * Output::rebuildLayerStacks()
+ */
+
+struct OutputRebuildLayerStacksTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD2(collectVisibleLayers,
+                     void(const compositionengine::CompositionRefreshArgs&,
+                          compositionengine::Output::CoverageState&));
+    };
+
+    OutputRebuildLayerStacksTest() {
+        mOutput.mState.isEnabled = true;
+        mOutput.mState.transform = kIdentityTransform;
+        mOutput.mState.bounds = kOutputBounds;
+
+        mRefreshArgs.updatingOutputGeometryThisFrame = true;
+
+        mCoverageAboveCoveredLayersToSet = Region(Rect(0, 0, 10, 10));
+
+        EXPECT_CALL(mOutput, collectVisibleLayers(Ref(mRefreshArgs), _))
+                .WillRepeatedly(Invoke(this, &OutputRebuildLayerStacksTest::setTestCoverageValues));
     }
+
+    void setTestCoverageValues(const CompositionRefreshArgs&,
+                               compositionengine::Output::CoverageState& state) {
+        state.aboveCoveredLayers = mCoverageAboveCoveredLayersToSet;
+        state.aboveOpaqueLayers = mCoverageAboveOpaqueLayersToSet;
+        state.dirtyRegion = mCoverageDirtyRegionToSet;
+    }
+
+    static const ui::Transform kIdentityTransform;
+    static const ui::Transform kRotate90Transform;
+    static const Rect kOutputBounds;
+
+    StrictMock<OutputPartialMock> mOutput;
+    CompositionRefreshArgs mRefreshArgs;
+    LayerFESet mGeomSnapshots;
+    Region mCoverageAboveCoveredLayersToSet;
+    Region mCoverageAboveOpaqueLayersToSet;
+    Region mCoverageDirtyRegionToSet;
+};
+
+const ui::Transform OutputRebuildLayerStacksTest::kIdentityTransform{TR_IDENT, 1920, 1080};
+const ui::Transform OutputRebuildLayerStacksTest::kRotate90Transform{TR_ROT_90, 1920, 1080};
+const Rect OutputRebuildLayerStacksTest::kOutputBounds{0, 0, 1920, 1080};
+
+TEST_F(OutputRebuildLayerStacksTest, doesNothingIfNotEnabled) {
+    mOutput.mState.isEnabled = false;
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputRebuildLayerStacksTest, doesNothingIfNotUpdatingGeometryThisFrame) {
+    mRefreshArgs.updatingOutputGeometryThisFrame = false;
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWithNoRotationAndFullCoverage) {
+    mOutput.mState.transform = kIdentityTransform;
+
+    mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1920, 1080));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 0, 0))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWithNoRotationAndPartialCoverage) {
+    mOutput.mState.transform = kIdentityTransform;
+
+    mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 960, 1080));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(960, 0, 1920, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWith90RotationAndFullCoverage) {
+    mOutput.mState.transform = kRotate90Transform;
+
+    mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1080, 1920));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 0, 0))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWith90RotationAndPartialCoverage) {
+    mOutput.mState.transform = kRotate90Transform;
+
+    mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1080, 960));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 960, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, addsToDirtyRegionWithNoRotation) {
+    mOutput.mState.transform = kIdentityTransform;
+    mOutput.mState.dirtyRegion = Region(Rect(960, 0, 1920, 1080));
+
+    mCoverageDirtyRegionToSet = Region(Rect(0, 0, 960, 1080));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.dirtyRegion, RegionEq(Region(Rect(0, 0, 1920, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, addsToDirtyRegionWith90Rotation) {
+    mOutput.mState.transform = kRotate90Transform;
+    mOutput.mState.dirtyRegion = Region(Rect(0, 960, 1080, 1920));
+
+    mCoverageDirtyRegionToSet = Region(Rect(0, 0, 1080, 960));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.dirtyRegion, RegionEq(Region(Rect(0, 0, 1080, 1920))));
+}
+
+/*
+ * Output::collectVisibleLayers()
+ */
+
+struct OutputCollectVisibleLayersTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD2(ensureOutputLayerIfVisible,
+                     void(sp<compositionengine::LayerFE>&,
+                          compositionengine::Output::CoverageState&));
+        MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD0(finalizePendingOutputLayers, void());
+    };
+
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+            EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
+        }
+
+        StrictMock<mock::OutputLayer> outputLayer;
+        impl::OutputLayerCompositionState outputLayerState;
+        sp<StrictMock<mock::LayerFE>> layerFE{new StrictMock<mock::LayerFE>()};
+    };
+
+    OutputCollectVisibleLayersTest() {
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+                .WillRepeatedly(Return(&mLayer1.outputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+                .WillRepeatedly(Return(&mLayer2.outputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2))
+                .WillRepeatedly(Return(&mLayer3.outputLayer));
+
+        mRefreshArgs.layers.push_back(mLayer1.layerFE);
+        mRefreshArgs.layers.push_back(mLayer2.layerFE);
+        mRefreshArgs.layers.push_back(mLayer3.layerFE);
+    }
+
+    StrictMock<OutputPartialMock> mOutput;
+    CompositionRefreshArgs mRefreshArgs;
+    LayerFESet mGeomSnapshots;
+    Output::CoverageState mCoverageState{mGeomSnapshots};
+    Layer mLayer1;
+    Layer mLayer2;
+    Layer mLayer3;
+};
+
+TEST_F(OutputCollectVisibleLayersTest, doesMinimalWorkIfNoLayers) {
+    mRefreshArgs.layers.clear();
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+
+    EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs)));
+    EXPECT_CALL(mOutput, finalizePendingOutputLayers());
+
+    mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState);
+}
+
+TEST_F(OutputCollectVisibleLayersTest, processesCandidateLayersReversedAndSetsOutputLayerZ) {
+    // Enforce a call order sequence for this test.
+    InSequence seq;
+
+    // Layer coverage is evaluated from front to back!
+    EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer3.layerFE), Ref(mCoverageState)));
+    EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer2.layerFE), Ref(mCoverageState)));
+    EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer1.layerFE), Ref(mCoverageState)));
+
+    EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs)));
+    EXPECT_CALL(mOutput, finalizePendingOutputLayers());
+
+    mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState);
+
+    // Ensure all output layers have been assigned a simple/flattened z-order.
+    EXPECT_EQ(0u, mLayer1.outputLayerState.z);
+    EXPECT_EQ(1u, mLayer2.outputLayerState.z);
+    EXPECT_EQ(2u, mLayer3.outputLayerState.z);
+}
+
+/*
+ * Output::ensureOutputLayerIfVisible()
+ */
+
+struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&));
+        MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t));
+        MOCK_METHOD2(ensureOutputLayer,
+                     compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
+    };
+
+    OutputEnsureOutputLayerIfVisibleTest() {
+        EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE)))
+                .WillRepeatedly(Return(true));
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+                .WillRepeatedly(Return(&mLayer.outputLayer));
+
+        mOutput.mState.bounds = Rect(0, 0, 200, 300);
+        mOutput.mState.viewport = Rect(0, 0, 200, 300);
+        mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
+
+        mLayer.layerFEState.isVisible = true;
+        mLayer.layerFEState.isOpaque = true;
+        mLayer.layerFEState.contentDirty = true;
+        mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
+        mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+        mLayer.layerFEState.transparentRegionHint = Region(Rect(0, 0, 100, 100));
+
+        mLayer.outputLayerState.visibleRegion = Region(Rect(0, 0, 50, 200));
+        mLayer.outputLayerState.coveredRegion = Region(Rect(50, 0, 100, 200));
+
+        mGeomSnapshots.insert(mLayer.layerFE);
+    }
+
+    void ensureOutputLayerIfVisible() {
+        sp<LayerFE> layerFE(mLayer.layerFE);
+        mOutput.ensureOutputLayerIfVisible(layerFE, mCoverageState);
+    }
+
+    static const Region kEmptyRegion;
+    static const Region kFullBoundsNoRotation;
+    static const Region kRightHalfBoundsNoRotation;
+    static const Region kLowerHalfBoundsNoRotation;
+    static const Region kFullBounds90Rotation;
+
+    StrictMock<OutputPartialMock> mOutput;
+    LayerFESet mGeomSnapshots;
+    Output::CoverageState mCoverageState{mGeomSnapshots};
+
+    NonInjectedLayer mLayer;
+};
+
+const Region OutputEnsureOutputLayerIfVisibleTest::kEmptyRegion = Region(Rect(0, 0, 0, 0));
+const Region OutputEnsureOutputLayerIfVisibleTest::kFullBoundsNoRotation =
+        Region(Rect(0, 0, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kRightHalfBoundsNoRotation =
+        Region(Rect(0, 100, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kLowerHalfBoundsNoRotation =
+        Region(Rect(50, 0, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation =
+        Region(Rect(0, 0, 200, 100));
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerBelongs) {
+    EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer.layerFE,
+                prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry));
+
+    mGeomSnapshots.clear();
+
+    ensureOutputLayerIfVisible();
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerBelongs) {
+    EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
+
+    ensureOutputLayerIfVisible();
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerHasNoCompositionState) {
+    EXPECT_CALL(*mLayer.layerFE, getCompositionState()).WillOnce(Return(nullptr));
+
+    ensureOutputLayerIfVisible();
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerNotVisible) {
+    mLayer.layerFEState.isVisible = false;
+
+    ensureOutputLayerIfVisible();
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerHasEmptyVisibleRegion) {
+    mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 0, 0};
+
+    ensureOutputLayerIfVisible();
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
+    mOutput.mState.bounds = Rect(0, 0, 0, 0);
+
+    ensureOutputLayerIfVisible();
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayer) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayer) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForTransparentDirtyNotRotatedLayer) {
+    mLayer.layerFEState.isOpaque = false;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kRightHalfBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForTransparentDirtyNotRotatedLayer) {
+    mLayer.layerFEState.isOpaque = false;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kRightHalfBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = false;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = false;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kLowerHalfBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueDirtyRotated90Layer) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
+    mLayer.outputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
+    mLayer.outputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForOpaqueDirtyRotated90Layer) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
+    mLayer.outputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
+    mLayer.outputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    mOutput.mState.viewport = Rect(0, 0, 300, 200);
+    mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    mOutput.mState.viewport = Rect(0, 0, 300, 200);
+    mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueDirtyArbitraryTransformLayer) {
+    ui::Transform arbitraryTransform;
+    arbitraryTransform.set(1, 1, -1, 1);
+    arbitraryTransform.set(0, 100);
+
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
+    mLayer.layerFEState.geomLayerTransform = arbitraryTransform;
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    const Region kRegion = Region(Rect(0, 0, 300, 300));
+    const Region kRegionClipped = Region(Rect(0, 0, 200, 300));
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kRegion));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kRegion));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kRegion));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kRegion));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kRegionClipped));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesTest) {
+    mLayer.layerFEState.isOpaque = false;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+    mCoverageState.aboveCoveredLayers = Region(Rect(50, 0, 150, 200));
+    mCoverageState.aboveOpaqueLayers = Region(Rect(50, 0, 150, 200));
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    const Region kExpectedDirtyRegion = Region(Rect(0, 0, 500, 500));
+    const Region kExpectedAboveCoveredRegion = Region(Rect(0, 0, 150, 200));
+    const Region kExpectedAboveOpaqueRegion = Region(Rect(50, 0, 150, 200));
+    const Region kExpectedLayerVisibleRegion = Region(Rect(0, 0, 50, 200));
+    const Region kExpectedLayerCoveredRegion = Region(Rect(50, 0, 100, 200));
+    const Region kExpectedLayerVisibleNonTransparentRegion = Region(Rect(0, 100, 50, 200));
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kExpectedDirtyRegion));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kExpectedAboveCoveredRegion));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kExpectedAboveOpaqueRegion));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kExpectedLayerVisibleNonTransparentRegion));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion,
+                RegionEq(kExpectedLayerVisibleRegion));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesWithShadowsTest) {
+    ui::Transform translate;
+    translate.set(50, 50);
+    mLayer.layerFEState.geomLayerTransform = translate;
+    mLayer.layerFEState.shadowRadius = 10.0f;
+
+    mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+    // half of the layer including the casting shadow is covered and opaque
+    mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 100, 260));
+    mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 100, 260));
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    const Region kExpectedDirtyRegion = Region(Rect(0, 0, 500, 500));
+    const Region kExpectedAboveCoveredRegion = Region(Rect(40, 40, 160, 260));
+    // add starting opaque region to the opaque half of the casting layer bounds
+    const Region kExpectedAboveOpaqueRegion =
+            Region(Rect(40, 40, 100, 260)).orSelf(Rect(100, 50, 150, 250));
+    const Region kExpectedLayerVisibleRegion = Region(Rect(100, 40, 160, 260));
+    const Region kExpectedoutputSpaceLayerVisibleRegion = Region(Rect(100, 50, 150, 250));
+    const Region kExpectedLayerCoveredRegion = Region(Rect(40, 40, 100, 260));
+    const Region kExpectedLayerVisibleNonTransparentRegion = Region(Rect(100, 40, 160, 260));
+    const Region kExpectedLayerShadowRegion =
+            Region(Rect(40, 40, 160, 260)).subtractSelf(Rect(50, 50, 150, 250));
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kExpectedDirtyRegion));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kExpectedAboveCoveredRegion));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kExpectedAboveOpaqueRegion));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kExpectedLayerVisibleNonTransparentRegion));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion,
+                RegionEq(kExpectedoutputSpaceLayerVisibleRegion));
+    EXPECT_THAT(mLayer.outputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
+    EXPECT_FALSE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty());
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, shadowRegionOnlyTest) {
+    ui::Transform translate;
+    translate.set(50, 50);
+    mLayer.layerFEState.geomLayerTransform = translate;
+    mLayer.layerFEState.shadowRadius = 10.0f;
+
+    mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+    // Casting layer is covered by an opaque region leaving only part of its shadow to be drawn
+    mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 150, 260));
+    mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 150, 260));
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    const Region kExpectedLayerVisibleRegion = Region(Rect(150, 40, 160, 260));
+    const Region kExpectedLayerShadowRegion =
+            Region(Rect(40, 40, 160, 260)).subtractSelf(Rect(50, 50, 150, 250));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+    EXPECT_THAT(mLayer.outputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
+    EXPECT_TRUE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty());
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifLayerWithShadowIsCovered) {
+    ui::Transform translate;
+    translate.set(50, 50);
+    mLayer.layerFEState.geomLayerTransform = translate;
+    mLayer.layerFEState.shadowRadius = 10.0f;
+
+    mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+    // Casting layer and its shadows are covered by an opaque region
+    mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 160, 260));
+    mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 160, 260));
+
+    ensureOutputLayerIfVisible();
+}
+
+/*
+ * Output::present()
+ */
+
+struct OutputPresentTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD1(updateAndWriteCompositionState,
+                     void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD0(beginFrame, void());
+        MOCK_METHOD0(prepareFrame, void());
+        MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD0(postFramebuffer, void());
+    };
+
+    StrictMock<OutputPartialMock> mOutput;
+};
+
+TEST_F(OutputPresentTest, justInvokesChildFunctionsInSequence) {
+    CompositionRefreshArgs args;
+
+    InSequence seq;
+    EXPECT_CALL(mOutput, updateColorProfile(Ref(args)));
+    EXPECT_CALL(mOutput, updateAndWriteCompositionState(Ref(args)));
+    EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
+    EXPECT_CALL(mOutput, beginFrame());
+    EXPECT_CALL(mOutput, prepareFrame());
+    EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
+    EXPECT_CALL(mOutput, finishFrame(Ref(args)));
+    EXPECT_CALL(mOutput, postFramebuffer());
+
+    mOutput.present(args);
+}
+
+/*
+ * Output::updateColorProfile()
+ */
+
+struct OutputUpdateColorProfileTest : public testing::Test {
+    using TestType = OutputUpdateColorProfileTest;
+
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
+    };
+
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
+            EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+        }
+
+        StrictMock<mock::OutputLayer> mOutputLayer;
+        StrictMock<mock::LayerFE> mLayerFE;
+        LayerFECompositionState mLayerFEState;
+    };
+
+    OutputUpdateColorProfileTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+                .WillRepeatedly(Return(&mLayer1.mOutputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+                .WillRepeatedly(Return(&mLayer2.mOutputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2))
+                .WillRepeatedly(Return(&mLayer3.mOutputLayer));
+    }
+
+    struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
+        void execute() { getInstance()->mOutput.updateColorProfile(getInstance()->mRefreshArgs); }
+    };
+
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    StrictMock<OutputPartialMock> mOutput;
+
+    Layer mLayer1;
+    Layer mLayer2;
+    Layer mLayer3;
+
+    CompositionRefreshArgs mRefreshArgs;
+};
+
+// TODO(b/144522012): Refactor Output::updateColorProfile and the related code
+// to make it easier to write unit tests.
+
+TEST_F(OutputUpdateColorProfileTest, setsAColorProfileWhenUnmanaged) {
+    // When the outputColorSetting is set to kUnmanaged, the implementation sets
+    // a simple default color profile without looking at anything else.
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
+    EXPECT_CALL(mOutput,
+                setColorProfile(ColorProfileEq(
+                        ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+                                     ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN})));
+
+    mRefreshArgs.outputColorSetting = OutputColorSetting::kUnmanaged;
+    mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+
+    mOutput.updateColorProfile(mRefreshArgs);
+}
+
+struct OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile
+      : public OutputUpdateColorProfileTest {
+    OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile() {
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+        mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+        mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+    }
+
+    struct ExpectBestColorModeCallResultUsedToSetColorProfileState
+          : public CallOrderStateMachineHelper<
+                    TestType, ExpectBestColorModeCallResultUsedToSetColorProfileState> {
+        [[nodiscard]] auto expectBestColorModeCallResultUsedToSetColorProfile(
+                ui::ColorMode colorMode, ui::Dataspace dataspace, ui::RenderIntent renderIntent) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+                        getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _,
+                                         _))
+                    .WillOnce(DoAll(SetArgPointee<2>(dataspace), SetArgPointee<3>(colorMode),
+                                    SetArgPointee<4>(renderIntent)));
+            EXPECT_CALL(getInstance()->mOutput,
+                        setColorProfile(
+                                ColorProfileEq(ColorProfile{colorMode, dataspace, renderIntent,
+                                                            ui::Dataspace::UNKNOWN})));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() {
+        return ExpectBestColorModeCallResultUsedToSetColorProfileState::make(this);
+    }
+};
+
+TEST_F(OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile,
+       Native_Unknown_Colorimetric_Set) {
+    verify().expectBestColorModeCallResultUsedToSetColorProfile(ui::ColorMode::NATIVE,
+                                                                ui::Dataspace::UNKNOWN,
+                                                                ui::RenderIntent::COLORIMETRIC)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile,
+       DisplayP3_DisplayP3_Enhance_Set) {
+    verify().expectBestColorModeCallResultUsedToSetColorProfile(ui::ColorMode::DISPLAY_P3,
+                                                                ui::Dataspace::DISPLAY_P3,
+                                                                ui::RenderIntent::ENHANCE)
+            .execute();
+}
+
+struct OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile
+      : public OutputUpdateColorProfileTest {
+    OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile() {
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+        EXPECT_CALL(*mDisplayColorProfile,
+                    getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _, _))
+                .WillRepeatedly(DoAll(SetArgPointee<2>(ui::Dataspace::UNKNOWN),
+                                      SetArgPointee<3>(ui::ColorMode::NATIVE),
+                                      SetArgPointee<4>(ui::RenderIntent::COLORIMETRIC)));
+        mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+    }
+
+    struct IfColorSpaceAgnosticDataspaceSetToState
+          : public CallOrderStateMachineHelper<TestType, IfColorSpaceAgnosticDataspaceSetToState> {
+        [[nodiscard]] auto ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace dataspace) {
+            getInstance()->mRefreshArgs.colorSpaceAgnosticDataspace = dataspace;
+            return nextState<ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState>();
+        }
+    };
+
+    struct ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState
+          : public CallOrderStateMachineHelper<
+                    TestType, ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState> {
+        [[nodiscard]] auto thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(
+                ui::Dataspace dataspace) {
+            EXPECT_CALL(getInstance()->mOutput,
+                        setColorProfile(ColorProfileEq(
+                                ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+                                             ui::RenderIntent::COLORIMETRIC, dataspace})));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() { return IfColorSpaceAgnosticDataspaceSetToState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, DisplayP3) {
+    verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::DISPLAY_P3)
+            .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, V0_SRGB) {
+    verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::V0_SRGB)
+            .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::V0_SRGB)
+            .execute();
+}
+
+struct OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference
+      : public OutputUpdateColorProfileTest {
+    // Internally the implementation looks through the dataspaces of all the
+    // visible layers. The topmost one that also has an actual dataspace
+    // preference set is used to drive subsequent choices.
+
+    OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference() {
+        mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+        mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
+        EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+    }
+
+    struct IfTopLayerDataspaceState
+          : public CallOrderStateMachineHelper<TestType, IfTopLayerDataspaceState> {
+        [[nodiscard]] auto ifTopLayerIs(ui::Dataspace dataspace) {
+            getInstance()->mLayer3.mLayerFEState.dataspace = dataspace;
+            return nextState<AndIfMiddleLayerDataspaceState>();
+        }
+        [[nodiscard]] auto ifTopLayerHasNoPreference() {
+            return ifTopLayerIs(ui::Dataspace::UNKNOWN);
+        }
+    };
+
+    struct AndIfMiddleLayerDataspaceState
+          : public CallOrderStateMachineHelper<TestType, AndIfMiddleLayerDataspaceState> {
+        [[nodiscard]] auto andIfMiddleLayerIs(ui::Dataspace dataspace) {
+            getInstance()->mLayer2.mLayerFEState.dataspace = dataspace;
+            return nextState<AndIfBottomLayerDataspaceState>();
+        }
+        [[nodiscard]] auto andIfMiddleLayerHasNoPreference() {
+            return andIfMiddleLayerIs(ui::Dataspace::UNKNOWN);
+        }
+    };
+
+    struct AndIfBottomLayerDataspaceState
+          : public CallOrderStateMachineHelper<TestType, AndIfBottomLayerDataspaceState> {
+        [[nodiscard]] auto andIfBottomLayerIs(ui::Dataspace dataspace) {
+            getInstance()->mLayer1.mLayerFEState.dataspace = dataspace;
+            return nextState<ThenExpectBestColorModeCallUsesState>();
+        }
+        [[nodiscard]] auto andIfBottomLayerHasNoPreference() {
+            return andIfBottomLayerIs(ui::Dataspace::UNKNOWN);
+        }
+    };
+
+    struct ThenExpectBestColorModeCallUsesState
+          : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+        [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+                        getBestColorMode(dataspace, _, _, _, _));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() { return IfTopLayerDataspaceState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+       noStrongLayerPrefenceUses_V0_SRGB) {
+    // If none of the layers indicate a preference, then V0_SRGB is the
+    // preferred choice (subject to additional checks).
+    verify().ifTopLayerHasNoPreference()
+            .andIfMiddleLayerHasNoPreference()
+            .andIfBottomLayerHasNoPreference()
+            .thenExpectBestColorModeCallUses(ui::Dataspace::V0_SRGB)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+       ifTopmostUses_DisplayP3_Then_DisplayP3_Chosen) {
+    // If only the topmost layer has a preference, then that is what is chosen.
+    verify().ifTopLayerIs(ui::Dataspace::DISPLAY_P3)
+            .andIfMiddleLayerHasNoPreference()
+            .andIfBottomLayerHasNoPreference()
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+       ifMiddleUses_DisplayP3_Then_DisplayP3_Chosen) {
+    // If only the middle layer has a preference, that that is what is chosen.
+    verify().ifTopLayerHasNoPreference()
+            .andIfMiddleLayerIs(ui::Dataspace::DISPLAY_P3)
+            .andIfBottomLayerHasNoPreference()
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+       ifBottomUses_DisplayP3_Then_DisplayP3_Chosen) {
+    // If only the middle layer has a preference, that that is what is chosen.
+    verify().ifTopLayerHasNoPreference()
+            .andIfMiddleLayerHasNoPreference()
+            .andIfBottomLayerIs(ui::Dataspace::DISPLAY_P3)
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+       ifTopUses_DisplayBT2020_AndBottomUses_DisplayP3_Then_DisplayBT2020_Chosen) {
+    // If multiple layers have a preference, the topmost value is what is used.
+    verify().ifTopLayerIs(ui::Dataspace::DISPLAY_BT2020)
+            .andIfMiddleLayerHasNoPreference()
+            .andIfBottomLayerIs(ui::Dataspace::DISPLAY_P3)
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_BT2020)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+       ifTopUses_DisplayP3_AndBottomUses_V0_SRGB_Then_DisplayP3_Chosen) {
+    // If multiple layers have a preference, the topmost value is what is used.
+    verify().ifTopLayerIs(ui::Dataspace::DISPLAY_P3)
+            .andIfMiddleLayerHasNoPreference()
+            .andIfBottomLayerIs(ui::Dataspace::DISPLAY_BT2020)
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+            .execute();
+}
+
+struct OutputUpdateColorProfileTest_ForceOutputColorOverrides
+      : public OutputUpdateColorProfileTest {
+    // If CompositionRefreshArgs::forceOutputColorMode is set to some specific
+    // values, it overrides the layer dataspace choice.
+
+    OutputUpdateColorProfileTest_ForceOutputColorOverrides() {
+        mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+        mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+
+        mLayer1.mLayerFEState.dataspace = ui::Dataspace::DISPLAY_BT2020;
+
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
+        EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+    }
+
+    struct IfForceOutputColorModeState
+          : public CallOrderStateMachineHelper<TestType, IfForceOutputColorModeState> {
+        [[nodiscard]] auto ifForceOutputColorMode(ui::ColorMode colorMode) {
+            getInstance()->mRefreshArgs.forceOutputColorMode = colorMode;
+            return nextState<ThenExpectBestColorModeCallUsesState>();
+        }
+        [[nodiscard]] auto ifNoOverride() { return ifForceOutputColorMode(ui::ColorMode::NATIVE); }
+    };
+
+    struct ThenExpectBestColorModeCallUsesState
+          : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+        [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+                        getBestColorMode(dataspace, _, _, _, _));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() { return IfForceOutputColorModeState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, NoOverride_DoesNotOverride) {
+    // By default the layer state is used to set the preferred dataspace
+    verify().ifNoOverride()
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_BT2020)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, SRGB_Override_USES_V0_SRGB) {
+    // Setting ui::ColorMode::SRGB overrides it with ui::Dataspace::V0_SRGB
+    verify().ifForceOutputColorMode(ui::ColorMode::SRGB)
+            .thenExpectBestColorModeCallUses(ui::Dataspace::V0_SRGB)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, DisplayP3_Override_Uses_DisplayP3) {
+    // Setting ui::ColorMode::DISPLAY_P3 overrides it with ui::Dataspace::DISPLAY_P3
+    verify().ifForceOutputColorMode(ui::ColorMode::DISPLAY_P3)
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+            .execute();
+}
+
+// HDR output requires all layers to be compatible with the chosen HDR
+// dataspace, along with there being proper support.
+struct OutputUpdateColorProfileTest_Hdr : public OutputUpdateColorProfileTest {
+    OutputUpdateColorProfileTest_Hdr() {
+        mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+        mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+        EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+    }
+
+    static constexpr ui::Dataspace kNonHdrDataspace = ui::Dataspace::DISPLAY_P3;
+    static constexpr ui::Dataspace BT2020_PQ = ui::Dataspace::BT2020_PQ;
+    static constexpr ui::Dataspace BT2020_HLG = ui::Dataspace::BT2020_HLG;
+    static constexpr ui::Dataspace DISPLAY_P3 = ui::Dataspace::DISPLAY_P3;
+
+    struct IfTopLayerDataspaceState
+          : public CallOrderStateMachineHelper<TestType, IfTopLayerDataspaceState> {
+        [[nodiscard]] auto ifTopLayerIs(ui::Dataspace dataspace) {
+            getInstance()->mLayer2.mLayerFEState.dataspace = dataspace;
+            return nextState<AndTopLayerCompositionTypeState>();
+        }
+        [[nodiscard]] auto ifTopLayerIsNotHdr() { return ifTopLayerIs(kNonHdrDataspace); }
+    };
+
+    struct AndTopLayerCompositionTypeState
+          : public CallOrderStateMachineHelper<TestType, AndTopLayerCompositionTypeState> {
+        [[nodiscard]] auto andTopLayerIsREComposed(bool renderEngineComposed) {
+            getInstance()->mLayer2.mLayerFEState.forceClientComposition = renderEngineComposed;
+            return nextState<AndIfBottomLayerDataspaceState>();
+        }
+    };
+
+    struct AndIfBottomLayerDataspaceState
+          : public CallOrderStateMachineHelper<TestType, AndIfBottomLayerDataspaceState> {
+        [[nodiscard]] auto andIfBottomLayerIs(ui::Dataspace dataspace) {
+            getInstance()->mLayer1.mLayerFEState.dataspace = dataspace;
+            return nextState<AndBottomLayerCompositionTypeState>();
+        }
+        [[nodiscard]] auto andIfBottomLayerIsNotHdr() {
+            return andIfBottomLayerIs(kNonHdrDataspace);
+        }
+    };
+
+    struct AndBottomLayerCompositionTypeState
+          : public CallOrderStateMachineHelper<TestType, AndBottomLayerCompositionTypeState> {
+        [[nodiscard]] auto andBottomLayerIsREComposed(bool renderEngineComposed) {
+            getInstance()->mLayer1.mLayerFEState.forceClientComposition = renderEngineComposed;
+            return nextState<AndIfHasLegacySupportState>();
+        }
+    };
+
+    struct AndIfHasLegacySupportState
+          : public CallOrderStateMachineHelper<TestType, AndIfHasLegacySupportState> {
+        [[nodiscard]] auto andIfLegacySupportFor(ui::Dataspace dataspace, bool legacySupport) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile, hasLegacyHdrSupport(dataspace))
+                    .WillOnce(Return(legacySupport));
+            return nextState<ThenExpectBestColorModeCallUsesState>();
+        }
+    };
+
+    struct ThenExpectBestColorModeCallUsesState
+          : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+        [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+                        getBestColorMode(dataspace, _, _, _, _));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() { return IfTopLayerDataspaceState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_HW_Uses_PQ) {
+    // If all layers use BT2020_PQ, and there are no other special conditions,
+    // BT2020_PQ is used.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+    // BT2020_PQ is not used if there is only legacy support for it.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, true)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_RE_Uses_PQ) {
+    // BT2020_PQ is still used if the bottom layer is RenderEngine composed.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(true)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_RE_On_PQ_HW_Uses_DisplayP3) {
+    // BT2020_PQ is not used if the top layer is RenderEngine composed.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(true)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_HW_Uses_PQ) {
+    // If there is mixed HLG/PQ use, and the topmost layer is PQ, then PQ is used if there
+    // are no other special conditions.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+    // BT2020_PQ is not used if there is only legacy support for it.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, true)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_RE_Uses_PQ) {
+    // BT2020_PQ is used if the bottom HLG layer is RenderEngine composed.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(true)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_RE_On_HLG_HW_Uses_DisplayP3) {
+    // BT2020_PQ is not used if the top PQ layer is RenderEngine composed.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(true)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_HW_Uses_PQ) {
+    // If there is mixed HLG/PQ use, and the topmost layer is HLG, then PQ is
+    // used if there are no other special conditions.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+    // BT2020_PQ is not used if there is only legacy support for it.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, true)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_RE_Uses_DisplayP3) {
+    // BT2020_PQ is not used if the bottom PQ layer is RenderEngine composed.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(true)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_RE_On_PQ_HW_Uses_PQ) {
+    // BT2020_PQ is still used if the top HLG layer is RenderEngine composed.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(true)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_HW_Uses_HLG) {
+    // If all layers use HLG then HLG is used if there are no other special
+    // conditions.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_HLG, false)
+            .thenExpectBestColorModeCallUses(BT2020_HLG)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+    // BT2020_HLG is not used if there is legacy support for it.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_HLG, true)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_RE_Uses_HLG) {
+    // BT2020_HLG is used even if the bottom layer is client composed.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(true)
+            .andIfLegacySupportFor(BT2020_HLG, false)
+            .thenExpectBestColorModeCallUses(BT2020_HLG)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_RE_On_HLG_HW_Uses_HLG) {
+    // BT2020_HLG is used even if the top layer is client composed.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(true)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_HLG, false)
+            .thenExpectBestColorModeCallUses(BT2020_HLG)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_NonHdr_HW_Uses_PQ) {
+    // Even if there are non-HDR layers present, BT2020_PQ can still be used.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIsNotHdr()
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_NonHdr_RE_Uses_HLG) {
+    // If all layers use HLG then HLG is used if there are no other special
+    // conditions.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIsNotHdr()
+            .andBottomLayerIsREComposed(true)
+            .andIfLegacySupportFor(BT2020_HLG, false)
+            .thenExpectBestColorModeCallUses(BT2020_HLG)
+            .execute();
+}
+
+struct OutputUpdateColorProfile_AffectsChosenRenderIntentTest
+      : public OutputUpdateColorProfileTest {
+    // The various values for CompositionRefreshArgs::outputColorSetting affect
+    // the chosen renderIntent, along with whether the preferred dataspace is an
+    // HDR dataspace or not.
+
+    OutputUpdateColorProfile_AffectsChosenRenderIntentTest() {
+        mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+        mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+        mLayer1.mLayerFEState.dataspace = ui::Dataspace::BT2020_PQ;
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
+        EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+        EXPECT_CALL(*mDisplayColorProfile, hasLegacyHdrSupport(ui::Dataspace::BT2020_PQ))
+                .WillRepeatedly(Return(false));
+    }
+
+    // The tests here involve enough state and GMock setup that using a mini-DSL
+    // makes the tests much more readable, and allows the test to focus more on
+    // the intent than on some of the details.
+
+    static constexpr ui::Dataspace kNonHdrDataspace = ui::Dataspace::DISPLAY_P3;
+    static constexpr ui::Dataspace kHdrDataspace = ui::Dataspace::BT2020_PQ;
+
+    struct IfDataspaceChosenState
+          : public CallOrderStateMachineHelper<TestType, IfDataspaceChosenState> {
+        [[nodiscard]] auto ifDataspaceChosenIs(ui::Dataspace dataspace) {
+            getInstance()->mLayer1.mLayerFEState.dataspace = dataspace;
+            return nextState<AndOutputColorSettingState>();
+        }
+        [[nodiscard]] auto ifDataspaceChosenIsNonHdr() {
+            return ifDataspaceChosenIs(kNonHdrDataspace);
+        }
+        [[nodiscard]] auto ifDataspaceChosenIsHdr() { return ifDataspaceChosenIs(kHdrDataspace); }
+    };
+
+    struct AndOutputColorSettingState
+          : public CallOrderStateMachineHelper<TestType, AndOutputColorSettingState> {
+        [[nodiscard]] auto andOutputColorSettingIs(OutputColorSetting setting) {
+            getInstance()->mRefreshArgs.outputColorSetting = setting;
+            return nextState<ThenExpectBestColorModeCallUsesState>();
+        }
+    };
+
+    struct ThenExpectBestColorModeCallUsesState
+          : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+        [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::RenderIntent intent) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+                        getBestColorMode(getInstance()->mLayer1.mLayerFEState.dataspace, intent, _,
+                                         _, _));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Tests call one of these two helper member functions to start using the
+    // mini-DSL defined above.
+    [[nodiscard]] auto verify() { return IfDataspaceChosenState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest,
+       Managed_NonHdr_Prefers_Colorimetric) {
+    verify().ifDataspaceChosenIsNonHdr()
+            .andOutputColorSettingIs(OutputColorSetting::kManaged)
+            .thenExpectBestColorModeCallUses(ui::RenderIntent::COLORIMETRIC)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest,
+       Managed_Hdr_Prefers_ToneMapColorimetric) {
+    verify().ifDataspaceChosenIsHdr()
+            .andOutputColorSettingIs(OutputColorSetting::kManaged)
+            .thenExpectBestColorModeCallUses(ui::RenderIntent::TONE_MAP_COLORIMETRIC)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Enhanced_NonHdr_Prefers_Enhance) {
+    verify().ifDataspaceChosenIsNonHdr()
+            .andOutputColorSettingIs(OutputColorSetting::kEnhanced)
+            .thenExpectBestColorModeCallUses(ui::RenderIntent::ENHANCE)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest,
+       Enhanced_Hdr_Prefers_ToneMapEnhance) {
+    verify().ifDataspaceChosenIsHdr()
+            .andOutputColorSettingIs(OutputColorSetting::kEnhanced)
+            .thenExpectBestColorModeCallUses(ui::RenderIntent::TONE_MAP_ENHANCE)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Vendor_NonHdr_Prefers_Vendor) {
+    verify().ifDataspaceChosenIsNonHdr()
+            .andOutputColorSettingIs(kVendorSpecifiedOutputColorSetting)
+            .thenExpectBestColorModeCallUses(
+                    static_cast<ui::RenderIntent>(kVendorSpecifiedOutputColorSetting))
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Vendor_Hdr_Prefers_Vendor) {
+    verify().ifDataspaceChosenIsHdr()
+            .andOutputColorSettingIs(kVendorSpecifiedOutputColorSetting)
+            .thenExpectBestColorModeCallUses(
+                    static_cast<ui::RenderIntent>(kVendorSpecifiedOutputColorSetting))
+            .execute();
+}
+
+/*
+ * Output::beginFrame()
+ */
+
+struct OutputBeginFrameTest : public ::testing::Test {
+    using TestType = OutputBeginFrameTest;
+
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+    };
+
+    OutputBeginFrameTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+
+    struct IfGetDirtyRegionExpectationState
+          : public CallOrderStateMachineHelper<TestType, IfGetDirtyRegionExpectationState> {
+        [[nodiscard]] auto ifGetDirtyRegionReturns(Region dirtyRegion) {
+            EXPECT_CALL(getInstance()->mOutput, getDirtyRegion(false))
+                    .WillOnce(Return(dirtyRegion));
+            return nextState<AndIfGetOutputLayerCountExpectationState>();
+        }
+    };
+
+    struct AndIfGetOutputLayerCountExpectationState
+          : public CallOrderStateMachineHelper<TestType, AndIfGetOutputLayerCountExpectationState> {
+        [[nodiscard]] auto andIfGetOutputLayerCountReturns(size_t layerCount) {
+            EXPECT_CALL(getInstance()->mOutput, getOutputLayerCount()).WillOnce(Return(layerCount));
+            return nextState<AndIfLastCompositionHadVisibleLayersState>();
+        }
+    };
+
+    struct AndIfLastCompositionHadVisibleLayersState
+          : public CallOrderStateMachineHelper<TestType,
+                                               AndIfLastCompositionHadVisibleLayersState> {
+        [[nodiscard]] auto andIfLastCompositionHadVisibleLayersIs(bool hadOutputLayers) {
+            getInstance()->mOutput.mState.lastCompositionHadVisibleLayers = hadOutputLayers;
+            return nextState<ThenExpectRenderSurfaceBeginFrameCallState>();
+        }
+    };
+
+    struct ThenExpectRenderSurfaceBeginFrameCallState
+          : public CallOrderStateMachineHelper<TestType,
+                                               ThenExpectRenderSurfaceBeginFrameCallState> {
+        [[nodiscard]] auto thenExpectRenderSurfaceBeginFrameCall(bool mustRecompose) {
+            EXPECT_CALL(*getInstance()->mRenderSurface, beginFrame(mustRecompose));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
+        [[nodiscard]] auto execute() {
+            getInstance()->mOutput.beginFrame();
+            return nextState<CheckPostconditionHadVisibleLayersState>();
+        }
+    };
+
+    struct CheckPostconditionHadVisibleLayersState
+          : public CallOrderStateMachineHelper<TestType, CheckPostconditionHadVisibleLayersState> {
+        void checkPostconditionHadVisibleLayers(bool expected) {
+            EXPECT_EQ(expected, getInstance()->mOutput.mState.lastCompositionHadVisibleLayers);
+        }
+    };
+
+    // Tests call one of these two helper member functions to start using the
+    // mini-DSL defined above.
+    [[nodiscard]] auto verify() { return IfGetDirtyRegionExpectationState::make(this); }
+
+    static const Region kEmptyRegion;
+    static const Region kNotEmptyRegion;
+
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    StrictMock<OutputPartialMock> mOutput;
+};
+
+const Region OutputBeginFrameTest::kEmptyRegion{Rect{0, 0, 0, 0}};
+const Region OutputBeginFrameTest::kNotEmptyRegion{Rect{0, 0, 1, 1}};
+
+TEST_F(OutputBeginFrameTest, hasDirtyHasLayersHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+            .andIfGetOutputLayerCountReturns(1u)
+            .andIfLastCompositionHadVisibleLayersIs(true)
+            .thenExpectRenderSurfaceBeginFrameCall(true)
+            .execute()
+            .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, hasDirtyNotHasLayersHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+            .andIfGetOutputLayerCountReturns(0u)
+            .andIfLastCompositionHadVisibleLayersIs(true)
+            .thenExpectRenderSurfaceBeginFrameCall(true)
+            .execute()
+            .checkPostconditionHadVisibleLayers(false);
+}
+
+TEST_F(OutputBeginFrameTest, hasDirtyHasLayersNotHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+            .andIfGetOutputLayerCountReturns(1u)
+            .andIfLastCompositionHadVisibleLayersIs(false)
+            .thenExpectRenderSurfaceBeginFrameCall(true)
+            .execute()
+            .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, hasDirtyNotHasLayersNotHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+            .andIfGetOutputLayerCountReturns(0u)
+            .andIfLastCompositionHadVisibleLayersIs(false)
+            .thenExpectRenderSurfaceBeginFrameCall(false)
+            .execute()
+            .checkPostconditionHadVisibleLayers(false);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyHasLayersHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kEmptyRegion)
+            .andIfGetOutputLayerCountReturns(1u)
+            .andIfLastCompositionHadVisibleLayersIs(true)
+            .thenExpectRenderSurfaceBeginFrameCall(false)
+            .execute()
+            .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyNotHasLayersHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kEmptyRegion)
+            .andIfGetOutputLayerCountReturns(0u)
+            .andIfLastCompositionHadVisibleLayersIs(true)
+            .thenExpectRenderSurfaceBeginFrameCall(false)
+            .execute()
+            .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyHasLayersNotHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kEmptyRegion)
+            .andIfGetOutputLayerCountReturns(1u)
+            .andIfLastCompositionHadVisibleLayersIs(false)
+            .thenExpectRenderSurfaceBeginFrameCall(false)
+            .execute()
+            .checkPostconditionHadVisibleLayers(false);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyNotHasLayersNotHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kEmptyRegion)
+            .andIfGetOutputLayerCountReturns(0u)
+            .andIfLastCompositionHadVisibleLayersIs(false)
+            .thenExpectRenderSurfaceBeginFrameCall(false)
+            .execute()
+            .checkPostconditionHadVisibleLayers(false);
+}
+
+/*
+ * Output::devOptRepaintFlash()
+ */
+
+struct OutputDevOptRepaintFlashTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+        MOCK_METHOD2(composeSurfaces,
+                     std::optional<base::unique_fd>(
+                             const Region&, const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD0(postFramebuffer, void());
+        MOCK_METHOD0(prepareFrame, void());
+    };
+
+    OutputDevOptRepaintFlashTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+
+    static const Region kEmptyRegion;
+    static const Region kNotEmptyRegion;
+
+    StrictMock<OutputPartialMock> mOutput;
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    CompositionRefreshArgs mRefreshArgs;
+};
+
+const Region OutputDevOptRepaintFlashTest::kEmptyRegion{Rect{0, 0, 0, 0}};
+const Region OutputDevOptRepaintFlashTest::kNotEmptyRegion{Rect{0, 0, 1, 1}};
+
+TEST_F(OutputDevOptRepaintFlashTest, doesNothingIfFlashDelayNotSet) {
+    mRefreshArgs.devOptFlashDirtyRegionsDelay = {};
+    mRefreshArgs.repaintEverything = true;
+    mOutput.mState.isEnabled = true;
+
+    mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotEnabled) {
+    mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
+    mRefreshArgs.repaintEverything = true;
+    mOutput.mState.isEnabled = false;
+
+    InSequence seq;
+    EXPECT_CALL(mOutput, postFramebuffer());
+    EXPECT_CALL(mOutput, prepareFrame());
+
+    mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotDirty) {
+    mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
+    mRefreshArgs.repaintEverything = true;
+    mOutput.mState.isEnabled = true;
+
+    InSequence seq;
+    EXPECT_CALL(mOutput, getDirtyRegion(true)).WillOnce(Return(kEmptyRegion));
+    EXPECT_CALL(mOutput, postFramebuffer());
+    EXPECT_CALL(mOutput, prepareFrame());
+
+    mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+TEST_F(OutputDevOptRepaintFlashTest, alsoComposesSurfacesAndQueuesABufferIfDirty) {
+    mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
+    mRefreshArgs.repaintEverything = false;
+    mOutput.mState.isEnabled = true;
+
+    InSequence seq;
+    EXPECT_CALL(mOutput, getDirtyRegion(false)).WillOnce(Return(kNotEmptyRegion));
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), Ref(mRefreshArgs)));
+    EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+    EXPECT_CALL(mOutput, postFramebuffer());
+    EXPECT_CALL(mOutput, prepareFrame());
+
+    mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+/*
+ * Output::finishFrame()
+ */
+
+struct OutputFinishFrameTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD2(composeSurfaces,
+                     std::optional<base::unique_fd>(
+                             const Region&, const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD0(postFramebuffer, void());
+    };
+
+    OutputFinishFrameTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+
+    StrictMock<OutputPartialMock> mOutput;
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    CompositionRefreshArgs mRefreshArgs;
+};
+
+TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) {
+    mOutput.mState.isEnabled = false;
+
+    mOutput.finishFrame(mRefreshArgs);
+}
+
+TEST_F(OutputFinishFrameTest, takesEarlyOutifComposeSurfacesReturnsNoFence) {
+    mOutput.mState.isEnabled = true;
+
+    InSequence seq;
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _));
+
+    mOutput.finishFrame(mRefreshArgs);
+}
+
+TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) {
+    mOutput.mState.isEnabled = true;
+
+    InSequence seq;
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _))
+            .WillOnce(Return(ByMove(base::unique_fd())));
+    EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+
+    mOutput.finishFrame(mRefreshArgs);
+}
+
+/*
+ * Output::postFramebuffer()
+ */
+
+struct OutputPostFramebufferTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
+    };
+
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+            EXPECT_CALL(outputLayer, getHwcLayer()).WillRepeatedly(Return(&hwc2Layer));
+        }
+
+        StrictMock<mock::OutputLayer> outputLayer;
+        StrictMock<mock::LayerFE> layerFE;
+        StrictMock<HWC2::mock::Layer> hwc2Layer;
+    };
+
+    OutputPostFramebufferTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+                .WillRepeatedly(Return(&mLayer1.outputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+                .WillRepeatedly(Return(&mLayer2.outputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2u))
+                .WillRepeatedly(Return(&mLayer3.outputLayer));
+    }
+
+    StrictMock<OutputPartialMock> mOutput;
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+
+    Layer mLayer1;
+    Layer mLayer2;
+    Layer mLayer3;
+};
+
+TEST_F(OutputPostFramebufferTest, ifNotEnabledDoesNothing) {
+    mOutput.mState.isEnabled = false;
+
+    mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCompleted) {
+    mOutput.mState.isEnabled = true;
+
+    compositionengine::Output::FrameFences frameFences;
+
+    // This should happen even if there are no output layers.
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+    // For this test in particular we want to make sure the call expectations
+    // setup below are satisfied in the specific order.
+    InSequence seq;
+
+    EXPECT_CALL(*mRenderSurface, flip());
+    EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+    mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) {
+    // Simulate getting release fences from each layer, and ensure they are passed to the
+    // front-end layer interface for each layer correctly.
+
+    mOutput.mState.isEnabled = true;
+
+    // Create three unique fence instances
+    sp<Fence> layer1Fence = new Fence();
+    sp<Fence> layer2Fence = new Fence();
+    sp<Fence> layer3Fence = new Fence();
+
+    Output::FrameFences frameFences;
+    frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
+    frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
+    frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
+
+    EXPECT_CALL(*mRenderSurface, flip());
+    EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+    // Compare the pointers values of each fence to make sure the correct ones
+    // are passed. This happens to work with the current implementation, but
+    // would not survive certain calls like Fence::merge() which would return a
+    // new instance.
+    EXPECT_CALL(mLayer1.layerFE,
+                onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer1Fence.get()))));
+    EXPECT_CALL(mLayer2.layerFE,
+                onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer2Fence.get()))));
+    EXPECT_CALL(mLayer3.layerFE,
+                onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer3Fence.get()))));
+
+    mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
+    mOutput.mState.isEnabled = true;
+    mOutput.mState.usesClientComposition = true;
+
+    sp<Fence> clientTargetAcquireFence = new Fence();
+    sp<Fence> layer1Fence = new Fence();
+    sp<Fence> layer2Fence = new Fence();
+    sp<Fence> layer3Fence = new Fence();
+    Output::FrameFences frameFences;
+    frameFences.clientTargetAcquireFence = clientTargetAcquireFence;
+    frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
+    frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
+    frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
+
+    EXPECT_CALL(*mRenderSurface, flip());
+    EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+    // Fence::merge is called, and since none of the fences are actually valid,
+    // Fence::NO_FENCE is returned and passed to each onLayerDisplayed() call.
+    // This is the best we can do without creating a real kernel fence object.
+    EXPECT_CALL(mLayer1.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+    EXPECT_CALL(mLayer2.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+    EXPECT_CALL(mLayer3.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+
+    mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) {
+    mOutput.mState.isEnabled = true;
+    mOutput.mState.usesClientComposition = true;
+
+    // This should happen even if there are no (current) output layers.
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+    // Load up the released layers with some mock instances
+    sp<StrictMock<mock::LayerFE>> releasedLayer1{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> releasedLayer2{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> releasedLayer3{new StrictMock<mock::LayerFE>()};
+    Output::ReleasedLayers layers;
+    layers.push_back(releasedLayer1);
+    layers.push_back(releasedLayer2);
+    layers.push_back(releasedLayer3);
+    mOutput.setReleasedLayers(std::move(layers));
+
+    // Set up a fake present fence
+    sp<Fence> presentFence = new Fence();
+    Output::FrameFences frameFences;
+    frameFences.presentFence = presentFence;
+
+    EXPECT_CALL(*mRenderSurface, flip());
+    EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+    // Each released layer should be given the presentFence.
+    EXPECT_CALL(*releasedLayer1,
+                onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+    EXPECT_CALL(*releasedLayer2,
+                onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+    EXPECT_CALL(*releasedLayer3,
+                onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+
+    mOutput.postFramebuffer();
+
+    // After the call the list of released layers should have been cleared.
+    EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
+}
+
+/*
+ * Output::composeSurfaces()
+ */
+
+struct OutputComposeSurfacesTest : public testing::Test {
+    using TestType = OutputComposeSurfacesTest;
+
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_CONST_METHOD0(getSkipColorTransform, bool());
+        MOCK_METHOD3(generateClientCompositionRequests,
+                     std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace));
+        MOCK_METHOD2(appendRegionFlashRequests,
+                     void(const Region&, std::vector<LayerFE::LayerSettings>&));
+        MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
+    };
+
+    OutputComposeSurfacesTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+        mOutput.cacheClientCompositionRequests(MAX_CLIENT_COMPOSITION_CACHE_SIZE);
+
+        mOutput.mState.frame = kDefaultOutputFrame;
+        mOutput.mState.viewport = kDefaultOutputViewport;
+        mOutput.mState.sourceClip = kDefaultOutputSourceClip;
+        mOutput.mState.destinationClip = kDefaultOutputDestinationClip;
+        mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
+        mOutput.mState.orientation = kDefaultOutputOrientation;
+        mOutput.mState.dataspace = kDefaultOutputDataspace;
+        mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
+        mOutput.mState.isSecure = false;
+        mOutput.mState.needsFiltering = false;
+        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));
+        EXPECT_CALL(mCompositionEngine, getTimeStats())
+                .WillRepeatedly(ReturnRef(*mTimeStats.get()));
+        EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities())
+                .WillRepeatedly(ReturnRef(kHdrCapabilities));
+    }
+
+    struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
+        auto execute() {
+            getInstance()->mReadyFence =
+                    getInstance()->mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+            return nextState<FenceCheckState>();
+        }
+    };
+
+    struct FenceCheckState : public CallOrderStateMachineHelper<TestType, FenceCheckState> {
+        void expectNoFenceWasReturned() { EXPECT_FALSE(getInstance()->mReadyFence); }
+
+        void expectAFenceWasReturned() { EXPECT_TRUE(getInstance()->mReadyFence); }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() { return ExecuteState::make(this); }
+
+    static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT;
+    static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::UNKNOWN;
+    static constexpr ui::Dataspace kExpensiveOutputDataspace = ui::Dataspace::DISPLAY_P3;
+    static constexpr float kDefaultMaxLuminance = 0.9f;
+    static constexpr float kDefaultAvgLuminance = 0.7f;
+    static constexpr float kDefaultMinLuminance = 0.1f;
+
+    static const Rect kDefaultOutputFrame;
+    static const Rect kDefaultOutputViewport;
+    static const Rect kDefaultOutputSourceClip;
+    static const Rect kDefaultOutputDestinationClip;
+    static const mat4 kDefaultColorTransformMat;
+
+    static const Region kDebugRegion;
+    static const compositionengine::CompositionRefreshArgs kDefaultRefreshArgs;
+    static const HdrCapabilities kHdrCapabilities;
+
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
+    // TODO: make this is a proper mock.
+    std::shared_ptr<TimeStats> mTimeStats = std::make_shared<android::impl::TimeStats>();
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    StrictMock<OutputPartialMock> mOutput;
+    sp<GraphicBuffer> mOutputBuffer = new GraphicBuffer();
+
+    std::optional<base::unique_fd> mReadyFence;
+};
+
+const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004};
+const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
+const Rect OutputComposeSurfacesTest::kDefaultOutputSourceClip{1009, 1010, 1011, 1012};
+const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016};
+const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
+const compositionengine::CompositionRefreshArgs OutputComposeSurfacesTest::kDefaultRefreshArgs;
+const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
+const HdrCapabilities OutputComposeSurfacesTest::
+        kHdrCapabilities{{},
+                         OutputComposeSurfacesTest::kDefaultMaxLuminance,
+                         OutputComposeSurfacesTest::kDefaultAvgLuminance,
+                         OutputComposeSurfacesTest::kDefaultMinLuminance};
+
+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,
+       dequeuesABufferIfNoClientCompositionButFlipClientTargetRequested) {
+    mOutput.mState.usesClientComposition = false;
+    mOutput.mState.flipClientTarget = true;
+
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+
+    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));
+
+    verify().execute().expectNoFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest, handlesZeroCompositionRequests) {
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(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(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, true, _, _))
+            .WillRepeatedly(Return(NO_ERROR));
+
+    verify().execute().expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest, buildsAndRendersRequestList) {
+    LayerFE::LayerSettings r1;
+    LayerFE::LayerSettings r2;
+
+    r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+    r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(
+                    Invoke([&](const Region&,
+                               std::vector<LayerFE::LayerSettings>& clientCompositionLayers) {
+                        clientCompositionLayers.emplace_back(r2);
+                    }));
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+            .WillRepeatedly(Return(NO_ERROR));
+
+    verify().execute().expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest, renderDuplicateClientCompositionRequestsWithoutCache) {
+    mOutput.cacheClientCompositionRequests(0);
+    LayerFE::LayerSettings r1;
+    LayerFE::LayerSettings r2;
+
+    r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+    r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(Return());
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+            .Times(2)
+            .WillOnce(Return(NO_ERROR));
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+}
+
+TEST_F(OutputComposeSurfacesTest, skipDuplicateClientCompositionRequests) {
+    mOutput.cacheClientCompositionRequests(3);
+    LayerFE::LayerSettings r1;
+    LayerFE::LayerSettings r2;
+
+    r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+    r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(Return());
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+
+    // We do not expect another call to draw layers.
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_TRUE(mOutput.mState.reusedClientComposition);
+}
+
+TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChanges) {
+    LayerFE::LayerSettings r1;
+    LayerFE::LayerSettings r2;
+
+    r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+    r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(Return());
+
+    sp<GraphicBuffer> otherOutputBuffer = new GraphicBuffer();
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
+            .WillOnce(Return(mOutputBuffer))
+            .WillOnce(Return(otherOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+            .WillRepeatedly(Return(NO_ERROR));
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+}
+
+TEST_F(OutputComposeSurfacesTest, clientCompositionIfRequestChanges) {
+    LayerFE::LayerSettings r1;
+    LayerFE::LayerSettings r2;
+    LayerFE::LayerSettings r3;
+
+    r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+    r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+    r3.geometry.boundaries = FloatRect{5, 6, 7, 9};
+
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r2}))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r3}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(Return());
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, true, _, _))
+            .WillOnce(Return(NO_ERROR));
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+}
+
+struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComposeSurfacesTest {
+    OutputComposeSurfacesTest_UsesExpectedDisplaySettings() {
+        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(_)).WillRepeatedly(Return(mOutputBuffer));
+    }
+
+    struct MixedCompositionState
+          : public CallOrderStateMachineHelper<TestType, MixedCompositionState> {
+        auto ifMixedCompositionIs(bool used) {
+            getInstance()->mOutput.mState.usesDeviceComposition = used;
+            return nextState<OutputUsesHdrState>();
+        }
+    };
+
+    struct OutputUsesHdrState : public CallOrderStateMachineHelper<TestType, OutputUsesHdrState> {
+        auto andIfUsesHdr(bool used) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile, hasWideColorGamut())
+                    .WillOnce(Return(used));
+            return nextState<SkipColorTransformState>();
+        }
+    };
+
+    struct SkipColorTransformState
+          : public CallOrderStateMachineHelper<TestType, SkipColorTransformState> {
+        auto andIfSkipColorTransform(bool skip) {
+            // May be called zero or one times.
+            EXPECT_CALL(getInstance()->mOutput, getSkipColorTransform())
+                    .WillRepeatedly(Return(skip));
+            return nextState<ExpectDisplaySettingsState>();
+        }
+    };
+
+    struct ExpectDisplaySettingsState
+          : public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> {
+        auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) {
+            EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, true, _, _))
+                    .WillOnce(Return(NO_ERROR));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() { return MixedCompositionState::make(this); }
+};
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposition) {
+    verify().ifMixedCompositionIs(true)
+            .andIfUsesHdr(true)
+            .andIfSkipColorTransform(false)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
+                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComposition) {
+    verify().ifMixedCompositionIs(true)
+            .andIfUsesHdr(false)
+            .andIfSkipColorTransform(false)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
+                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientComposition) {
+    verify().ifMixedCompositionIs(false)
+            .andIfUsesHdr(true)
+            .andIfSkipColorTransform(false)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            kDefaultMaxLuminance, kDefaultOutputDataspace,
+                                            kDefaultColorTransformMat, Region::INVALID_REGION,
+                                            kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClientComposition) {
+    verify().ifMixedCompositionIs(false)
+            .andIfUsesHdr(false)
+            .andIfSkipColorTransform(false)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            kDefaultMaxLuminance, kDefaultOutputDataspace,
+                                            kDefaultColorTransformMat, Region::INVALID_REGION,
+                                            kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings,
+       usesExpectedDisplaySettingsForHdrOnlyClientCompositionWithSkipClientTransform) {
+    verify().ifMixedCompositionIs(false)
+            .andIfUsesHdr(true)
+            .andIfSkipColorTransform(true)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
+                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeSurfacesTest {
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
+        }
+
+        StrictMock<mock::OutputLayer> mOutputLayer;
+        StrictMock<mock::LayerFE> mLayerFE;
+        LayerFECompositionState mLayerFEState;
+    };
+
+    OutputComposeSurfacesTest_HandlesProtectedContent() {
+        mLayer1.mLayerFEState.hasProtectedContent = false;
+        mLayer2.mLayerFEState.hasProtectedContent = false;
+
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+                .WillRepeatedly(Return(&mLayer1.mOutputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+                .WillRepeatedly(Return(&mLayer2.mOutputLayer));
+
+        EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+
+        EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+
+        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, _))
+                .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
+        EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+                .WillRepeatedly(Return());
+        EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _))
+                .WillRepeatedly(Return(NO_ERROR));
+    }
+
+    Layer mLayer1;
+    Layer mLayer2;
+};
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifDisplayIsNotSecure) {
+    mOutput.mState.isSecure = false;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = false;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(false));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
+    EXPECT_CALL(mRenderEngine, useProtectedContext(false));
+    EXPECT_CALL(*mRenderSurface, setProtected(false));
+
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+
+    // For this test, we also check the call order of key functions.
+    InSequence seq;
+
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false));
+    EXPECT_CALL(mRenderEngine, useProtectedContext(true));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
+    EXPECT_CALL(*mRenderSurface, setProtected(true));
+    // Must happen after setting the protected content state.
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
+
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)).WillOnce(Return(false));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+    EXPECT_CALL(mRenderEngine, useProtectedContext(true));
+
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(true));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+    EXPECT_CALL(*mRenderSurface, setProtected(true));
+
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
+    EXPECT_CALL(mRenderEngine, useProtectedContext(true));
+
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+}
+
+struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSurfacesTest {
+    OutputComposeSurfacesTest_SetsExpensiveRendering() {
+        EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+        EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+        EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+        EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+                .WillRepeatedly(Return());
+        EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    }
+};
+
+TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDataspaceIsUsed) {
+    mOutput.mState.dataspace = kExpensiveOutputDataspace;
+
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kExpensiveOutputDataspace))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
+
+    // For this test, we also check the call order of key functions.
+    InSequence seq;
+
+    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+
+    mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs);
+}
+
+struct OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur
+      : public OutputComposeSurfacesTest_SetsExpensiveRendering {
+    OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur() {
+        mLayer.layerFEState.backgroundBlurRadius = 10;
+        mOutput.editState().isEnabled = 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>{}));
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+                .WillRepeatedly(Return(&mLayer.outputLayer));
+    }
+
+    NonInjectedLayer mLayer;
+    compositionengine::CompositionRefreshArgs mRefreshArgs;
+};
+
+TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreExpensive) {
+    mRefreshArgs.blursAreExpensive = true;
+    mOutput.updateAndWriteCompositionState(mRefreshArgs);
+
+    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
+    mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
+}
+
+TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering_ForBlur, IfBlursAreNotExpensive) {
+    mRefreshArgs.blursAreExpensive = false;
+    mOutput.updateAndWriteCompositionState(mRefreshArgs);
+
+    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(0);
+    mOutput.composeSurfaces(kDebugRegion, mRefreshArgs);
+}
+
+/*
+ * Output::generateClientCompositionRequests()
+ */
+
+struct GenerateClientCompositionRequestsTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // compositionengine::Output overrides
+        std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
+                bool supportsProtectedContent, Region& clearRegion,
+                ui::Dataspace dataspace) override {
+            return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
+                                                                   clearRegion, dataspace);
+        }
+    };
+
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+            EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
+            EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+        }
+
+        StrictMock<mock::OutputLayer> mOutputLayer;
+        StrictMock<mock::LayerFE> mLayerFE;
+        LayerFECompositionState mLayerFEState;
+        impl::OutputLayerCompositionState mOutputLayerState;
+        LayerFE::LayerSettings mLayerSettings;
+    };
+
+    GenerateClientCompositionRequestsTest() {
+        mOutput.mState.needsFiltering = false;
+
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    StrictMock<OutputPartialMock> mOutput;
+};
+
+struct GenerateClientCompositionRequestsTest_ThreeLayers
+      : public GenerateClientCompositionRequestsTest {
+    GenerateClientCompositionRequestsTest_ThreeLayers() {
+        mOutput.mState.frame = kDisplayFrame;
+        mOutput.mState.viewport = kDisplayViewport;
+        mOutput.mState.sourceClip = kDisplaySourceClip;
+        mOutput.mState.destinationClip = kDisplayDestinationClip;
+        mOutput.mState.transform = ui::Transform{kDisplayOrientation};
+        mOutput.mState.orientation = kDisplayOrientation;
+        mOutput.mState.needsFiltering = false;
+        mOutput.mState.isSecure = false;
+
+        for (size_t i = 0; i < mLayers.size(); i++) {
+            mLayers[i].mOutputLayerState.clearClientTarget = false;
+            mLayers[i].mOutputLayerState.visibleRegion = Region(kDisplayFrame);
+            mLayers[i].mLayerFEState.isOpaque = true;
+            mLayers[i].mLayerSettings.geometry.boundaries =
+                    FloatRect{static_cast<float>(i + 1), 0.f, 0.f, 0.f};
+            mLayers[i].mLayerSettings.source.solidColor = {1.0f, 1.0f, 1.0f};
+            mLayers[i].mLayerSettings.alpha = 1.0f;
+            mLayers[i].mLayerSettings.disableBlending = false;
+
+            EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(i))
+                    .WillRepeatedly(Return(&mLayers[i].mOutputLayer));
+            EXPECT_CALL(mLayers[i].mOutputLayer, requiresClientComposition())
+                    .WillRepeatedly(Return(true));
+            EXPECT_CALL(mLayers[i].mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+        }
+
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(mLayers.size()));
+    }
+
+    static constexpr uint32_t kDisplayOrientation = TR_IDENT;
+    static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN;
+
+    static const Rect kDisplayFrame;
+    static const Rect kDisplayViewport;
+    static const Rect kDisplaySourceClip;
+    static const Rect kDisplayDestinationClip;
+
+    std::array<Layer, 3> mLayers;
+};
+
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayFrame(0, 0, 100, 200);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayViewport(0, 0, 101, 201);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplaySourceClip(0, 0, 102, 202);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayDestinationClip(0, 0, 103,
+                                                                                      203);
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, handlesNoClientCompostionLayers) {
+    EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    EXPECT_EQ(0u, requests.size());
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionAfterViewportClip) {
+    mLayers[0].mOutputLayerState.visibleRegion = Region(Rect(10, 10, 10, 10));
+    mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(4000, 0, 4010, 10));
+    mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 0, 0));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    EXPECT_EQ(0u, requests.size());
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) {
+    LayerFE::LayerSettings mShadowSettings;
+    mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[1].mLayerSettings})));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
+                    {mShadowSettings, mLayers[2].mLayerSettings})));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(3u, requests.size());
+    EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
+    EXPECT_EQ(mShadowSettings, requests[1]);
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
+
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+
+    // Check that a timestamp was set for the layers that generated requests
+    EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
+    EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp);
+    EXPECT_TRUE(0 != mLayers[2].mOutputLayerState.clientCompositionTimestamp);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       onlyClientComposesClientComposedLayersIfNoClearingNeeded) {
+    EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+    mLayers[0].mOutputLayerState.clearClientTarget = false;
+    mLayers[1].mOutputLayerState.clearClientTarget = false;
+    mLayers[2].mOutputLayerState.clearClientTarget = false;
+
+    mLayers[0].mLayerFEState.isOpaque = true;
+    mLayers[1].mLayerFEState.isOpaque = true;
+    mLayers[2].mLayerFEState.isOpaque = true;
+
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(1u, requests.size());
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
+
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       onlyClientComposesClientComposedLayersIfOthersAreNotOpaque) {
+    EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+    mLayers[0].mOutputLayerState.clearClientTarget = true;
+    mLayers[1].mOutputLayerState.clearClientTarget = true;
+    mLayers[2].mOutputLayerState.clearClientTarget = true;
+
+    mLayers[0].mLayerFEState.isOpaque = false;
+    mLayers[1].mLayerFEState.isOpaque = false;
+    mLayers[2].mLayerFEState.isOpaque = false;
+
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(1u, requests.size());
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
+
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqueAndNotFirst) {
+    // If client composition is performed with some layers set to use device
+    // composition, device layers after the first layer (device or client) will
+    // clear the frame buffer if they are opaque and if that layer has a flag
+    // set to do so. The first layer is skipped as the frame buffer is already
+    // expected to be clear.
+
+    EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+    mLayers[0].mOutputLayerState.clearClientTarget = true;
+    mLayers[1].mOutputLayerState.clearClientTarget = true;
+    mLayers[2].mOutputLayerState.clearClientTarget = true;
+
+    mLayers[0].mLayerFEState.isOpaque = true;
+    mLayers[1].mLayerFEState.isOpaque = true;
+    mLayers[2].mLayerFEState.isOpaque = true;
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    Region stubRegion;
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false,      /* identity transform */
+            false,      /* needs filtering */
+            false,      /* secure */
+            false,      /* supports protected content */
+            stubRegion, /* clear region */
+            kDisplayViewport,
+            kDisplayDataspace,
+            false /* realContentIsVisible */,
+            true /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings;
+    mBlackoutSettings.source.buffer.buffer = nullptr;
+    mBlackoutSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+    mBlackoutSettings.alpha = 0.f;
+    mBlackoutSettings.disableBlending = true;
+
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mBlackoutSettings})));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
+
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(2u, requests.size());
+
+    // The second layer is expected to be rendered as alpha=0 black with no blending
+    EXPECT_EQ(mBlackoutSettings, requests[0]);
+
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
+
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       clippedVisibleRegionUsedToGenerateRequest) {
+    mLayers[0].mOutputLayerState.visibleRegion = Region(Rect(10, 10, 20, 20));
+    mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 30, 30));
+    mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, 0, 40, 4000));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+            Region(Rect(10, 10, 20, 20)),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(Rect(0, 0, 30, 30)),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(Rect(0, 0, 40, 201)),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+
+    static_cast<void>(
+            mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                      accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       perLayerNeedsFilteringUsedToGenerateRequests) {
+    mOutput.mState.needsFiltering = false;
+    EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            true,  /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+
+    static_cast<void>(
+            mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                      accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       wholeOutputNeedsFilteringUsedToGenerateRequests) {
+    mOutput.mState.needsFiltering = true;
+    EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            true,  /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            true,  /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            true,  /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+
+    static_cast<void>(
+            mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                      accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       wholeOutputSecurityUsedToGenerateRequests) {
+    mOutput.mState.isSecure = true;
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            true,  /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            true,  /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            true,  /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+
+    static_cast<void>(
+            mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                      accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       protectedContentSupportUsedToGenerateRequests) {
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            true,  /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            true,  /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            true,  /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+
+    static_cast<void>(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */,
+                                                                accumClearRegion,
+                                                                kDisplayDataspace));
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    // Layer requesting blur, or below, should request client composition.
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+
+    layer2.layerFEState.backgroundBlurRadius = 10;
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
+    // In split-screen landscape mode, the screen is rotated 90 degrees, with
+    // one layer on the left covering the left side of the output, and one layer
+    // on the right covering that side of the output.
+
+    const Rect kPortraitFrame(0, 0, 1000, 2000);
+    const Rect kPortraitViewport(0, 0, 2000, 1000);
+    const Rect kPortraitSourceClip(0, 0, 1000, 2000);
+    const Rect kPortraitDestinationClip(0, 0, 1000, 2000);
+    const uint32_t kPortraitOrientation = TR_ROT_90;
+    constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
+
+    mOutput.mState.frame = kPortraitFrame;
+    mOutput.mState.viewport = kPortraitViewport;
+    mOutput.mState.sourceClip = kPortraitSourceClip;
+    mOutput.mState.destinationClip = kPortraitDestinationClip;
+    mOutput.mState.transform = ui::Transform{kPortraitOrientation};
+    mOutput.mState.orientation = kPortraitOrientation;
+    mOutput.mState.needsFiltering = false;
+    mOutput.mState.isSecure = true;
+
+    Layer leftLayer;
+    Layer rightLayer;
+
+    leftLayer.mOutputLayerState.clearClientTarget = false;
+    leftLayer.mOutputLayerState.visibleRegion = Region(Rect(0, 0, 1000, 1000));
+    leftLayer.mLayerFEState.isOpaque = true;
+    leftLayer.mLayerSettings.source.solidColor = {1.f, 0.f, 0.f};
+
+    rightLayer.mOutputLayerState.clearClientTarget = false;
+    rightLayer.mOutputLayerState.visibleRegion = Region(Rect(1000, 0, 2000, 1000));
+    rightLayer.mLayerFEState.isOpaque = true;
+    rightLayer.mLayerSettings.source.solidColor = {0.f, 1.f, 0.f};
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+    EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+            .WillRepeatedly(Return(&leftLayer.mOutputLayer));
+    EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+            .WillRepeatedly(Return(&rightLayer.mOutputLayer));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{
+            Region(Rect(0, 0, 1000, 1000)),
+            false, /* identity transform */
+            false, /* needs filtering */
+            true,  /* secure */
+            true,  /* supports protected content */
+            accumClearRegion,
+            kPortraitViewport,
+            kOutputDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+    EXPECT_CALL(leftLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+    EXPECT_CALL(leftLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(leftLayerSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({leftLayer.mLayerSettings})));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{
+            Region(Rect(1000, 0, 2000, 1000)),
+            false, /* identity transform */
+            false, /* needs filtering */
+            true,  /* secure */
+            true,  /* supports protected content */
+            accumClearRegion,
+            kPortraitViewport,
+            kOutputDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+    EXPECT_CALL(rightLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+    EXPECT_CALL(rightLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(rightLayerSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({rightLayer.mLayerSettings})));
+
+    constexpr bool supportsProtectedContent = true;
+    auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent,
+                                                              accumClearRegion, kOutputDataspace);
+    ASSERT_EQ(2u, requests.size());
+    EXPECT_EQ(leftLayer.mLayerSettings, requests[0]);
+    EXPECT_EQ(rightLayer.mLayerSettings, requests[1]);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       shadowRegionOnlyVisibleSkipsContentComposition) {
+    const Rect kContentWithShadow(40, 40, 70, 90);
+    const Rect kContent(50, 50, 60, 80);
+    const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent);
+    const Region kPartialShadowRegion = Region(kContentWithShadow).subtract(Rect(40, 40, 60, 80));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
+            Region(Rect(60, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
+            false,                                                    /* identity transform */
+            false,                                                    /* needs filtering */
+            false,                                                    /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            false /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    LayerFE::LayerSettings mShadowSettings;
+    mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+    mLayers[2].mOutputLayerState.visibleRegion = kPartialShadowRegion;
+    mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion;
+
+    EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mShadowSettings})));
+
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(1u, requests.size());
+
+    EXPECT_EQ(mShadowSettings, requests[0]);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       shadowRegionWithContentVisibleRequestsContentAndShadowComposition) {
+    const Rect kContentWithShadow(40, 40, 70, 90);
+    const Rect kContent(50, 50, 60, 80);
+    const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent);
+    const Region kPartialContentWithPartialShadowRegion =
+            Region(kContentWithShadow).subtract(Rect(40, 40, 50, 80));
+
+    LayerFE::LayerSettings mShadowSettings;
+    mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+    mLayers[2].mOutputLayerState.visibleRegion = kPartialContentWithPartialShadowRegion;
+    mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion;
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
+            Region(Rect(50, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
+            false,                                                    /* identity transform */
+            false,                                                    /* needs filtering */
+            false,                                                    /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
+                    {mShadowSettings, mLayers[2].mLayerSettings})));
+
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(2u, requests.size());
+
+    EXPECT_EQ(mShadowSettings, requests[0]);
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h
deleted file mode 100644
index d4c76bc..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string>
-
-#include <android-base/stringprintf.h>
-#include <gmock/gmock.h>
-
-namespace {
-
-using android::base::StringAppendF;
-using Rect = android::Rect;
-
-void dumpRect(const Rect& rect, std::string& result, const char* name) {
-    StringAppendF(&result, "%s (%d %d %d %d) ", name, rect.left, rect.top, rect.right, rect.bottom);
-}
-
-// Checks for a region match
-MATCHER_P(RectEq, expected, "") {
-    std::string buf;
-    buf.append("Rects are not equal\n");
-    dumpRect(expected, buf, "expected rect");
-    dumpRect(arg, buf, "actual rect");
-    *result_listener << buf;
-
-    return (expected.left == arg.left) && (expected.top == arg.top) &&
-            (expected.right == arg.right) && (expected.bottom == arg.bottom);
-}
-
-} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
index 5a4efa9..2adde23 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
+++ b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
@@ -20,24 +20,22 @@
 
 namespace {
 
-// Checks for a region match
-MATCHER_P(RegionEq, expected, "") {
-    std::string buf;
-    buf.append("Regions are not equal\n");
-    expected.dump(buf, "expected region");
-    arg.dump(buf, "actual region");
-    *result_listener << buf;
+using Region = android::Region;
 
-    size_t expectedRectCount = 0;
-    android::Rect const* expectedRects = expected.getArray(&expectedRectCount);
-    size_t actualRectCount = 0;
-    android::Rect const* actualRects = arg.getArray(&actualRectCount);
+struct RegionMatcher : public testing::MatcherInterface<const Region&> {
+    const Region expected;
 
-    if (expectedRectCount != actualRectCount) return false;
-    for (size_t i = 0; i < expectedRectCount; i++) {
-        if (expectedRects[i] != actualRects[i]) return false;
+    explicit RegionMatcher(const Region& expectedValue) : expected(expectedValue) {}
+
+    bool MatchAndExplain(const Region& actual, testing::MatchResultListener*) const override {
+        return expected.hasSameRects(actual);
     }
-    return true;
+
+    void DescribeTo(::std::ostream* os) const override { PrintTo(expected, os); }
+};
+
+testing::Matcher<const Region&> RegionEq(const Region& expected) {
+    return MakeMatcher(new RegionMatcher(expected));
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index f75a4dc..fd47e45 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -18,6 +18,7 @@
 #include <cstdint>
 
 #include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/RenderSurface.h>
 #include <compositionengine/mock/CompositionEngine.h>
 #include <compositionengine/mock/Display.h>
@@ -27,15 +28,9 @@
 #include <gtest/gtest.h>
 #include <renderengine/mock/RenderEngine.h>
 
-#include "MockHWComposer.h"
-
 namespace android::compositionengine {
 namespace {
 
-/* ------------------------------------------------------------------------
- * RenderSurfaceTest
- */
-
 constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
 constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
 constexpr std::optional<DisplayId> DEFAULT_DISPLAY_ID = std::make_optional(DisplayId{123u});
@@ -55,14 +50,11 @@
     RenderSurfaceTest() {
         EXPECT_CALL(mDisplay, getId()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_ID));
         EXPECT_CALL(mDisplay, getName()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_NAME));
-        EXPECT_CALL(mCompositionEngine, getHwComposer).WillRepeatedly(ReturnRef(mHwComposer));
         EXPECT_CALL(mCompositionEngine, getRenderEngine).WillRepeatedly(ReturnRef(mRenderEngine));
         EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL))
                 .WillRepeatedly(Return(NO_ERROR));
     }
-    ~RenderSurfaceTest() override = default;
 
-    StrictMock<android::mock::HWComposer> mHwComposer;
     StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
     StrictMock<mock::CompositionEngine> mCompositionEngine;
     StrictMock<mock::Display> mDisplay;
@@ -74,7 +66,7 @@
                                                            mDisplaySurface}};
 };
 
-/* ------------------------------------------------------------------------
+/*
  * Basic construction
  */
 
@@ -82,7 +74,7 @@
     EXPECT_TRUE(mSurface.isValid());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::initialize()
  */
 
@@ -95,7 +87,7 @@
     mSurface.initialize();
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::getSize()
  */
 
@@ -105,7 +97,7 @@
     EXPECT_EQ(expected, mSurface.getSize());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::getClientTargetAcquireFence()
  */
 
@@ -117,7 +109,7 @@
     EXPECT_EQ(fence.get(), mSurface.getClientTargetAcquireFence().get());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::setDisplaySize()
  */
 
@@ -127,7 +119,7 @@
     mSurface.setDisplaySize(ui::Size(640, 480));
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::setBufferDataspace()
  */
 
@@ -138,7 +130,7 @@
     mSurface.setBufferDataspace(ui::Dataspace::DISPLAY_P3);
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::setProtected()
  */
 
@@ -179,7 +171,7 @@
     EXPECT_FALSE(mSurface.isProtected());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::beginFrame()
  */
 
@@ -189,73 +181,39 @@
     EXPECT_EQ(NO_ERROR, mSurface.beginFrame(true));
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::prepareFrame()
  */
 
-TEST_F(RenderSurfaceTest, prepareFramePassesOutputLayersToHwc) {
-    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
-            .WillOnce(Return(INVALID_OPERATION));
-
-    EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame());
-}
-
-TEST_F(RenderSurfaceTest, prepareFrameTakesEarlyOutOnHwcError) {
-    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
-            .WillOnce(Return(INVALID_OPERATION));
-
-    EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame());
-}
-
 TEST_F(RenderSurfaceTest, prepareFrameHandlesMixedComposition) {
-    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
-            .WillOnce(Return(NO_ERROR));
-    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
-    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
-
     EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_MIXED))
-            .WillOnce(Return(INVALID_OPERATION));
+            .WillOnce(Return(NO_ERROR));
 
-    EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame());
+    mSurface.prepareFrame(true, true);
 }
 
-TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGlesComposition) {
-    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
-            .WillOnce(Return(NO_ERROR));
-    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
-    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
-
-    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GLES))
+TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGpuComposition) {
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GPU))
             .WillOnce(Return(NO_ERROR));
 
-    EXPECT_EQ(NO_ERROR, mSurface.prepareFrame());
+    mSurface.prepareFrame(true, false);
 }
 
 TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyHwcComposition) {
-    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
-            .WillOnce(Return(NO_ERROR));
-    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
-    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
-
     EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
             .WillOnce(Return(NO_ERROR));
 
-    EXPECT_EQ(NO_ERROR, mSurface.prepareFrame());
+    mSurface.prepareFrame(false, true);
 }
 
 TEST_F(RenderSurfaceTest, prepareFrameHandlesNoComposition) {
-    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
-            .WillOnce(Return(NO_ERROR));
-    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
-    EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
-
     EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
             .WillOnce(Return(NO_ERROR));
 
-    EXPECT_EQ(NO_ERROR, mSurface.prepareFrame());
+    mSurface.prepareFrame(false, false);
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::dequeueBuffer()
  */
 
@@ -272,7 +230,7 @@
     EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::queueBuffer()
  */
 
@@ -280,9 +238,11 @@
     sp<GraphicBuffer> buffer = new GraphicBuffer();
     mSurface.mutableGraphicBufferForTest() = buffer;
 
-    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
-    EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID))
-            .WillOnce(Return(false));
+    impl::OutputCompositionState state;
+    state.usesClientComposition = false;
+    state.flipClientTarget = false;
+
+    EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
     mSurface.queueBuffer(base::unique_fd());
@@ -294,7 +254,11 @@
     sp<GraphicBuffer> buffer = new GraphicBuffer();
     mSurface.mutableGraphicBufferForTest() = buffer;
 
-    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    impl::OutputCompositionState state;
+    state.usesClientComposition = true;
+    state.flipClientTarget = false;
+
+    EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
     EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
@@ -308,8 +272,11 @@
     sp<GraphicBuffer> buffer = new GraphicBuffer();
     mSurface.mutableGraphicBufferForTest() = buffer;
 
-    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
-    EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    impl::OutputCompositionState state;
+    state.usesClientComposition = false;
+    state.flipClientTarget = true;
+
+    EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
     EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
@@ -322,8 +289,11 @@
 TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequestWithNoBufferYetDequeued) {
     sp<GraphicBuffer> buffer = new GraphicBuffer();
 
-    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false));
-    EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    impl::OutputCompositionState state;
+    state.usesClientComposition = false;
+    state.flipClientTarget = true;
+
+    EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
     EXPECT_CALL(*mNativeWindow, dequeueBuffer(_, _))
             .WillOnce(
                     DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
@@ -340,7 +310,10 @@
     sp<GraphicBuffer> buffer = new GraphicBuffer();
     mSurface.mutableGraphicBufferForTest() = buffer;
 
-    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    impl::OutputCompositionState state;
+    state.usesClientComposition = true;
+
+    EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
     EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
             .WillOnce(Return(INVALID_OPERATION));
     EXPECT_CALL(mDisplay, isVirtual()).WillOnce(Return(true));
@@ -353,7 +326,7 @@
     EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::onPresentDisplayCompleted()
  */
 
@@ -363,21 +336,7 @@
     mSurface.onPresentDisplayCompleted();
 }
 
-/* ------------------------------------------------------------------------
- * RenderSurface::setViewportAndProjection()
- */
-
-TEST_F(RenderSurfaceTest, setViewportAndProjectionAppliesChang) {
-    mSurface.setSizeForTest(ui::Size(100, 200));
-
-    EXPECT_CALL(mRenderEngine,
-                setViewportAndProjection(100, 200, Rect(100, 200), ui::Transform::ROT_0))
-            .Times(1);
-
-    mSurface.setViewportAndProjection();
-}
-
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::flip()
  */
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
deleted file mode 100644
index ea07bed..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string>
-
-#include <gmock/gmock.h>
-
-namespace {
-
-// Check for a transform match
-MATCHER_P(TransformEq, expected, "") {
-    std::string buf;
-    buf.append("Transforms are not equal\n");
-    expected.dump(buf, "expected transform");
-    arg.dump(buf, "actual transform");
-    *result_listener << buf;
-
-    const float TOLERANCE = 1e-3f;
-
-    for (int i = 0; i < 3; i++) {
-        for (int j = 0; j < 3; j++) {
-            if (std::fabs(expected[i][j] - arg[i][j]) > TOLERANCE) {
-                return false;
-            }
-        }
-    }
-
-    return true;
-}
-
-} // namespace
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index e33bedb..841e79f 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "ContainerLayer"
@@ -26,16 +30,19 @@
 
 ContainerLayer::~ContainerLayer() = default;
 
-bool ContainerLayer::prepareClientLayer(const RenderArea&, const Region&, bool, Region&, const bool,
-                                        renderengine::LayerSettings&) {
-    return false;
-}
-
 bool ContainerLayer::isVisible() const {
     return false;
 }
 
+sp<Layer> ContainerLayer::createClone() {
+    sp<ContainerLayer> layer = mFlinger->getFactory().createContainerLayer(
+            LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0,
+                              LayerMetadata()));
+    layer->setInitialValuesForClone(this);
+    return layer;
+}
 
-void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&, const ui::Transform&,
-                                     const Rect&, int32_t, const ui::Dataspace) {}
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index e6dbfcc..9b7bab1 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -28,22 +28,14 @@
     explicit ContainerLayer(const LayerCreationArgs&);
     ~ContainerLayer() override;
 
-    const char* getTypeId() const override { return "ContainerLayer"; }
+    const char* getType() const override { return "ContainerLayer"; }
     bool isVisible() const override;
 
-    void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
-                         const Rect& viewport, int32_t supportedPerFrameMetadata,
-                         const ui::Dataspace targetDataspace) override;
-
     bool isCreatedFromMainThread() const override { return true; }
 
-    bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
-
 protected:
-    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                            bool useIdentityTransform, Region& clearRegion,
-                            const bool supportProtectedContent,
-                            renderengine::LayerSettings& layer) override;
+    bool canDrawShadows() const override { return false; }
+    sp<Layer> createClone() override;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 5700d72..730f297 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "DisplayDevice"
@@ -39,32 +43,26 @@
 
 namespace android {
 
+namespace hal = hardware::graphics::composer::hal;
+
 using android::base::StringAppendF;
 
-/*
- * Initialize the display to the specified values.
- *
- */
+ui::Transform::RotationFlags DisplayDevice::sPrimaryDisplayRotationFlags = ui::Transform::ROT_0;
 
-uint32_t DisplayDevice::sPrimaryDisplayOrientation = 0;
+DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(
+        const sp<SurfaceFlinger>& flinger, const wp<IBinder>& displayToken,
+        std::shared_ptr<compositionengine::Display> compositionDisplay)
+      : flinger(flinger), displayToken(displayToken), compositionDisplay(compositionDisplay) {}
 
-DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger,
-                                                     const wp<IBinder>& displayToken,
-                                                     const std::optional<DisplayId>& displayId)
-      : flinger(flinger), displayToken(displayToken), displayId(displayId) {}
-
-DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs&& args)
+DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args)
       : mFlinger(args.flinger),
         mDisplayToken(args.displayToken),
         mSequenceId(args.sequenceId),
-        mDisplayInstallOrientation(args.displayInstallOrientation),
-        mCompositionDisplay{mFlinger->getCompositionEngine().createDisplay(
-                compositionengine::DisplayCreationArgs{args.isSecure, args.isVirtual,
-                                                       args.displayId})},
-        mIsVirtual(args.isVirtual),
-        mOrientation(),
-        mActiveConfig(0),
+        mConnectionType(args.connectionType),
+        mCompositionDisplay{args.compositionDisplay},
+        mPhysicalOrientation(args.physicalOrientation),
         mIsPrimary(args.isPrimary) {
+    mCompositionDisplay->editState().isSecure = args.isSecure;
     mCompositionDisplay->createRenderSurface(
             compositionengine::RenderSurfaceCreationArgs{ANativeWindow_getWidth(
                                                                  args.nativeWindow.get()),
@@ -72,6 +70,12 @@
                                                                  args.nativeWindow.get()),
                                                          args.nativeWindow, args.displaySurface});
 
+    if (!mFlinger->mDisableClientCompositionCache &&
+        SurfaceFlinger::maxFrameBufferAcquiredBuffers > 0) {
+        mCompositionDisplay->createClientCompositionCache(
+                static_cast<uint32_t>(SurfaceFlinger::maxFrameBufferAcquiredBuffers));
+    }
+
     mCompositionDisplay->createDisplayColorProfile(
             compositionengine::DisplayColorProfileCreationArgs{args.hasWideColorGamut,
                                                                std::move(args.hdrCapabilities),
@@ -87,7 +91,7 @@
     setPowerMode(args.initialPowerMode);
 
     // initialize the display orientation transform.
-    setProjection(DisplayState::eOrientationDefault, Rect::INVALID_RECT, Rect::INVALID_RECT);
+    setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT);
 }
 
 DisplayDevice::~DisplayDevice() = default;
@@ -117,106 +121,56 @@
 }
 
 // ----------------------------------------------------------------------------
-
-void DisplayDevice::setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers) {
-    mVisibleLayersSortedByZ = layers;
-}
-
-const Vector< sp<Layer> >& DisplayDevice::getVisibleLayersSortedByZ() const {
-    return mVisibleLayersSortedByZ;
-}
-
-void DisplayDevice::setLayersNeedingFences(const Vector< sp<Layer> >& layers) {
-    mLayersNeedingFences = layers;
-}
-
-const Vector< sp<Layer> >& DisplayDevice::getLayersNeedingFences() const {
-    return mLayersNeedingFences;
-}
-
-// ----------------------------------------------------------------------------
-void DisplayDevice::setPowerMode(int mode) {
+void DisplayDevice::setPowerMode(hal::PowerMode mode) {
     mPowerMode = mode;
-    getCompositionDisplay()->setCompositionEnabled(mPowerMode != HWC_POWER_MODE_OFF);
+    getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF);
 }
 
-int DisplayDevice::getPowerMode()  const {
+hal::PowerMode DisplayDevice::getPowerMode() const {
     return mPowerMode;
 }
 
 bool DisplayDevice::isPoweredOn() const {
-    return mPowerMode != HWC_POWER_MODE_OFF;
+    return mPowerMode != hal::PowerMode::OFF;
 }
 
-// ----------------------------------------------------------------------------
-void DisplayDevice::setActiveConfig(int mode) {
+void DisplayDevice::setActiveConfig(HwcConfigIndexType mode) {
     mActiveConfig = mode;
 }
 
-int DisplayDevice::getActiveConfig()  const {
+HwcConfigIndexType DisplayDevice::getActiveConfig() const {
     return mActiveConfig;
 }
 
-// ----------------------------------------------------------------------------
-
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
     return mCompositionDisplay->getState().dataspace;
 }
 
-// ----------------------------------------------------------------------------
-
-void DisplayDevice::setLayerStack(uint32_t stack) {
+void DisplayDevice::setLayerStack(ui::LayerStack stack) {
     mCompositionDisplay->setLayerStackFilter(stack, isPrimary());
 }
 
-// ----------------------------------------------------------------------------
-
-uint32_t DisplayDevice::displayStateOrientationToTransformOrientation(int orientation) {
-    switch (orientation) {
-    case DisplayState::eOrientationDefault:
-        return ui::Transform::ROT_0;
-    case DisplayState::eOrientation90:
-        return ui::Transform::ROT_90;
-    case DisplayState::eOrientation180:
-        return ui::Transform::ROT_180;
-    case DisplayState::eOrientation270:
-        return ui::Transform::ROT_270;
-    default:
-        return ui::Transform::ROT_INVALID;
-    }
+void DisplayDevice::setDisplaySize(int width, int height) {
+    mCompositionDisplay->setBounds(ui::Size(width, height));
 }
 
-status_t DisplayDevice::orientationToTransfrom(int orientation, int w, int h, ui::Transform* tr) {
-    uint32_t flags = displayStateOrientationToTransformOrientation(orientation);
-    if (flags == ui::Transform::ROT_INVALID) {
-        return BAD_VALUE;
-    }
-    tr->set(flags, w, h);
-    return NO_ERROR;
-}
-
-void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) {
-    mCompositionDisplay->setBounds(ui::Size(newWidth, newHeight));
-}
-
-void DisplayDevice::setProjection(int orientation,
-        const Rect& newViewport, const Rect& newFrame) {
-    Rect viewport(newViewport);
-    Rect frame(newFrame);
-
+void DisplayDevice::setProjection(ui::Rotation orientation, Rect viewport, Rect frame) {
     mOrientation = orientation;
 
     const Rect& displayBounds = getCompositionDisplay()->getState().bounds;
-    const int w = displayBounds.width();
-    const int h = displayBounds.height();
+    const int displayWidth = displayBounds.width();
+    const int displayHeight = displayBounds.height();
 
-    ui::Transform R;
-    DisplayDevice::orientationToTransfrom(orientation, w, h, &R);
+    ui::Transform rotation;
+    if (const auto flags = ui::Transform::toRotationFlags(orientation);
+        flags != ui::Transform::ROT_INVALID) {
+        rotation.set(flags, displayWidth, displayHeight);
+    }
 
     if (!frame.isValid()) {
         // the destination frame can be invalid if it has never been set,
         // in that case we assume the whole display frame.
-        frame = Rect(w, h);
+        frame = Rect(displayWidth, displayHeight);
     }
 
     if (viewport.isEmpty()) {
@@ -224,86 +178,97 @@
         // we assume the whole display size.
         // it's also invalid to have an empty viewport, so we handle that
         // case in the same way.
-        viewport = Rect(w, h);
-        if (R.getOrientation() & ui::Transform::ROT_90) {
+        viewport = Rect(displayWidth, displayHeight);
+        if (rotation.getOrientation() & ui::Transform::ROT_90) {
             // viewport is always specified in the logical orientation
             // of the display (ie: post-rotation).
             std::swap(viewport.right, viewport.bottom);
         }
     }
 
-    ui::Transform TL, TP, S;
-    float src_width  = viewport.width();
-    float src_height = viewport.height();
-    float dst_width  = frame.width();
-    float dst_height = frame.height();
-    if (src_width != dst_width || src_height != dst_height) {
-        float sx = dst_width  / src_width;
-        float sy = dst_height / src_height;
-        S.set(sx, 0, 0, sy);
+    ui::Transform logicalTranslation, physicalTranslation, scale;
+    const float sourceWidth = viewport.width();
+    const float sourceHeight = viewport.height();
+    const float destWidth = frame.width();
+    const float destHeight = frame.height();
+    if (sourceWidth != destWidth || sourceHeight != destHeight) {
+        const float scaleX = destWidth / sourceWidth;
+        const float scaleY = destHeight / sourceHeight;
+        scale.set(scaleX, 0, 0, scaleY);
     }
 
-    float src_x = viewport.left;
-    float src_y = viewport.top;
-    float dst_x = frame.left;
-    float dst_y = frame.top;
-    TL.set(-src_x, -src_y);
-    TP.set(dst_x, dst_y);
+    const float sourceX = viewport.left;
+    const float sourceY = viewport.top;
+    const float destX = frame.left;
+    const float destY = frame.top;
+    logicalTranslation.set(-sourceX, -sourceY);
+    physicalTranslation.set(destX, destY);
 
     // need to take care of primary display rotation for globalTransform
     // for case if the panel is not installed aligned with device orientation
     if (isPrimary()) {
-        DisplayDevice::orientationToTransfrom(
-                (orientation + mDisplayInstallOrientation) % (DisplayState::eOrientation270 + 1),
-                w, h, &R);
+        if (const auto flags = ui::Transform::toRotationFlags(orientation + mPhysicalOrientation);
+            flags != ui::Transform::ROT_INVALID) {
+            rotation.set(flags, displayWidth, displayHeight);
+        }
     }
 
     // The viewport and frame are both in the logical orientation.
     // Apply the logical translation, scale to physical size, apply the
     // physical translation and finally rotate to the physical orientation.
-    ui::Transform globalTransform = R * TP * S * TL;
+    ui::Transform globalTransform = rotation * physicalTranslation * scale * logicalTranslation;
 
     const uint8_t type = globalTransform.getType();
     const bool needsFiltering =
             (!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
 
-    Rect scissor = globalTransform.transform(viewport);
-    if (scissor.isEmpty()) {
-        scissor = displayBounds;
+    const Rect& sourceClip = viewport;
+    Rect destinationClip = globalTransform.transform(viewport);
+    if (destinationClip.isEmpty()) {
+        destinationClip = displayBounds;
     }
+    // Make sure the destination clip is contained in the display bounds
+    destinationClip.intersect(displayBounds, &destinationClip);
 
     uint32_t transformOrientation;
 
     if (isPrimary()) {
-        sPrimaryDisplayOrientation = displayStateOrientationToTransformOrientation(orientation);
-        transformOrientation = displayStateOrientationToTransformOrientation(
-                (orientation + mDisplayInstallOrientation) % (DisplayState::eOrientation270 + 1));
+        sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
+        transformOrientation = ui::Transform::toRotationFlags(orientation + mPhysicalOrientation);
     } else {
-        transformOrientation = displayStateOrientationToTransformOrientation(orientation);
+        transformOrientation = ui::Transform::toRotationFlags(orientation);
     }
 
-    getCompositionDisplay()->setProjection(globalTransform, transformOrientation,
-                                           frame, viewport, scissor, needsFiltering);
+    getCompositionDisplay()->setProjection(globalTransform, transformOrientation, frame, viewport,
+                                           sourceClip, destinationClip, needsFiltering);
 }
 
-uint32_t DisplayDevice::getPrimaryDisplayOrientationTransform() {
-    return sPrimaryDisplayOrientation;
+ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
+    return sPrimaryDisplayRotationFlags;
 }
 
 std::string DisplayDevice::getDebugName() const {
-    const auto id = getId() ? to_string(*getId()) + ", " : std::string();
-    return base::StringPrintf("DisplayDevice{%s%s%s\"%s\"}", id.c_str(),
-                              isPrimary() ? "primary, " : "", isVirtual() ? "virtual, " : "",
-                              mDisplayName.c_str());
+    std::string displayId;
+    if (const auto id = getId()) {
+        displayId = to_string(*id) + ", ";
+    }
+
+    const char* type = "virtual";
+    if (mConnectionType) {
+        type = *mConnectionType == DisplayConnectionType::Internal ? "internal" : "external";
+    }
+
+    return base::StringPrintf("DisplayDevice{%s%s%s, \"%s\"}", displayId.c_str(), type,
+                              isPrimary() ? ", primary" : "", mDisplayName.c_str());
 }
 
 void DisplayDevice::dump(std::string& result) const {
     StringAppendF(&result, "+ %s\n", getDebugName().c_str());
 
     result.append("   ");
-    StringAppendF(&result, "powerMode=%d, ", mPowerMode);
-    StringAppendF(&result, "activeConfig=%d, ", mActiveConfig);
-    StringAppendF(&result, "numLayers=%zu\n", mVisibleLayersSortedByZ.size());
+    StringAppendF(&result, "powerMode=%s (%d), ", to_string(mPowerMode).c_str(),
+                  static_cast<int32_t>(mPowerMode));
+    StringAppendF(&result, "activeConfig=%d, ", mActiveConfig.value());
     getCompositionDisplay()->dump(result);
 }
 
@@ -333,7 +298,7 @@
     return mCompositionDisplay->getState().needsFiltering;
 }
 
-uint32_t DisplayDevice::getLayerStack() const {
+ui::LayerStack DisplayDevice::getLayerStack() const {
     return mCompositionDisplay->getState().layerStackId;
 }
 
@@ -349,8 +314,8 @@
     return mCompositionDisplay->getState().frame;
 }
 
-const Rect& DisplayDevice::getScissor() const {
-    return mCompositionDisplay->getState().scissor;
+const Rect& DisplayDevice::getSourceClip() const {
+    return mCompositionDisplay->getState().sourceClip;
 }
 
 bool DisplayDevice::hasWideColorGamut() const {
@@ -384,3 +349,6 @@
 std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
 
 }  // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 0067b50..cb467ea 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_DISPLAY_DEVICE_H
-#define ANDROID_DISPLAY_DEVICE_H
-
-#include <stdlib.h>
+#pragma once
 
 #include <memory>
 #include <optional>
@@ -27,10 +24,11 @@
 #include <android/native_window.h>
 #include <binder/IBinder.h>
 #include <gui/LayerState.h>
-#include <hardware/hwcomposer_defs.h>
 #include <math/mat4.h>
 #include <renderengine/RenderEngine.h>
 #include <system/window.h>
+#include <ui/DisplayInfo.h>
+#include <ui/DisplayState.h>
 #include <ui/GraphicTypes.h>
 #include <ui/HdrCapabilities.h>
 #include <ui/Region.h>
@@ -40,7 +38,10 @@
 #include <utils/Timers.h>
 
 #include "DisplayHardware/DisplayIdentification.h"
+#include "DisplayHardware/Hal.h"
+#include "DisplayHardware/PowerAdvisor.h"
 #include "RenderArea.h"
+#include "Scheduler/HwcStrongTypes.h"
 
 namespace android {
 
@@ -52,7 +53,6 @@
 
 struct CompositionInfo;
 struct DisplayDeviceCreationArgs;
-struct DisplayInfo;
 
 namespace compositionengine {
 class Display;
@@ -64,45 +64,45 @@
     constexpr static float sDefaultMinLumiance = 0.0;
     constexpr static float sDefaultMaxLumiance = 500.0;
 
-    enum {
-        NO_LAYER_STACK = 0xFFFFFFFF,
-    };
-
-    explicit DisplayDevice(DisplayDeviceCreationArgs&& args);
+    explicit DisplayDevice(DisplayDeviceCreationArgs& args);
     virtual ~DisplayDevice();
 
     std::shared_ptr<compositionengine::Display> getCompositionDisplay() const {
         return mCompositionDisplay;
     }
 
-    bool isVirtual() const { return mIsVirtual; }
+    std::optional<DisplayConnectionType> getConnectionType() const { return mConnectionType; }
+
+    bool isVirtual() const { return !mConnectionType; }
     bool isPrimary() const { return mIsPrimary; }
 
     // isSecure indicates whether this display can be trusted to display
     // secure surfaces.
     bool isSecure() const;
 
-    int         getWidth() const;
-    int         getHeight() const;
-    int         getInstallOrientation() const { return mDisplayInstallOrientation; }
+    int getWidth() const;
+    int getHeight() const;
+    ui::Size getSize() const { return {getWidth(), getHeight()}; }
 
-    void                    setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers);
-    const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const;
-    void                    setLayersNeedingFences(const Vector< sp<Layer> >& layers);
-    const Vector< sp<Layer> >& getLayersNeedingFences() const;
+    void setLayerStack(ui::LayerStack);
+    void setDisplaySize(int width, int height);
+    void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
 
-    void                    setLayerStack(uint32_t stack);
-    void                    setDisplaySize(const int newWidth, const int newHeight);
-    void                    setProjection(int orientation, const Rect& viewport, const Rect& frame);
+    ui::Rotation getPhysicalOrientation() const { return mPhysicalOrientation; }
+    ui::Rotation getOrientation() const { return mOrientation; }
 
-    int                     getOrientation() const { return mOrientation; }
-    static uint32_t         getPrimaryDisplayOrientationTransform();
+    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;
-    const Rect& getScissor() const;
+    const Rect& getSourceClip() const;
     bool needsFiltering() const;
-    uint32_t getLayerStack() const;
+    ui::LayerStack getLayerStack() const;
 
     const std::optional<DisplayId>& getId() const;
     const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
@@ -139,8 +139,8 @@
     /* ------------------------------------------------------------------------
      * Display power mode management.
      */
-    int getPowerMode() const;
-    void setPowerMode(int mode);
+    hardware::graphics::composer::hal::PowerMode getPowerMode() const;
+    void setPowerMode(hardware::graphics::composer::hal::PowerMode mode);
     bool isPoweredOn() const;
 
     ui::Dataspace getCompositionDataSpace() const;
@@ -148,8 +148,8 @@
     /* ------------------------------------------------------------------------
      * Display active config management.
      */
-    int getActiveConfig() const;
-    void setActiveConfig(int mode);
+    HwcConfigIndexType getActiveConfig() const;
+    void setActiveConfig(HwcConfigIndexType mode);
 
     // release HWC resources (if any) for removable displays
     void disconnect();
@@ -162,58 +162,48 @@
     void dump(std::string& result) const;
 
 private:
-    /*
-     *  Constants, set during initialization
-     */
     const sp<SurfaceFlinger> mFlinger;
     const wp<IBinder> mDisplayToken;
     const int32_t mSequenceId;
+    const std::optional<DisplayConnectionType> mConnectionType;
 
-    const int mDisplayInstallOrientation;
     const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
 
     std::string mDisplayName;
-    const bool mIsVirtual;
 
-    /*
-     * Can only accessed from the main thread, these members
-     * don't need synchronization.
-     */
+    const ui::Rotation mPhysicalOrientation;
+    ui::Rotation mOrientation = ui::ROTATION_0;
 
-    // list of visible layers on that display
-    Vector< sp<Layer> > mVisibleLayersSortedByZ;
-    // list of layers needing fences
-    Vector< sp<Layer> > mLayersNeedingFences;
+    static ui::Transform::RotationFlags sPrimaryDisplayRotationFlags;
 
-    /*
-     * Transaction state
-     */
-    static uint32_t displayStateOrientationToTransformOrientation(int orientation);
-    static status_t orientationToTransfrom(int orientation,
-                                           int w, int h, ui::Transform* tr);
-
-    int mOrientation;
-    static uint32_t sPrimaryDisplayOrientation;
-
-    // Current power mode
-    int mPowerMode;
-    // Current active config
-    int mActiveConfig;
+    hardware::graphics::composer::hal::PowerMode mPowerMode =
+            hardware::graphics::composer::hal::PowerMode::OFF;
+    HwcConfigIndexType mActiveConfig;
 
     // TODO(b/74619554): Remove special cases for primary display.
     const bool mIsPrimary;
 };
 
 struct DisplayDeviceState {
-    bool isVirtual() const { return !displayId.has_value(); }
+    struct Physical {
+        DisplayId id;
+        DisplayConnectionType type;
+        hardware::graphics::composer::hal::HWDisplayId hwcDisplayId;
+
+        bool operator==(const Physical& other) const {
+            return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId;
+        }
+    };
+
+    bool isVirtual() const { return !physical; }
 
     int32_t sequenceId = sNextSequenceId++;
-    std::optional<DisplayId> displayId;
+    std::optional<Physical> physical;
     sp<IGraphicBufferProducer> surface;
-    uint32_t layerStack = DisplayDevice::NO_LAYER_STACK;
+    ui::LayerStack layerStack = ui::NO_LAYER_STACK;
     Rect viewport;
     Rect frame;
-    uint8_t orientation = 0;
+    ui::Rotation orientation = ui::ROTATION_0;
     uint32_t width = 0;
     uint32_t height = 0;
     std::string displayName;
@@ -226,59 +216,56 @@
 struct DisplayDeviceCreationArgs {
     // We use a constructor to ensure some of the values are set, without
     // assuming a default value.
-    DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger, const wp<IBinder>& displayToken,
-                              const std::optional<DisplayId>& displayId);
-
+    DisplayDeviceCreationArgs(const sp<SurfaceFlinger>&, const wp<IBinder>& displayToken,
+                              std::shared_ptr<compositionengine::Display>);
     const sp<SurfaceFlinger> flinger;
     const wp<IBinder> displayToken;
-    const std::optional<DisplayId> displayId;
+    const std::shared_ptr<compositionengine::Display> compositionDisplay;
 
     int32_t sequenceId{0};
-    bool isVirtual{false};
+    std::optional<DisplayConnectionType> connectionType;
     bool isSecure{false};
     sp<ANativeWindow> nativeWindow;
     sp<compositionengine::DisplaySurface> displaySurface;
-    int displayInstallOrientation{DisplayState::eOrientationDefault};
+    ui::Rotation physicalOrientation{ui::ROTATION_0};
     bool hasWideColorGamut{false};
     HdrCapabilities hdrCapabilities;
     int32_t supportedPerFrameMetadata{0};
     std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes;
-    int initialPowerMode{HWC_POWER_MODE_NORMAL};
+    hardware::graphics::composer::hal::PowerMode initialPowerMode{
+            hardware::graphics::composer::hal::PowerMode::ON};
     bool isPrimary{false};
 };
 
 class DisplayRenderArea : public RenderArea {
 public:
-    DisplayRenderArea(const sp<const DisplayDevice> device,
-                      ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
-          : DisplayRenderArea(device, device->getBounds(), device->getWidth(), device->getHeight(),
-                              device->getCompositionDataSpace(), rotation) {}
-    DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqWidth,
-                      uint32_t reqHeight, ui::Dataspace reqDataSpace,
-                      ui::Transform::orientation_flags rotation, bool allowSecureLayers = true)
+    DisplayRenderArea(const sp<const DisplayDevice>& display,
+                      RotationFlags rotation = ui::Transform::ROT_0)
+          : DisplayRenderArea(display, display->getBounds(),
+                              static_cast<uint32_t>(display->getWidth()),
+                              static_cast<uint32_t>(display->getHeight()),
+                              display->getCompositionDataSpace(), rotation) {}
+
+    DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, uint32_t reqWidth,
+                      uint32_t reqHeight, ui::Dataspace reqDataSpace, RotationFlags rotation,
+                      bool allowSecureLayers = true)
           : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
-                       getDisplayRotation(rotation, device->getInstallOrientation())),
-            mDevice(device),
+                       display->getViewport(), applyDeviceOrientation(rotation, display)),
+            mDisplay(std::move(display)),
             mSourceCrop(sourceCrop),
             mAllowSecureLayers(allowSecureLayers) {}
 
-    const ui::Transform& getTransform() const override { return mDevice->getTransform(); }
-    Rect getBounds() const override { return mDevice->getBounds(); }
-    int getHeight() const override { return mDevice->getHeight(); }
-    int getWidth() const override { return mDevice->getWidth(); }
-    bool isSecure() const override { return mAllowSecureLayers && mDevice->isSecure(); }
-    const sp<const DisplayDevice> getDisplayDevice() const override { return mDevice; }
+    const ui::Transform& getTransform() const override { return mTransform; }
+    Rect getBounds() const override { return mDisplay->getBounds(); }
+    int getHeight() const override { return mDisplay->getHeight(); }
+    int getWidth() const override { return mDisplay->getWidth(); }
+    bool isSecure() const override { return mAllowSecureLayers && mDisplay->isSecure(); }
+    sp<const DisplayDevice> getDisplayDevice() const override { return mDisplay; }
 
     bool needsFiltering() const override {
-        // check if the projection from the logical display to the physical
-        // display needs filtering
-        if (mDevice->needsFiltering()) {
-            return true;
-        }
-
-        // check if the projection from the logical render area (i.e., the
-        // physical display) to the physical render area requires filtering
-        const Rect sourceCrop = getSourceCrop();
+        // check if the projection from the logical render area
+        // to the physical render area requires filtering
+        const Rect& sourceCrop = getSourceCrop();
         int width = sourceCrop.width();
         int height = sourceCrop.height();
         if (getRotationFlags() & ui::Transform::ROT_90) {
@@ -290,88 +277,78 @@
     Rect getSourceCrop() const override {
         // use the projected display viewport by default.
         if (mSourceCrop.isEmpty()) {
-            return mDevice->getScissor();
+            return mDisplay->getSourceClip();
         }
 
-        // Recompute the device transformation for the source crop.
+        // If there is a source crop provided then it is assumed that the device
+        // was in portrait orientation. This may not logically be true, so
+        // correct for the orientation error by undoing the rotation
+
+        ui::Rotation logicalOrientation = mDisplay->getOrientation();
+        if (logicalOrientation == ui::Rotation::Rotation90) {
+            logicalOrientation = ui::Rotation::Rotation270;
+        } else if (logicalOrientation == ui::Rotation::Rotation270) {
+            logicalOrientation = ui::Rotation::Rotation90;
+        }
+
+        const auto flags = ui::Transform::toRotationFlags(logicalOrientation);
+        int width = mDisplay->getSourceClip().getWidth();
+        int height = mDisplay->getSourceClip().getHeight();
         ui::Transform rotation;
-        ui::Transform translatePhysical;
-        ui::Transform translateLogical;
-        ui::Transform scale;
-        const Rect& viewport = mDevice->getViewport();
-        const Rect& scissor = mDevice->getScissor();
-        const Rect& frame = mDevice->getFrame();
-
-        const int orientation = mDevice->getInstallOrientation();
-        // Install orientation is transparent to the callers.  Apply it now.
-        uint32_t flags = 0x00;
-        switch (orientation) {
-            case DisplayState::eOrientation90:
-                flags = ui::Transform::ROT_90;
-                break;
-            case DisplayState::eOrientation180:
-                flags = ui::Transform::ROT_180;
-                break;
-            case DisplayState::eOrientation270:
-                flags = ui::Transform::ROT_270;
-                break;
-            default:
-                break;
-        }
-        rotation.set(flags, getWidth(), getHeight());
-        translateLogical.set(-viewport.left, -viewport.top);
-        translatePhysical.set(scissor.left, scissor.top);
-        scale.set(frame.getWidth() / float(viewport.getWidth()), 0, 0,
-                  frame.getHeight() / float(viewport.getHeight()));
-        const ui::Transform finalTransform =
-                rotation * translatePhysical * scale * translateLogical;
-        return finalTransform.transform(mSourceCrop);
+        rotation.set(flags, width, height);
+        return rotation.transform(mSourceCrop);
     }
 
 private:
-    // Install orientation is transparent to the callers.  We need to cancel
-    // it out by modifying rotation flags.
-    static ui::Transform::orientation_flags getDisplayRotation(
-            ui::Transform::orientation_flags rotation, int orientation) {
-        if (orientation == DisplayState::eOrientationDefault) {
-            return rotation;
+    static RotationFlags applyDeviceOrientation(RotationFlags orientationFlag,
+                                                const sp<const DisplayDevice>& device) {
+        uint32_t inverseRotate90 = 0;
+        uint32_t inverseReflect = 0;
+
+        // Reverse the logical orientation.
+        ui::Rotation logicalOrientation = device->getOrientation();
+        if (logicalOrientation == ui::Rotation::Rotation90) {
+            logicalOrientation = ui::Rotation::Rotation270;
+        } else if (logicalOrientation == ui::Rotation::Rotation270) {
+            logicalOrientation = ui::Rotation::Rotation90;
         }
 
-        // convert hw orientation into flag presentation
-        // here inverse transform needed
-        uint8_t hw_rot_90 = 0x00;
-        uint8_t hw_flip_hv = 0x00;
+        const ui::Rotation orientation = device->getPhysicalOrientation() + logicalOrientation;
+
         switch (orientation) {
-            case DisplayState::eOrientation90:
-                hw_rot_90 = ui::Transform::ROT_90;
-                hw_flip_hv = ui::Transform::ROT_180;
+            case ui::ROTATION_0:
+                return orientationFlag;
+
+            case ui::ROTATION_90:
+                inverseRotate90 = ui::Transform::ROT_90;
+                inverseReflect = ui::Transform::ROT_180;
                 break;
-            case DisplayState::eOrientation180:
-                hw_flip_hv = ui::Transform::ROT_180;
+
+            case ui::ROTATION_180:
+                inverseReflect = ui::Transform::ROT_180;
                 break;
-            case DisplayState::eOrientation270:
-                hw_rot_90 = ui::Transform::ROT_90;
+
+            case ui::ROTATION_270:
+                inverseRotate90 = ui::Transform::ROT_90;
                 break;
         }
 
-        // transform flags operation
-        // 1) flip H V if both have ROT_90 flag
-        // 2) XOR these flags
-        uint8_t rotation_rot_90 = rotation & ui::Transform::ROT_90;
-        uint8_t rotation_flip_hv = rotation & ui::Transform::ROT_180;
-        if (rotation_rot_90 & hw_rot_90) {
-            rotation_flip_hv = (~rotation_flip_hv) & ui::Transform::ROT_180;
+        const uint32_t rotate90 = orientationFlag & ui::Transform::ROT_90;
+        uint32_t reflect = orientationFlag & ui::Transform::ROT_180;
+
+        // Apply reflection for double rotation.
+        if (rotate90 & inverseRotate90) {
+            reflect = ~reflect & ui::Transform::ROT_180;
         }
 
-        return static_cast<ui::Transform::orientation_flags>(
-                (rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv));
+        return static_cast<RotationFlags>((rotate90 ^ inverseRotate90) |
+                                          (reflect ^ inverseReflect));
     }
 
-    const sp<const DisplayDevice> mDevice;
+    const sp<const DisplayDevice> mDisplay;
     const Rect mSourceCrop;
     const bool mAllowSecureLayers;
+    const ui::Transform mTransform = ui::Transform();
 };
 
-}; // namespace android
-
-#endif // ANDROID_DISPLAY_DEVICE_H
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 7f47a2e..a3f1b52 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -14,12 +14,18 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "HwcComposer"
 
-#include <inttypes.h>
 #include <log/log.h>
 
+#include <algorithm>
+#include <cinttypes>
+
 #include "ComposerHal.h"
 
 #include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
@@ -93,6 +99,7 @@
 
 // assume NO_RESOURCES when Status::isOk returns false
 constexpr Error kDefaultError = Error::NO_RESOURCES;
+constexpr V2_4::Error kDefaultError_2_4 = static_cast<V2_4::Error>(kDefaultError);
 
 template<typename T, typename U>
 T unwrapRet(Return<T>& ret, const U& default_val)
@@ -110,6 +117,7 @@
 
 namespace impl {
 
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 Composer::CommandWriter::CommandWriter(uint32_t initialMaxSize)
     : CommandWriterBase(initialMaxSize) {}
 
@@ -160,6 +168,7 @@
     writeSigned(static_cast<int32_t>(metadata.format));
     write64(metadata.usage);
 }
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 
 Composer::Composer(const std::string& serviceName)
     : mWriter(kWriterInitialSize),
@@ -171,7 +180,16 @@
         LOG_ALWAYS_FATAL("failed to get hwcomposer service");
     }
 
-    if (sp<IComposer> composer_2_3 = IComposer::castFrom(mComposer)) {
+    if (sp<IComposer> composer_2_4 = IComposer::castFrom(mComposer)) {
+        composer_2_4->createClient_2_4([&](const auto& tmpError, const auto& tmpClient) {
+            if (tmpError == V2_4::Error::NONE) {
+                mClient = tmpClient;
+                mClient_2_2 = tmpClient;
+                mClient_2_3 = tmpClient;
+                mClient_2_4 = tmpClient;
+            }
+        });
+    } else if (sp<V2_3::IComposer> composer_2_3 = V2_3::IComposer::castFrom(mComposer)) {
         composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
             if (tmpError == Error::NONE) {
                 mClient = tmpClient;
@@ -198,12 +216,14 @@
         LOG_ALWAYS_FATAL("failed to create composer client");
     }
 
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
     if (mIsUsingVrComposer) {
         sp<IVrComposerClient> vrClient = IVrComposerClient::castFrom(mClient);
         if (vrClient == nullptr) {
             LOG_ALWAYS_FATAL("failed to create vr composer client");
         }
     }
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 }
 
 Composer::~Composer() = default;
@@ -231,7 +251,12 @@
 void Composer::registerCallback(const sp<IComposerCallback>& callback)
 {
     android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
-    auto ret = mClient->registerCallback(callback);
+    auto ret = [&]() {
+        if (mClient_2_4) {
+            return mClient_2_4->registerCallback_2_4(callback);
+        }
+        return mClient->registerCallback(callback);
+    }();
     if (!ret.isOk()) {
         ALOGE("failed to register IComposerCallback");
     }
@@ -397,15 +422,28 @@
         IComposerClient::Attribute attribute, int32_t* outValue)
 {
     Error error = kDefaultError;
-    mClient->getDisplayAttribute(display, config, attribute,
-            [&](const auto& tmpError, const auto& tmpValue) {
-                error = tmpError;
-                if (error != Error::NONE) {
-                    return;
-                }
+    if (mClient_2_4) {
+        mClient_2_4->getDisplayAttribute_2_4(display, config, attribute,
+                                             [&](const auto& tmpError, const auto& tmpValue) {
+                                                 error = static_cast<Error>(tmpError);
+                                                 if (error != Error::NONE) {
+                                                     return;
+                                                 }
 
-                *outValue = tmpValue;
-            });
+                                                 *outValue = tmpValue;
+                                             });
+    } else {
+        mClient->getDisplayAttribute(display, config,
+                                     static_cast<V2_1::IComposerClient::Attribute>(attribute),
+                                     [&](const auto& tmpError, const auto& tmpValue) {
+                                         error = tmpError;
+                                         if (error != Error::NONE) {
+                                             return;
+                                         }
+
+                                         *outValue = tmpValue;
+                                     });
+    }
 
     return error;
 }
@@ -452,23 +490,6 @@
     return Error::NONE;
 }
 
-Error Composer::getDisplayType(Display display,
-        IComposerClient::DisplayType* outType)
-{
-    Error error = kDefaultError;
-    mClient->getDisplayType(display,
-            [&](const auto& tmpError, const auto& tmpType) {
-                error = tmpError;
-                if (error != Error::NONE) {
-                    return;
-                }
-
-                *outType = tmpType;
-            });
-
-    return error;
-}
-
 Error Composer::getDozeSupport(Display display, bool* outSupport)
 {
     Error error = kDefaultError;
@@ -565,17 +586,20 @@
         const std::vector<IComposerClient::Rect>& damage)
 {
     mWriter.selectDisplay(display);
+
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
     if (mIsUsingVrComposer && target.get()) {
         IVrComposerClient::BufferMetadata metadata = {
-            .width = target->getWidth(),
-            .height = target->getHeight(),
-            .stride = target->getStride(),
-            .layerCount = target->getLayerCount(),
-            .format = static_cast<types::V1_0::PixelFormat>(target->getPixelFormat()),
-            .usage = target->getUsage(),
+                .width = target->getWidth(),
+                .height = target->getHeight(),
+                .stride = target->getStride(),
+                .layerCount = target->getLayerCount(),
+                .format = static_cast<types::V1_2::PixelFormat>(target->getPixelFormat()),
+                .usage = target->getUsage(),
         };
         mWriter.setClientTargetMetadata(metadata);
     }
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 
     const native_handle_t* handle = nullptr;
     if (target.get()) {
@@ -695,17 +719,20 @@
 {
     mWriter.selectDisplay(display);
     mWriter.selectLayer(layer);
+
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
     if (mIsUsingVrComposer && buffer.get()) {
         IVrComposerClient::BufferMetadata metadata = {
-            .width = buffer->getWidth(),
-            .height = buffer->getHeight(),
-            .stride = buffer->getStride(),
-            .layerCount = buffer->getLayerCount(),
-            .format = static_cast<types::V1_0::PixelFormat>(buffer->getPixelFormat()),
-            .usage = buffer->getUsage(),
+                .width = buffer->getWidth(),
+                .height = buffer->getHeight(),
+                .stride = buffer->getStride(),
+                .layerCount = buffer->getLayerCount(),
+                .format = static_cast<types::V1_2::PixelFormat>(buffer->getPixelFormat()),
+                .usage = buffer->getUsage(),
         };
         mWriter.setLayerBufferMetadata(metadata);
     }
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 
     const native_handle_t* handle = nullptr;
     if (buffer.get()) {
@@ -823,6 +850,7 @@
     return Error::NONE;
 }
 
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 Error Composer::setLayerInfo(Display display, Layer layer, uint32_t type,
                              uint32_t appId)
 {
@@ -833,6 +861,15 @@
     }
     return Error::NONE;
 }
+#else
+Error Composer::setLayerInfo(Display display, Layer layer, uint32_t, uint32_t) {
+    if (mIsUsingVrComposer) {
+        mWriter.selectDisplay(display);
+        mWriter.selectLayer(layer);
+    }
+    return Error::NONE;
+}
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 
 Error Composer::execute()
 {
@@ -1093,23 +1130,6 @@
     return error;
 }
 
-Error Composer::getDisplayCapabilities(Display display,
-                                       std::vector<DisplayCapability>* outCapabilities) {
-    if (!mClient_2_3) {
-        return Error::UNSUPPORTED;
-    }
-    Error error = kDefaultError;
-    mClient_2_3->getDisplayCapabilities(display,
-                                        [&](const auto& tmpError, const auto& tmpCapabilities) {
-                                            error = tmpError;
-                                            if (error != Error::NONE) {
-                                                return;
-                                            }
-                                            *outCapabilities = tmpCapabilities;
-                                        });
-    return error;
-}
-
 Error Composer::setDisplayContentSamplingEnabled(Display display, bool enabled,
                                                  uint8_t componentMask, uint64_t maxFrames) {
     if (!mClient_2_3) {
@@ -1167,6 +1187,179 @@
     return mClient_2_3->setDisplayBrightness(display, brightness);
 }
 
+// Composer HAL 2.4
+
+Error Composer::getDisplayCapabilities(Display display,
+                                       std::vector<DisplayCapability>* outCapabilities) {
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+
+    V2_4::Error error = kDefaultError_2_4;
+    if (mClient_2_4) {
+        mClient_2_4->getDisplayCapabilities_2_4(display,
+                                                [&](const auto& tmpError, const auto& tmpCaps) {
+                                                    error = tmpError;
+                                                    if (error != V2_4::Error::NONE) {
+                                                        return;
+                                                    }
+                                                    *outCapabilities = tmpCaps;
+                                                });
+    } else {
+        mClient_2_3
+                ->getDisplayCapabilities(display, [&](const auto& tmpError, const auto& tmpCaps) {
+                    error = static_cast<V2_4::Error>(tmpError);
+                    if (error != V2_4::Error::NONE) {
+                        return;
+                    }
+
+                    outCapabilities->resize(tmpCaps.size());
+                    std::transform(tmpCaps.begin(), tmpCaps.end(), outCapabilities->begin(),
+                                   [](auto cap) { return static_cast<DisplayCapability>(cap); });
+                });
+    }
+
+    return static_cast<Error>(error);
+}
+
+V2_4::Error Composer::getDisplayConnectionType(Display display,
+                                               IComposerClient::DisplayConnectionType* outType) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError_2_4;
+    mClient_2_4->getDisplayConnectionType(display, [&](const auto& tmpError, const auto& tmpType) {
+        error = tmpError;
+        if (error != V2_4::Error::NONE) {
+            return;
+        }
+
+        *outType = tmpType;
+    });
+
+    return error;
+}
+
+V2_4::Error Composer::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError_2_4;
+    mClient_2_4->getDisplayVsyncPeriod(display,
+                                       [&](const auto& tmpError, const auto& tmpVsyncPeriod) {
+                                           error = tmpError;
+                                           if (error != Error::NONE) {
+                                               return;
+                                           }
+
+                                           *outVsyncPeriod = tmpVsyncPeriod;
+                                       });
+
+    return error;
+}
+
+V2_4::Error Composer::setActiveConfigWithConstraints(
+        Display display, Config config,
+        const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+        VsyncPeriodChangeTimeline* outTimeline) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError_2_4;
+    mClient_2_4->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints,
+                                                [&](const auto& tmpError, const auto& tmpTimeline) {
+                                                    error = tmpError;
+                                                    if (error != Error::NONE) {
+                                                        return;
+                                                    }
+
+                                                    *outTimeline = tmpTimeline;
+                                                });
+
+    return error;
+}
+
+V2_4::Error Composer::setAutoLowLatencyMode(Display display, bool on) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    return mClient_2_4->setAutoLowLatencyMode(display, on);
+}
+
+V2_4::Error Composer::getSupportedContentTypes(
+        Display displayId, std::vector<IComposerClient::ContentType>* outSupportedContentTypes) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError_2_4;
+    mClient_2_4->getSupportedContentTypes(displayId,
+                                          [&](const auto& tmpError,
+                                              const auto& tmpSupportedContentTypes) {
+                                              error = tmpError;
+                                              if (error != Error::NONE) {
+                                                  return;
+                                              }
+
+                                              *outSupportedContentTypes = tmpSupportedContentTypes;
+                                          });
+    return error;
+}
+
+V2_4::Error Composer::setContentType(Display display, IComposerClient::ContentType contentType) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    return mClient_2_4->setContentType(display, contentType);
+}
+
+V2_4::Error Composer::setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
+                                              bool mandatory, const std::vector<uint8_t>& value) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerGenericMetadata(key, mandatory, value);
+    return Error::NONE;
+}
+
+V2_4::Error Composer::getLayerGenericMetadataKeys(
+        std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+    Error error = kDefaultError_2_4;
+    mClient_2_4->getLayerGenericMetadataKeys([&](const auto& tmpError, const auto& tmpKeys) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+
+        *outKeys = tmpKeys;
+    });
+    return error;
+}
+
+Error Composer::getClientTargetProperty(
+        Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
+    mReader.takeClientTargetProperty(display, outClientTargetProperty);
+    return Error::NONE;
+}
+
 CommandReader::~CommandReader()
 {
     resetData();
@@ -1180,8 +1373,7 @@
     uint16_t length = 0;
 
     while (!isEmpty()) {
-        auto command_2_1 = reinterpret_cast<V2_1::IComposerClient::Command*>(&command);
-        if (!beginCommand(command_2_1, &length)) {
+        if (!beginCommand(&command, &length)) {
             break;
         }
 
@@ -1208,6 +1400,9 @@
         case IComposerClient::Command ::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT:
             parsed = parseSetPresentOrValidateDisplayResult(length);
             break;
+        case IComposerClient::Command::SET_CLIENT_TARGET_PROPERTY:
+            parsed = parseSetClientTargetProperty(length);
+            break;
         default:
             parsed = false;
             break;
@@ -1345,6 +1540,15 @@
     return true;
 }
 
+bool CommandReader::parseSetClientTargetProperty(uint16_t length) {
+    if (length != CommandWriterBase::kSetClientTargetPropertyLength || !mCurrentReturnData) {
+        return false;
+    }
+    mCurrentReturnData->clientTargetProperty.pixelFormat = static_cast<PixelFormat>(readSigned());
+    mCurrentReturnData->clientTargetProperty.dataspace = static_cast<Dataspace>(readSigned());
+    return true;
+}
+
 void CommandReader::resetData()
 {
     mErrors.clear();
@@ -1464,8 +1668,24 @@
     *state = data.presentOrValidateState;
 }
 
+void CommandReader::takeClientTargetProperty(
+        Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
+    auto found = mReturnData.find(display);
+
+    // If not found, return the default values.
+    if (found == mReturnData.end()) {
+        outClientTargetProperty->pixelFormat = PixelFormat::RGBA_8888;
+        outClientTargetProperty->dataspace = Dataspace::UNKNOWN;
+        return;
+    }
+
+    ReturnData& data = found->second;
+    *outClientTargetProperty = data.clientTargetProperty;
+}
+
 } // namespace impl
-
 } // namespace Hwc2
-
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index c4e952b..00ef782 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -23,28 +23,40 @@
 #include <utility>
 #include <vector>
 
-#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
+#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 #include <android/hardware/graphics/common/1.1/types.h>
-#include <android/hardware/graphics/composer/2.3/IComposer.h>
-#include <android/hardware/graphics/composer/2.3/IComposerClient.h>
-#include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
+#include <android/hardware/graphics/composer/2.4/IComposer.h>
+#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+#include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/StrongPointer.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android {
 
 namespace Hwc2 {
 
-using frameworks::vr::composer::V1_0::IVrComposerClient;
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
+using frameworks::vr::composer::V2_0::IVrComposerClient;
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 
 namespace types = hardware::graphics::common;
 
 namespace V2_1 = hardware::graphics::composer::V2_1;
 namespace V2_2 = hardware::graphics::composer::V2_2;
 namespace V2_3 = hardware::graphics::composer::V2_3;
+namespace V2_4 = hardware::graphics::composer::V2_4;
 
 using types::V1_0::ColorTransform;
 using types::V1_0::Transform;
@@ -57,12 +69,14 @@
 using V2_1::Config;
 using V2_1::Display;
 using V2_1::Error;
-using V2_1::IComposerCallback;
 using V2_1::Layer;
-using V2_3::CommandReaderBase;
-using V2_3::CommandWriterBase;
-using V2_3::IComposer;
-using V2_3::IComposerClient;
+using V2_4::CommandReaderBase;
+using V2_4::CommandWriterBase;
+using V2_4::IComposer;
+using V2_4::IComposerCallback;
+using V2_4::IComposerClient;
+using V2_4::VsyncPeriodChangeTimeline;
+using V2_4::VsyncPeriodNanos;
 using DisplayCapability = IComposerClient::DisplayCapability;
 using PerFrameMetadata = IComposerClient::PerFrameMetadata;
 using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
@@ -114,7 +128,6 @@
                                      std::vector<Layer>* outLayers,
                                      std::vector<uint32_t>* outLayerRequestMasks) = 0;
 
-    virtual Error getDisplayType(Display display, IComposerClient::DisplayType* outType) = 0;
     virtual Error getDozeSupport(Display display, bool* outSupport) = 0;
     virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
                                      float* outMaxLuminance, float* outMaxAverageLuminance,
@@ -199,11 +212,36 @@
                                                    uint8_t componentMask, uint64_t maxFrames) = 0;
     virtual Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
                                             DisplayedFrameStats* outStats) = 0;
-    virtual Error getDisplayCapabilities(Display display,
-                                         std::vector<DisplayCapability>* outCapabilities) = 0;
     virtual Error setLayerPerFrameMetadataBlobs(
             Display display, Layer layer, const std::vector<PerFrameMetadataBlob>& metadata) = 0;
     virtual Error setDisplayBrightness(Display display, float brightness) = 0;
+
+    // Composer HAL 2.4
+    virtual bool isVsyncPeriodSwitchSupported() = 0;
+    virtual Error getDisplayCapabilities(Display display,
+                                         std::vector<DisplayCapability>* outCapabilities) = 0;
+    virtual V2_4::Error getDisplayConnectionType(
+            Display display, IComposerClient::DisplayConnectionType* outType) = 0;
+    virtual V2_4::Error getDisplayVsyncPeriod(Display display,
+                                              VsyncPeriodNanos* outVsyncPeriod) = 0;
+    virtual V2_4::Error setActiveConfigWithConstraints(
+            Display display, Config config,
+            const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+            VsyncPeriodChangeTimeline* outTimeline) = 0;
+
+    virtual V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) = 0;
+    virtual V2_4::Error getSupportedContentTypes(
+            Display displayId,
+            std::vector<IComposerClient::ContentType>* outSupportedContentTypes) = 0;
+    virtual V2_4::Error setContentType(Display displayId,
+                                       IComposerClient::ContentType contentType) = 0;
+    virtual V2_4::Error setLayerGenericMetadata(Display display, Layer layer,
+                                                const std::string& key, bool mandatory,
+                                                const std::vector<uint8_t>& value) = 0;
+    virtual V2_4::Error getLayerGenericMetadataKeys(
+            std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0;
+    virtual Error getClientTargetProperty(
+            Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) = 0;
 };
 
 namespace impl {
@@ -246,6 +284,10 @@
     // Get what stage succeeded during PresentOrValidate: Present or Validate
     void takePresentOrValidateStage(Display display, uint32_t * state);
 
+    // Get the client target properties requested by hardware composer.
+    void takeClientTargetProperty(Display display,
+                                  IComposerClient::ClientTargetProperty* outClientTargetProperty);
+
 private:
     void resetData();
 
@@ -256,6 +298,7 @@
     bool parseSetPresentFence(uint16_t length);
     bool parseSetReleaseFences(uint16_t length);
     bool parseSetPresentOrValidateDisplayResult(uint16_t length);
+    bool parseSetClientTargetProperty(uint16_t length);
 
     struct ReturnData {
         uint32_t displayRequests = 0;
@@ -272,6 +315,13 @@
         std::vector<int> releaseFences;
 
         uint32_t presentOrValidateState;
+
+        // Composer 2.4 implementation can return a client target property
+        // structure to indicate the client target properties that hardware
+        // composer requests. The composer client must change the client target
+        // properties to match this request.
+        IComposerClient::ClientTargetProperty clientTargetProperty{PixelFormat::RGBA_8888,
+                                                                   Dataspace::UNKNOWN};
     };
 
     std::vector<CommandError> mErrors;
@@ -330,7 +380,6 @@
                              std::vector<Layer>* outLayers,
                              std::vector<uint32_t>* outLayerRequestMasks) override;
 
-    Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
     Error getDozeSupport(Display display, bool* outSupport) override;
     Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
                              float* outMaxAverageLuminance, float* outMinLuminance) override;
@@ -410,14 +459,38 @@
                                            uint64_t maxFrames) override;
     Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp,
                                     DisplayedFrameStats* outStats) override;
-    Error getDisplayCapabilities(Display display,
-                                 std::vector<DisplayCapability>* outCapabilities) override;
     Error setLayerPerFrameMetadataBlobs(
             Display display, Layer layer,
             const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override;
     Error setDisplayBrightness(Display display, float brightness) override;
 
+    // Composer HAL 2.4
+    bool isVsyncPeriodSwitchSupported() override { return mClient_2_4 != nullptr; }
+    Error getDisplayCapabilities(Display display,
+                                 std::vector<DisplayCapability>* outCapabilities) override;
+    V2_4::Error getDisplayConnectionType(Display display,
+                                         IComposerClient::DisplayConnectionType* outType) override;
+    V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
+    V2_4::Error setActiveConfigWithConstraints(
+            Display display, Config config,
+            const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+            VsyncPeriodChangeTimeline* outTimeline) override;
+    V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) override;
+    V2_4::Error getSupportedContentTypes(
+            Display displayId,
+            std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+    V2_4::Error setContentType(Display displayId,
+                               IComposerClient::ContentType contentType) override;
+    V2_4::Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
+                                        bool mandatory, const std::vector<uint8_t>& value) override;
+    V2_4::Error getLayerGenericMetadataKeys(
+            std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
+    Error getClientTargetProperty(
+            Display display,
+            IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
+
 private:
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
     class CommandWriter : public CommandWriterBase {
     public:
         explicit CommandWriter(uint32_t initialMaxSize);
@@ -433,6 +506,13 @@
         void writeBufferMetadata(
                 const IVrComposerClient::BufferMetadata& metadata);
     };
+#else
+    class CommandWriter : public CommandWriterBase {
+    public:
+        explicit CommandWriter(uint32_t initialMaxSize) : CommandWriterBase(initialMaxSize) {}
+        ~CommandWriter() override {}
+    };
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 
     // Many public functions above simply write a command into the command
     // queue to batch the calls.  validateDisplay and presentDisplay will call
@@ -443,7 +523,8 @@
 
     sp<V2_1::IComposerClient> mClient;
     sp<V2_2::IComposerClient> mClient_2_2;
-    sp<IComposerClient> mClient_2_3;
+    sp<V2_3::IComposerClient> mClient_2_3;
+    sp<IComposerClient> mClient_2_4;
 
     // 64KiB minus a small space for metadata such as read/write pointers
     static constexpr size_t kWriterInitialSize =
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index ba7818d..4dfc743 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "DisplayIdentification"
 
@@ -31,6 +35,7 @@
 
 using byte_view = std::basic_string_view<uint8_t>;
 
+constexpr size_t kEdidBlockSize = 128;
 constexpr size_t kEdidHeaderLength = 5;
 
 constexpr uint16_t kFallbackEdidManufacturerId = 0;
@@ -64,15 +69,101 @@
     return letter < 'A' || letter > 'Z' ? '\0' : letter;
 }
 
+DeviceProductInfo buildDeviceProductInfo(const Edid& edid) {
+    DeviceProductInfo info;
+    std::copy(edid.displayName.begin(), edid.displayName.end(), info.name.begin());
+    info.name[edid.displayName.size()] = '\0';
+
+    const auto productId = std::to_string(edid.productId);
+    std::copy(productId.begin(), productId.end(), info.productId.begin());
+    info.productId[productId.size()] = '\0';
+    info.manufacturerPnpId = edid.pnpId;
+
+    constexpr uint8_t kModelYearFlag = 0xff;
+    constexpr uint32_t kYearOffset = 1990;
+
+    const auto year = edid.manufactureOrModelYear + kYearOffset;
+    if (edid.manufactureWeek == kModelYearFlag) {
+        info.manufactureOrModelDate = DeviceProductInfo::ModelYear{.year = year};
+    } else if (edid.manufactureWeek == 0) {
+        DeviceProductInfo::ManufactureYear date;
+        date.year = year;
+        info.manufactureOrModelDate = date;
+    } else {
+        DeviceProductInfo::ManufactureWeekAndYear date;
+        date.year = year;
+        date.week = edid.manufactureWeek;
+        info.manufactureOrModelDate = date;
+    }
+
+    if (edid.cea861Block && edid.cea861Block->hdmiVendorDataBlock) {
+        const auto& address = edid.cea861Block->hdmiVendorDataBlock->physicalAddress;
+        info.relativeAddress = {address.a, address.b, address.c, address.d};
+    } else {
+        info.relativeAddress = DeviceProductInfo::NO_RELATIVE_ADDRESS;
+    }
+    return info;
+}
+
+Cea861ExtensionBlock parseCea861Block(const byte_view& block) {
+    Cea861ExtensionBlock cea861Block;
+
+    constexpr size_t kRevisionNumberOffset = 1;
+    cea861Block.revisionNumber = block[kRevisionNumberOffset];
+
+    constexpr size_t kDetailedTimingDescriptorsOffset = 2;
+    const size_t dtdStart =
+            std::min(kEdidBlockSize, static_cast<size_t>(block[kDetailedTimingDescriptorsOffset]));
+
+    // Parse data blocks.
+    for (size_t dataBlockOffset = 4; dataBlockOffset < dtdStart;) {
+        const uint8_t header = block[dataBlockOffset];
+        const uint8_t tag = header >> 5;
+        const size_t bodyLength = header & 0b11111;
+        constexpr size_t kDataBlockHeaderSize = 1;
+        const size_t dataBlockSize = bodyLength + kDataBlockHeaderSize;
+
+        if (block.size() < dataBlockOffset + dataBlockSize) {
+            ALOGW("Invalid EDID: CEA 861 data block is truncated.");
+            break;
+        }
+
+        const byte_view dataBlock(block.data() + dataBlockOffset, dataBlockSize);
+        constexpr uint8_t kVendorSpecificDataBlockTag = 0x3;
+
+        if (tag == kVendorSpecificDataBlockTag) {
+            const uint32_t ieeeRegistrationId =
+                    dataBlock[1] | (dataBlock[2] << 8) | (dataBlock[3] << 16);
+            constexpr uint32_t kHdmiIeeeRegistrationId = 0xc03;
+
+            if (ieeeRegistrationId == kHdmiIeeeRegistrationId) {
+                const uint8_t a = dataBlock[4] >> 4;
+                const uint8_t b = dataBlock[4] & 0b1111;
+                const uint8_t c = dataBlock[5] >> 4;
+                const uint8_t d = dataBlock[5] & 0b1111;
+                cea861Block.hdmiVendorDataBlock =
+                        HdmiVendorDataBlock{.physicalAddress = HdmiPhysicalAddress{a, b, c, d}};
+            } else {
+                ALOGV("Ignoring vendor specific data block for vendor with IEEE OUI %x",
+                      ieeeRegistrationId);
+            }
+        } else {
+            ALOGV("Ignoring CEA-861 data block with tag %x", tag);
+        }
+        dataBlockOffset += bodyLength + kDataBlockHeaderSize;
+    }
+
+    return cea861Block;
+}
+
 } // namespace
 
 uint16_t DisplayId::manufacturerId() const {
     return static_cast<uint16_t>(value >> 40);
 }
 
-DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash) {
-    return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(displayNameHash) << 8) |
-            port};
+DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash) {
+    return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(modelHash) << 8) | port};
 }
 
 bool isEdid(const DisplayIdentificationData& data) {
@@ -82,13 +173,12 @@
 }
 
 std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) {
-    constexpr size_t kMinLength = 128;
-    if (edid.size() < kMinLength) {
+    if (edid.size() < kEdidBlockSize) {
         ALOGW("Invalid EDID: structure is truncated.");
         // Attempt parsing even if EDID is malformed.
     } else {
-        ALOGW_IF(edid[126] != 0, "EDID extensions are currently unsupported.");
-        ALOGW_IF(std::accumulate(edid.begin(), edid.begin() + kMinLength, static_cast<uint8_t>(0)),
+        ALOGW_IF(std::accumulate(edid.begin(), edid.begin() + kEdidBlockSize,
+                                 static_cast<uint8_t>(0)),
                  "Invalid EDID: structure does not checksum.");
     }
 
@@ -108,6 +198,31 @@
         return {};
     }
 
+    constexpr size_t kProductIdOffset = 10;
+    if (edid.size() < kProductIdOffset + sizeof(uint16_t)) {
+        ALOGE("Invalid EDID: product ID is truncated.");
+        return {};
+    }
+    const uint16_t productId = edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8);
+
+    constexpr size_t kManufactureWeekOffset = 16;
+    if (edid.size() < kManufactureWeekOffset + sizeof(uint8_t)) {
+        ALOGE("Invalid EDID: manufacture week is truncated.");
+        return {};
+    }
+    const uint8_t manufactureWeek = edid[kManufactureWeekOffset];
+    ALOGW_IF(0x37 <= manufactureWeek && manufactureWeek <= 0xfe,
+             "Invalid EDID: week of manufacture cannot be in the range [0x37, 0xfe].");
+
+    constexpr size_t kManufactureYearOffset = 17;
+    if (edid.size() < kManufactureYearOffset + sizeof(uint8_t)) {
+        ALOGE("Invalid EDID: manufacture year is truncated.");
+        return {};
+    }
+    const uint8_t manufactureOrModelYear = edid[kManufactureYearOffset];
+    ALOGW_IF(manufactureOrModelYear <= 0xf,
+             "Invalid EDID: model year or manufacture year cannot be in the range [0x0, 0xf].");
+
     constexpr size_t kDescriptorOffset = 54;
     if (edid.size() < kDescriptorOffset) {
         ALOGE("Invalid EDID: descriptors are missing.");
@@ -123,6 +238,7 @@
 
     constexpr size_t kDescriptorCount = 4;
     constexpr size_t kDescriptorLength = 18;
+    static_assert(kDescriptorLength - kEdidHeaderLength < DeviceProductInfo::TEXT_BUFFER_SIZE);
 
     for (size_t i = 0; i < kDescriptorCount; i++) {
         if (view.size() < kDescriptorLength) {
@@ -149,20 +265,62 @@
         view.remove_prefix(kDescriptorLength);
     }
 
-    if (displayName.empty()) {
+    std::string_view modelString = displayName;
+
+    if (modelString.empty()) {
         ALOGW("Invalid EDID: falling back to serial number due to missing display name.");
-        displayName = serialNumber;
+        modelString = serialNumber;
     }
-    if (displayName.empty()) {
+    if (modelString.empty()) {
         ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number.");
-        displayName = asciiText;
+        modelString = asciiText;
     }
-    if (displayName.empty()) {
+    if (modelString.empty()) {
         ALOGE("Invalid EDID: display name and fallback descriptors are missing.");
         return {};
     }
 
-    return Edid{manufacturerId, *pnpId, displayName};
+    // Hash model string instead of using product code or (integer) serial number, since the latter
+    // have been observed to change on some displays with multiple inputs.
+    const auto modelHash = static_cast<uint32_t>(std::hash<std::string_view>()(modelString));
+
+    // Parse extension blocks.
+    std::optional<Cea861ExtensionBlock> cea861Block;
+    if (edid.size() < kEdidBlockSize) {
+        ALOGW("Invalid EDID: block 0 is truncated.");
+    } else {
+        constexpr size_t kNumExtensionsOffset = 126;
+        const size_t numExtensions = edid[kNumExtensionsOffset];
+        view = byte_view(edid.data(), edid.size());
+        for (size_t blockNumber = 1; blockNumber <= numExtensions; blockNumber++) {
+            view.remove_prefix(kEdidBlockSize);
+            if (view.size() < kEdidBlockSize) {
+                ALOGW("Invalid EDID: block %zu is truncated.", blockNumber);
+                break;
+            }
+
+            const byte_view block(view.data(), kEdidBlockSize);
+            ALOGW_IF(std::accumulate(block.begin(), block.end(), static_cast<uint8_t>(0)),
+                     "Invalid EDID: block %zu does not checksum.", blockNumber);
+            const uint8_t tag = block[0];
+
+            constexpr uint8_t kCea861BlockTag = 0x2;
+            if (tag == kCea861BlockTag) {
+                cea861Block = parseCea861Block(block);
+            } else {
+                ALOGV("Ignoring block number %zu with tag %x.", blockNumber, tag);
+            }
+        }
+    }
+
+    return Edid{.manufacturerId = manufacturerId,
+                .productId = productId,
+                .pnpId = *pnpId,
+                .modelHash = modelHash,
+                .displayName = displayName,
+                .manufactureOrModelYear = manufactureOrModelYear,
+                .manufactureWeek = manufactureWeek,
+                .cea861Block = cea861Block};
 }
 
 std::optional<PnpId> getPnpId(uint16_t manufacturerId) {
@@ -188,11 +346,10 @@
         return {};
     }
 
-    // Hash display name instead of using product code or serial number, since the latter have been
-    // observed to change on some displays with multiple inputs.
-    const auto hash = static_cast<uint32_t>(std::hash<std::string_view>()(edid->displayName));
-    return DisplayIdentificationInfo{DisplayId::fromEdid(port, edid->manufacturerId, hash),
-                                     std::string(edid->displayName)};
+    const auto displayId = DisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
+    return DisplayIdentificationInfo{.id = displayId,
+                                     .name = std::string(edid->displayName),
+                                     .deviceProductInfo = buildDeviceProductInfo(*edid)};
 }
 
 DisplayId getFallbackDisplayId(uint8_t port) {
@@ -204,3 +361,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
index d63cd79..4819d1d 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -23,7 +23,11 @@
 #include <string_view>
 #include <vector>
 
-#include <ui/GraphicTypes.h>
+#include <ui/DeviceProductInfo.h>
+#include <ui/PhysicalDisplayId.h>
+
+#define LEGACY_DISPLAY_TYPE_PRIMARY 0
+#define LEGACY_DISPLAY_TYPE_EXTERNAL 1
 
 namespace android {
 
@@ -33,7 +37,7 @@
 
     uint16_t manufacturerId() const;
 
-    static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash);
+    static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash);
 };
 
 inline bool operator==(DisplayId lhs, DisplayId rhs) {
@@ -53,15 +57,38 @@
 struct DisplayIdentificationInfo {
     DisplayId id;
     std::string name;
+    std::optional<DeviceProductInfo> deviceProductInfo;
 };
 
-// NUL-terminated plug and play ID.
-using PnpId = std::array<char, 4>;
+struct ExtensionBlock {
+    uint8_t tag;
+    uint8_t revisionNumber;
+};
+
+struct HdmiPhysicalAddress {
+    // The address describes the path from the display sink in the network of connected HDMI
+    // devices. The format of the address is "a.b.c.d". For example, address 2.1.0.0 means we are
+    // connected to port 1 of a device which is connected to port 2 of the sink.
+    uint8_t a, b, c, d;
+};
+
+struct HdmiVendorDataBlock {
+    HdmiPhysicalAddress physicalAddress;
+};
+
+struct Cea861ExtensionBlock : ExtensionBlock {
+    std::optional<HdmiVendorDataBlock> hdmiVendorDataBlock;
+};
 
 struct Edid {
     uint16_t manufacturerId;
+    uint16_t productId;
     PnpId pnpId;
+    uint32_t modelHash;
     std::string_view displayName;
+    uint8_t manufactureOrModelYear;
+    uint8_t manufactureWeek;
+    std::optional<Cea861ExtensionBlock> cea861Block;
 };
 
 bool isEdid(const DisplayIdentificationData&);
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 7370b0c..4c3b3e5 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -15,6 +15,10 @@
  ** limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "FramebufferSurface"
@@ -53,9 +57,12 @@
  */
 
 FramebufferSurface::FramebufferSurface(HWComposer& hwc, DisplayId displayId,
-                                       const sp<IGraphicBufferConsumer>& consumer)
+                                       const sp<IGraphicBufferConsumer>& consumer,
+                                       uint32_t maxWidth, uint32_t maxHeight)
       : ConsumerBase(consumer),
         mDisplayId(displayId),
+        mMaxWidth(maxWidth),
+        mMaxHeight(maxHeight),
         mCurrentBufferSlot(-1),
         mCurrentBuffer(),
         mCurrentFence(Fence::NO_FENCE),
@@ -71,14 +78,16 @@
                                        GRALLOC_USAGE_HW_RENDER |
                                        GRALLOC_USAGE_HW_COMPOSER);
     const auto& activeConfig = mHwc.getActiveConfig(displayId);
-    mConsumer->setDefaultBufferSize(activeConfig->getWidth(),
-            activeConfig->getHeight());
+    ui::Size limitedSize =
+            limitFramebufferSize(activeConfig->getWidth(), activeConfig->getHeight());
+    mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
     mConsumer->setMaxAcquiredBufferCount(
             SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
 }
 
-void FramebufferSurface::resizeBuffers(const uint32_t width, const uint32_t height) {
-    mConsumer->setDefaultBufferSize(width, height);
+void FramebufferSurface::resizeBuffers(uint32_t width, uint32_t height) {
+    ui::Size limitedSize = limitFramebufferSize(width, height);
+    mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
 }
 
 status_t FramebufferSurface::beginFrame(bool /*mustRecompose*/) {
@@ -173,6 +182,26 @@
     }
 }
 
+ui::Size FramebufferSurface::limitFramebufferSize(uint32_t width, uint32_t height) {
+    ui::Size framebufferSize(width, height);
+    bool wasLimited = true;
+    if (width > mMaxWidth && mMaxWidth != 0) {
+        float aspectRatio = float(width) / float(height);
+        framebufferSize.height = mMaxWidth / aspectRatio;
+        framebufferSize.width = mMaxWidth;
+        wasLimited = true;
+    }
+    if (height > mMaxHeight && mMaxHeight != 0) {
+        float aspectRatio = float(width) / float(height);
+        framebufferSize.height = mMaxHeight;
+        framebufferSize.width = mMaxHeight * aspectRatio;
+        wasLimited = true;
+    }
+    ALOGI_IF(wasLimited, "framebuffer size has been limited to [%dx%d] from [%dx%d]",
+             framebufferSize.width, framebufferSize.height, width, height);
+    return framebufferSize;
+}
+
 void FramebufferSurface::dumpAsString(String8& result) const {
     Mutex::Autolock lock(mMutex);
     result.appendFormat("  FramebufferSurface: dataspace: %s(%d)\n",
@@ -193,3 +222,6 @@
 // ----------------------------------------------------------------------------
 }; // namespace android
 // ----------------------------------------------------------------------------
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 7f451a5..a1859f3 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -23,6 +23,7 @@
 #include <compositionengine/DisplaySurface.h>
 #include <compositionengine/impl/HwcBufferCache.h>
 #include <gui/ConsumerBase.h>
+#include <ui/Size.h>
 
 #include "DisplayIdentification.h"
 
@@ -39,7 +40,8 @@
 class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface {
 public:
     FramebufferSurface(HWComposer& hwc, DisplayId displayId,
-                       const sp<IGraphicBufferConsumer>& consumer);
+                       const sp<IGraphicBufferConsumer>& consumer, uint32_t maxWidth,
+                       uint32_t maxHeight);
 
     virtual status_t beginFrame(bool mustRecompose);
     virtual status_t prepareFrame(CompositionType compositionType);
@@ -47,7 +49,7 @@
     virtual void onFrameCommitted();
     virtual void dumpAsString(String8& result) const;
 
-    virtual void resizeBuffers(const uint32_t width, const uint32_t height);
+    virtual void resizeBuffers(uint32_t width, uint32_t height);
 
     virtual const sp<Fence>& getClientTargetAcquireFence() const override;
 
@@ -58,6 +60,9 @@
 
     virtual void dumpLocked(String8& result, const char* prefix) const;
 
+    // Limits the width and height by the maximum width specified in the constructor.
+    ui::Size limitFramebufferSize(uint32_t width, uint32_t height);
+
     // nextBuffer waits for and then latches the next buffer from the
     // BufferQueue and releases the previously latched buffer to the
     // BufferQueue.  The new buffer is returned in the 'buffer' argument.
@@ -66,6 +71,14 @@
 
     const DisplayId mDisplayId;
 
+    // Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
+    // the device.
+    const uint32_t mMaxWidth;
+
+    // Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
+    // the device.
+    const uint32_t mMaxHeight;
+
     // mCurrentBufferIndex is the slot index of the current buffer or
     // INVALID_BUFFER_SLOT to indicate that either there is no current buffer
     // or the buffer is not associated with a slot.
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index f4fc747..08559bd 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 
 #undef LOG_TAG
@@ -21,7 +25,6 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "HWC2.h"
-#include "ComposerHal.h"
 
 #include <ui/Fence.h>
 #include <ui/FloatRect.h>
@@ -34,6 +37,11 @@
 #include <iterator>
 #include <set>
 
+#include "../Promise.h"
+#include "ComposerHal.h"
+
+namespace android {
+
 using android::Fence;
 using android::FloatRect;
 using android::GraphicBuffer;
@@ -42,16 +50,12 @@
 using android::Rect;
 using android::Region;
 using android::sp;
-using android::hardware::Return;
-using android::hardware::Void;
 
 namespace HWC2 {
 
+using namespace android::hardware::graphics::composer::hal;
+
 namespace Hwc2 = android::Hwc2;
-using android::ui::ColorMode;
-using android::ui::Dataspace;
-using android::ui::PixelFormat;
-using android::ui::RenderIntent;
 
 namespace {
 
@@ -60,172 +64,12 @@
     return keys.find(key) != keys.end();
 }
 
-class ComposerCallbackBridge : public Hwc2::IComposerCallback {
-public:
-    ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId)
-            : mCallback(callback), mSequenceId(sequenceId) {}
-
-    Return<void> onHotplug(Hwc2::Display display,
-                           IComposerCallback::Connection conn) override
-    {
-        HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
-        mCallback->onHotplugReceived(mSequenceId, display, connection);
-        return Void();
-    }
-
-    Return<void> onRefresh(Hwc2::Display display) override
-    {
-        mCallback->onRefreshReceived(mSequenceId, display);
-        return Void();
-    }
-
-    Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
-    {
-        mCallback->onVsyncReceived(mSequenceId, display, timestamp);
-        return Void();
-    }
-
-private:
-    ComposerCallback* mCallback;
-    int32_t mSequenceId;
-};
-
 } // namespace anonymous
 
-
-// Device methods
-
-Device::Device(std::unique_ptr<android::Hwc2::Composer> composer) : mComposer(std::move(composer)) {
-    loadCapabilities();
-}
-
-void Device::registerCallback(ComposerCallback* callback, int32_t sequenceId) {
-    if (mRegisteredCallback) {
-        ALOGW("Callback already registered. Ignored extra registration "
-                "attempt.");
-        return;
-    }
-    mRegisteredCallback = true;
-    sp<ComposerCallbackBridge> callbackBridge(
-            new ComposerCallbackBridge(callback, sequenceId));
-    mComposer->registerCallback(callbackBridge);
-}
-
-// Required by HWC2 device
-
-std::string Device::dump() const
-{
-    return mComposer->dumpDebugInfo();
-}
-
-uint32_t Device::getMaxVirtualDisplayCount() const
-{
-    return mComposer->getMaxVirtualDisplayCount();
-}
-
-Error Device::getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
-                                           std::vector<uint8_t>* outData) const {
-    auto intError = mComposer->getDisplayIdentificationData(hwcDisplayId, outPort, outData);
-    return static_cast<Error>(intError);
-}
-
-Error Device::createVirtualDisplay(uint32_t width, uint32_t height,
-        PixelFormat* format, Display** outDisplay)
-{
-    ALOGI("Creating virtual display");
-
-    hwc2_display_t displayId = 0;
-    auto intError = mComposer->createVirtualDisplay(width, height,
-            format, &displayId);
-    auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
-        return error;
-    }
-
-    auto display = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities, displayId,
-                                                   DisplayType::Virtual);
-    display->setConnected(true);
-    *outDisplay = display.get();
-    mDisplays.emplace(displayId, std::move(display));
-    ALOGI("Created virtual display");
-    return Error::None;
-}
-
-void Device::destroyDisplay(hwc2_display_t displayId)
-{
-    ALOGI("Destroying display %" PRIu64, displayId);
-    mDisplays.erase(displayId);
-}
-
-void Device::onHotplug(hwc2_display_t displayId, Connection connection) {
-    if (connection == Connection::Connected) {
-        // If we get a hotplug connected event for a display we already have,
-        // destroy the display and recreate it. This will force us to requery
-        // the display params and recreate all layers on that display.
-        auto oldDisplay = getDisplayById(displayId);
-        if (oldDisplay != nullptr && oldDisplay->isConnected()) {
-            ALOGI("Hotplug connecting an already connected display."
-                    " Clearing old display state.");
-        }
-        mDisplays.erase(displayId);
-
-        DisplayType displayType;
-        auto intError = mComposer->getDisplayType(displayId,
-                reinterpret_cast<Hwc2::IComposerClient::DisplayType *>(
-                        &displayType));
-        auto error = static_cast<Error>(intError);
-        if (error != Error::None) {
-            ALOGE("getDisplayType(%" PRIu64 ") failed: %s (%d). "
-                    "Aborting hotplug attempt.",
-                    displayId, to_string(error).c_str(), intError);
-            return;
-        }
-
-        auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities,
-                                                          displayId, displayType);
-        newDisplay->setConnected(true);
-        mDisplays.emplace(displayId, std::move(newDisplay));
-    } else if (connection == Connection::Disconnected) {
-        // The display will later be destroyed by a call to
-        // destroyDisplay(). For now we just mark it disconnected.
-        auto display = getDisplayById(displayId);
-        if (display) {
-            display->setConnected(false);
-        } else {
-            ALOGW("Attempted to disconnect unknown display %" PRIu64,
-                  displayId);
-        }
-    }
-}
-
-// Other Device methods
-
-Display* Device::getDisplayById(hwc2_display_t id) {
-    auto iter = mDisplays.find(id);
-    return iter == mDisplays.end() ? nullptr : iter->second.get();
-}
-
-// Device initialization methods
-
-void Device::loadCapabilities()
-{
-    static_assert(sizeof(Capability) == sizeof(int32_t),
-            "Capability size has changed");
-    auto capabilities = mComposer->getCapabilities();
-    for (auto capability : capabilities) {
-        mCapabilities.emplace(static_cast<Capability>(capability));
-    }
-}
-
-Error Device::flushCommands()
-{
-    return static_cast<Error>(mComposer->executeCommands());
-}
-
 // Display methods
 Display::~Display() = default;
 
-Display::Config::Config(Display& display, hwc2_config_t id)
+Display::Config::Config(Display& display, HWConfigId id)
       : mDisplay(display),
         mId(id),
         mWidth(-1),
@@ -234,7 +78,7 @@
         mDpiX(-1),
         mDpiY(-1) {}
 
-Display::Config::Builder::Builder(Display& display, hwc2_config_t id)
+Display::Config::Builder::Builder(Display& display, HWConfigId id)
       : mConfig(new Config(display, id)) {}
 
 float Display::Config::Builder::getDefaultDensity() {
@@ -253,34 +97,38 @@
 }
 
 namespace impl {
+
 Display::Display(android::Hwc2::Composer& composer,
-                 const std::unordered_set<Capability>& capabilities, hwc2_display_t id,
+                 const std::unordered_set<Capability>& capabilities, HWDisplayId id,
                  DisplayType type)
-      : mComposer(composer),
-        mCapabilities(capabilities),
-        mId(id),
-        mIsConnected(false),
-        mType(type) {
+      : mComposer(composer), mCapabilities(capabilities), mId(id), mType(type) {
     ALOGV("Created display %" PRIu64, id);
 }
 
 Display::~Display() {
     mLayers.clear();
 
-    if (mType == DisplayType::Virtual) {
-        ALOGV("Destroying virtual display");
-        auto intError = mComposer.destroyVirtualDisplay(mId);
-        auto error = static_cast<Error>(intError);
-        ALOGE_IF(error != Error::None, "destroyVirtualDisplay(%" PRIu64
-                ") failed: %s (%d)", mId, to_string(error).c_str(), intError);
-    } else if (mType == DisplayType::Physical) {
-        auto error = setVsyncEnabled(HWC2::Vsync::Disable);
-        if (error != Error::None) {
-            ALOGE("~Display: Failed to disable vsync for display %" PRIu64
-                    ": %s (%d)", mId, to_string(error).c_str(),
-                    static_cast<int32_t>(error));
-        }
+    Error error = Error::NONE;
+    const char* msg;
+    switch (mType) {
+        case DisplayType::PHYSICAL:
+            error = setVsyncEnabled(HWC2::Vsync::DISABLE);
+            msg = "disable VSYNC for";
+            break;
+
+        case DisplayType::VIRTUAL:
+            error = static_cast<Error>(mComposer.destroyVirtualDisplay(mId));
+            msg = "destroy virtual";
+            break;
+
+        case DisplayType::INVALID: // Used in unit tests.
+            break;
     }
+
+    ALOGE_IF(error != Error::NONE, "%s: Failed to %s display %" PRIu64 ": %d", __FUNCTION__, msg,
+             mId, static_cast<int32_t>(error));
+
+    ALOGV("Destroyed display %" PRIu64, mId);
 }
 
 // Required by HWC2 display
@@ -292,38 +140,38 @@
 
 Error Display::createLayer(HWC2::Layer** outLayer) {
     if (!outLayer) {
-        return Error::BadParameter;
+        return Error::BAD_PARAMETER;
     }
-    hwc2_layer_t layerId = 0;
+    HWLayerId layerId = 0;
     auto intError = mComposer.createLayer(mId, &layerId);
     auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
+    if (error != Error::NONE) {
         return error;
     }
 
     auto layer = std::make_unique<impl::Layer>(mComposer, mCapabilities, mId, layerId);
     *outLayer = layer.get();
     mLayers.emplace(layerId, std::move(layer));
-    return Error::None;
+    return Error::NONE;
 }
 
 Error Display::destroyLayer(HWC2::Layer* layer) {
     if (!layer) {
-        return Error::BadParameter;
+        return Error::BAD_PARAMETER;
     }
     mLayers.erase(layer->getId());
-    return Error::None;
+    return Error::NONE;
 }
 
 Error Display::getActiveConfig(
         std::shared_ptr<const Display::Config>* outConfig) const
 {
     ALOGV("[%" PRIu64 "] getActiveConfig", mId);
-    hwc2_config_t configId = 0;
+    HWConfigId configId = 0;
     auto intError = mComposer.getActiveConfig(mId, &configId);
     auto error = static_cast<Error>(intError);
 
-    if (error != Error::None) {
+    if (error != Error::NONE) {
         ALOGE("Unable to get active config for mId:[%" PRIu64 "]", mId);
         *outConfig = nullptr;
         return error;
@@ -339,16 +187,50 @@
         *outConfig = nullptr;
     }
 
-    return Error::None;
+    return Error::NONE;
+}
+
+bool Display::isVsyncPeriodSwitchSupported() const {
+    ALOGV("[%" PRIu64 "] isVsyncPeriodSwitchSupported()", mId);
+
+    return mComposer.isVsyncPeriodSwitchSupported();
+}
+
+Error Display::getDisplayVsyncPeriod(nsecs_t* outVsyncPeriod) const {
+    ALOGV("[%" PRIu64 "] getDisplayVsyncPeriod", mId);
+
+    Error error;
+
+    if (isVsyncPeriodSwitchSupported()) {
+        Hwc2::VsyncPeriodNanos vsyncPeriodNanos = 0;
+        auto intError = mComposer.getDisplayVsyncPeriod(mId, &vsyncPeriodNanos);
+        error = static_cast<Error>(intError);
+        *outVsyncPeriod = static_cast<nsecs_t>(vsyncPeriodNanos);
+    } else {
+        // Get the default vsync period
+        std::shared_ptr<const Display::Config> config;
+        error = getActiveConfig(&config);
+        if (error != Error::NONE) {
+            return error;
+        }
+        if (!config) {
+            // HWC has updated the display modes and hasn't notified us yet.
+            return Error::BAD_CONFIG;
+        }
+
+        *outVsyncPeriod = config->getVsyncPeriod();
+    }
+
+    return error;
 }
 
 Error Display::getActiveConfigIndex(int* outIndex) const {
     ALOGV("[%" PRIu64 "] getActiveConfigIndex", mId);
-    hwc2_config_t configId = 0;
+    HWConfigId configId = 0;
     auto intError = mComposer.getActiveConfig(mId, &configId);
     auto error = static_cast<Error>(intError);
 
-    if (error != Error::None) {
+    if (error != Error::NONE) {
         ALOGE("Unable to get active config for mId:[%" PRIu64 "]", mId);
         *outIndex = -1;
         return error;
@@ -357,6 +239,7 @@
     auto pos = mConfigs.find(configId);
     if (pos != mConfigs.end()) {
         *outIndex = std::distance(mConfigs.begin(), pos);
+        ALOGV("[%" PRIu64 "] index = %d", mId, *outIndex);
     } else {
         ALOGE("[%" PRIu64 "] getActiveConfig returned unknown config %u", mId, configId);
         // Return no error, but the caller needs to check for a negative index
@@ -364,7 +247,7 @@
         *outIndex = -1;
     }
 
-    return Error::None;
+    return Error::NONE;
 }
 
 Error Display::getChangedCompositionTypes(std::unordered_map<HWC2::Layer*, Composition>* outTypes) {
@@ -375,7 +258,7 @@
     uint32_t numElements = layerIds.size();
     auto error = static_cast<Error>(intError);
     error = static_cast<Error>(intError);
-    if (error != Error::None) {
+    if (error != Error::NONE) {
         return error;
     }
 
@@ -384,7 +267,7 @@
     for (uint32_t element = 0; element < numElements; ++element) {
         auto layer = getLayerById(layerIds[element]);
         if (layer) {
-            auto type = static_cast<Composition>(types[element]);
+            auto type = types[element];
             ALOGV("getChangedCompositionTypes: adding %" PRIu64 " %s",
                     layer->getId(), to_string(type).c_str());
             outTypes->emplace(layer, type);
@@ -394,7 +277,7 @@
         }
     }
 
-    return Error::None;
+    return Error::NONE;
 }
 
 Error Display::getColorModes(std::vector<ColorMode>* outModes) const
@@ -477,7 +360,7 @@
             mId, &intDisplayRequests, &layerIds, &layerRequests);
     uint32_t numElements = layerIds.size();
     auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
+    if (error != Error::NONE) {
         return error;
     }
 
@@ -496,18 +379,28 @@
         }
     }
 
-    return Error::None;
+    return Error::NONE;
 }
 
-Error Display::getType(DisplayType* outType) const
-{
-    *outType = mType;
-    return Error::None;
+Error Display::getConnectionType(android::DisplayConnectionType* outType) const {
+    if (mType != DisplayType::PHYSICAL) return Error::BAD_DISPLAY;
+
+    using ConnectionType = Hwc2::IComposerClient::DisplayConnectionType;
+    ConnectionType connectionType;
+    const auto error = static_cast<Error>(mComposer.getDisplayConnectionType(mId, &connectionType));
+    if (error != Error::NONE) {
+        return error;
+    }
+
+    *outType = connectionType == ConnectionType::INTERNAL
+            ? android::DisplayConnectionType::Internal
+            : android::DisplayConnectionType::External;
+    return Error::NONE;
 }
 
 Error Display::supportsDoze(bool* outSupport) const {
-    *outSupport = mDisplayCapabilities.count(DisplayCapability::Doze) > 0;
-    return Error::None;
+    *outSupport = mDisplayCapabilities.count(DisplayCapability::DOZE) > 0;
+    return Error::NONE;
 }
 
 Error Display::getHdrCapabilities(HdrCapabilities* outCapabilities) const
@@ -520,16 +413,16 @@
             &maxLuminance, &maxAverageLuminance, &minLuminance);
     auto error = static_cast<HWC2::Error>(intError);
 
-    if (error != Error::None) {
+    if (error != Error::NONE) {
         return error;
     }
 
     *outCapabilities = HdrCapabilities(std::move(types),
             maxLuminance, maxAverageLuminance, minLuminance);
-    return Error::None;
+    return Error::NONE;
 }
 
-Error Display::getDisplayedContentSamplingAttributes(PixelFormat* outFormat,
+Error Display::getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
                                                      Dataspace* outDataspace,
                                                      uint8_t* outComponentMask) const {
     auto intError = mComposer.getDisplayedContentSamplingAttributes(mId, outFormat, outDataspace,
@@ -556,7 +449,7 @@
     auto intError = mComposer.getReleaseFences(mId, &layerIds, &fenceFds);
     auto error = static_cast<Error>(intError);
     uint32_t numElements = layerIds.size();
-    if (error != Error::None) {
+    if (error != Error::NONE) {
         return error;
     }
 
@@ -573,12 +466,12 @@
             for (; element < numElements; ++element) {
                 close(fenceFds[element]);
             }
-            return Error::BadLayer;
+            return Error::BAD_LAYER;
         }
     }
 
     *outFences = std::move(releaseFences);
-    return Error::None;
+    return Error::NONE;
 }
 
 Error Display::present(sp<Fence>* outPresentFence)
@@ -586,12 +479,52 @@
     int32_t presentFenceFd = -1;
     auto intError = mComposer.presentDisplay(mId, &presentFenceFd);
     auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
+    if (error != Error::NONE) {
         return error;
     }
 
     *outPresentFence = new Fence(presentFenceFd);
-    return Error::None;
+    return Error::NONE;
+}
+
+Error Display::setActiveConfigWithConstraints(
+        const std::shared_ptr<const HWC2::Display::Config>& config,
+        const VsyncPeriodChangeConstraints& constraints, VsyncPeriodChangeTimeline* outTimeline) {
+    ALOGV("[%" PRIu64 "] setActiveConfigWithConstraints", mId);
+    if (config->getDisplayId() != mId) {
+        ALOGE("setActiveConfigWithConstraints received config %u for the wrong display %" PRIu64
+              " (expected %" PRIu64 ")",
+              config->getId(), config->getDisplayId(), mId);
+        return Error::BAD_CONFIG;
+    }
+
+    if (isVsyncPeriodSwitchSupported()) {
+        Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints;
+        hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos;
+        hwc2Constraints.seamlessRequired = constraints.seamlessRequired;
+
+        Hwc2::VsyncPeriodChangeTimeline vsyncPeriodChangeTimeline = {};
+        auto intError =
+                mComposer.setActiveConfigWithConstraints(mId, config->getId(), hwc2Constraints,
+                                                         &vsyncPeriodChangeTimeline);
+        outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeTimeline.newVsyncAppliedTimeNanos;
+        outTimeline->refreshRequired = vsyncPeriodChangeTimeline.refreshRequired;
+        outTimeline->refreshTimeNanos = vsyncPeriodChangeTimeline.refreshTimeNanos;
+        return static_cast<Error>(intError);
+    }
+
+    // Use legacy setActiveConfig instead
+    ALOGV("fallback to legacy setActiveConfig");
+    const auto now = systemTime();
+    if (constraints.desiredTimeNanos > now || constraints.seamlessRequired) {
+        ALOGE("setActiveConfigWithConstraints received constraints that can't be satisfied");
+    }
+
+    auto intError_2_4 = mComposer.setActiveConfig(mId, config->getId());
+    outTimeline->newVsyncAppliedTimeNanos = std::max(now, constraints.desiredTimeNanos);
+    outTimeline->refreshRequired = true;
+    outTimeline->refreshTimeNanos = now;
+    return static_cast<Error>(intError_2_4);
 }
 
 Error Display::setActiveConfig(const std::shared_ptr<const Config>& config)
@@ -600,7 +533,7 @@
         ALOGE("setActiveConfig received config %u for the wrong display %"
                 PRIu64 " (expected %" PRIu64 ")", config->getId(),
                 config->getDisplayId(), mId);
-        return Error::BadConfig;
+        return Error::BAD_CONFIG;
     }
     auto intError = mComposer.setActiveConfig(mId, config->getId());
     return static_cast<Error>(intError);
@@ -622,11 +555,8 @@
     return static_cast<Error>(intError);
 }
 
-Error Display::setColorTransform(const android::mat4& matrix,
-        android_color_transform_t hint)
-{
-    auto intError = mComposer.setColorTransform(mId,
-            matrix.asArray(), static_cast<Hwc2::ColorTransform>(hint));
+Error Display::setColorTransform(const android::mat4& matrix, ColorTransform hint) {
+    auto intError = mComposer.setColorTransform(mId, matrix.asArray(), hint);
     return static_cast<Error>(intError);
 }
 
@@ -645,23 +575,23 @@
     auto intMode = static_cast<Hwc2::IComposerClient::PowerMode>(mode);
     auto intError = mComposer.setPowerMode(mId, intMode);
 
-    if (mode == PowerMode::On) {
+    if (mode == PowerMode::ON) {
         std::call_once(mDisplayCapabilityQueryFlag, [this]() {
             std::vector<Hwc2::DisplayCapability> tmpCapabilities;
             auto error =
                     static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities));
-            if (error == Error::None) {
+            if (error == Error::NONE) {
                 for (auto capability : tmpCapabilities) {
                     mDisplayCapabilities.emplace(static_cast<DisplayCapability>(capability));
                 }
-            } else if (error == Error::Unsupported) {
-                if (mCapabilities.count(Capability::SkipClientColorTransform)) {
-                    mDisplayCapabilities.emplace(DisplayCapability::SkipClientColorTransform);
+            } else if (error == Error::UNSUPPORTED) {
+                if (mCapabilities.count(Capability::SKIP_CLIENT_COLOR_TRANSFORM)) {
+                    mDisplayCapabilities.emplace(DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM);
                 }
                 bool dozeSupport = false;
                 error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport));
-                if (error == Error::None && dozeSupport) {
-                    mDisplayCapabilities.emplace(DisplayCapability::Doze);
+                if (error == Error::NONE && dozeSupport) {
+                    mDisplayCapabilities.emplace(DisplayCapability::DOZE);
                 }
             }
         });
@@ -683,7 +613,7 @@
     uint32_t numRequests = 0;
     auto intError = mComposer.validateDisplay(mId, &numTypes, &numRequests);
     auto error = static_cast<Error>(intError);
-    if (error != Error::None && error != Error::HasChanges) {
+    if (error != Error::NONE && !hasChangesError(error)) {
         return error;
     }
 
@@ -701,7 +631,7 @@
     auto intError = mComposer.presentOrValidateDisplay(
             mId, &numTypes, &numRequests, &presentFenceFd, state);
     auto error = static_cast<Error>(intError);
-    if (error != Error::None && error != Error::HasChanges) {
+    if (error != Error::NONE && !hasChangesError(error)) {
         return error;
     }
 
@@ -716,31 +646,54 @@
     return error;
 }
 
-Error Display::setDisplayBrightness(float brightness) const {
-    auto intError = mComposer.setDisplayBrightness(mId, brightness);
+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) {
+    auto intError = mComposer.setAutoLowLatencyMode(mId, on);
     return static_cast<Error>(intError);
 }
 
+Error Display::getSupportedContentTypes(std::vector<ContentType>* outSupportedContentTypes) const {
+    std::vector<Hwc2::IComposerClient::ContentType> tmpSupportedContentTypes;
+    auto intError = mComposer.getSupportedContentTypes(mId, &tmpSupportedContentTypes);
+    for (Hwc2::IComposerClient::ContentType contentType : tmpSupportedContentTypes) {
+        outSupportedContentTypes->push_back(static_cast<ContentType>(contentType));
+    }
+    return static_cast<Error>(intError);
+}
+
+Error Display::setContentType(ContentType contentType) {
+    auto intError = mComposer.setContentType(mId, contentType);
+    return static_cast<Error>(intError);
+}
+
+Error Display::getClientTargetProperty(ClientTargetProperty* outClientTargetProperty) {
+    const auto error = mComposer.getClientTargetProperty(mId, outClientTargetProperty);
+    return static_cast<Error>(error);
+}
+
 // For use by Device
 
 void Display::setConnected(bool connected) {
     if (!mIsConnected && connected) {
         mComposer.setClientTargetSlotCount(mId);
-        if (mType == DisplayType::Physical) {
+        if (mType == DisplayType::PHYSICAL) {
             loadConfigs();
         }
     }
     mIsConnected = connected;
 }
 
-int32_t Display::getAttribute(hwc2_config_t configId, Attribute attribute)
-{
+int32_t Display::getAttribute(HWConfigId configId, Attribute attribute) {
     int32_t value = 0;
-    auto intError = mComposer.getDisplayAttribute(mId, configId,
-            static_cast<Hwc2::IComposerClient::Attribute>(attribute),
-            &value);
+    auto intError = mComposer.getDisplayAttribute(mId, configId, attribute, &value);
     auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
+    if (error != Error::NONE) {
         ALOGE("getDisplayAttribute(%" PRIu64 ", %u, %s) failed: %s (%d)", mId,
                 configId, to_string(attribute).c_str(),
                 to_string(error).c_str(), intError);
@@ -749,17 +702,17 @@
     return value;
 }
 
-void Display::loadConfig(hwc2_config_t configId)
-{
+void Display::loadConfig(HWConfigId configId) {
     ALOGV("[%" PRIu64 "] loadConfig(%u)", mId, configId);
 
     auto config = Config::Builder(*this, configId)
-            .setWidth(getAttribute(configId, Attribute::Width))
-            .setHeight(getAttribute(configId, Attribute::Height))
-            .setVsyncPeriod(getAttribute(configId, Attribute::VsyncPeriod))
-            .setDpiX(getAttribute(configId, Attribute::DpiX))
-            .setDpiY(getAttribute(configId, Attribute::DpiY))
-            .build();
+                          .setWidth(getAttribute(configId, hal::Attribute::WIDTH))
+                          .setHeight(getAttribute(configId, hal::Attribute::HEIGHT))
+                          .setVsyncPeriod(getAttribute(configId, hal::Attribute::VSYNC_PERIOD))
+                          .setDpiX(getAttribute(configId, hal::Attribute::DPI_X))
+                          .setDpiY(getAttribute(configId, hal::Attribute::DPI_Y))
+                          .setConfigGroup(getAttribute(configId, hal::Attribute::CONFIG_GROUP))
+                          .build();
     mConfigs.emplace(configId, std::move(config));
 }
 
@@ -767,10 +720,10 @@
 {
     ALOGV("[%" PRIu64 "] loadConfigs", mId);
 
-    std::vector<Hwc2::Config> configIds;
+    std::vector<HWConfigId> configIds;
     auto intError = mComposer.getDisplayConfigs(mId, &configIds);
     auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
+    if (error != Error::NONE) {
         ALOGE("[%" PRIu64 "] getDisplayConfigs [2] failed: %s (%d)", mId,
                 to_string(error).c_str(), intError);
         return;
@@ -783,7 +736,7 @@
 
 // Other Display methods
 
-HWC2::Layer* Display::getLayerById(hwc2_layer_t id) const {
+HWC2::Layer* Display::getLayerById(HWLayerId id) const {
     if (mLayers.count(id) == 0) {
         return nullptr;
     }
@@ -799,13 +752,12 @@
 namespace impl {
 
 Layer::Layer(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities,
-             hwc2_display_t displayId, hwc2_layer_t layerId)
-  : mComposer(composer),
-    mCapabilities(capabilities),
-    mDisplayId(displayId),
-    mId(layerId),
-    mColorMatrix(android::mat4())
-{
+             HWDisplayId displayId, HWLayerId layerId)
+      : mComposer(composer),
+        mCapabilities(capabilities),
+        mDisplayId(displayId),
+        mId(layerId),
+        mColorMatrix(android::mat4()) {
     ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, displayId);
 }
 
@@ -813,9 +765,10 @@
 {
     auto intError = mComposer.destroyLayer(mDisplayId, mId);
     auto error = static_cast<Error>(intError);
-    ALOGE_IF(error != Error::None, "destroyLayer(%" PRIu64 ", %" PRIu64 ")"
-            " failed: %s (%d)", mDisplayId, mId, to_string(error).c_str(),
-            intError);
+    ALOGE_IF(error != Error::NONE,
+             "destroyLayer(%" PRIu64 ", %" PRIu64 ")"
+             " failed: %s (%d)",
+             mDisplayId, mId, to_string(error).c_str(), intError);
 }
 
 Error Layer::setCursorPosition(int32_t x, int32_t y)
@@ -828,7 +781,7 @@
         const sp<Fence>& acquireFence)
 {
     if (buffer == nullptr && mBufferSlot == slot) {
-        return Error::None;
+        return Error::NONE;
     }
     mBufferSlot = slot;
 
@@ -842,7 +795,7 @@
 {
     if (damage.isRect() && mDamageRegion.isRect() &&
         (damage.getBounds() == mDamageRegion.getBounds())) {
-        return Error::None;
+        return Error::NONE;
     }
     mDamageRegion = damage;
 
@@ -870,30 +823,25 @@
 
 Error Layer::setBlendMode(BlendMode mode)
 {
-    auto intMode = static_cast<Hwc2::IComposerClient::BlendMode>(mode);
-    auto intError = mComposer.setLayerBlendMode(mDisplayId, mId, intMode);
+    auto intError = mComposer.setLayerBlendMode(mDisplayId, mId, mode);
     return static_cast<Error>(intError);
 }
 
-Error Layer::setColor(hwc_color_t color)
-{
-    Hwc2::IComposerClient::Color hwcColor{color.r, color.g, color.b, color.a};
-    auto intError = mComposer.setLayerColor(mDisplayId, mId, hwcColor);
+Error Layer::setColor(Color color) {
+    auto intError = mComposer.setLayerColor(mDisplayId, mId, color);
     return static_cast<Error>(intError);
 }
 
 Error Layer::setCompositionType(Composition type)
 {
-    auto intType = static_cast<Hwc2::IComposerClient::Composition>(type);
-    auto intError = mComposer.setLayerCompositionType(
-            mDisplayId, mId, intType);
+    auto intError = mComposer.setLayerCompositionType(mDisplayId, mId, type);
     return static_cast<Error>(intError);
 }
 
 Error Layer::setDataspace(Dataspace dataspace)
 {
     if (dataspace == mDataSpace) {
-        return Error::None;
+        return Error::NONE;
     }
     mDataSpace = dataspace;
     auto intError = mComposer.setLayerDataspace(mDisplayId, mId, mDataSpace);
@@ -904,7 +852,7 @@
         const android::HdrMetadata& metadata)
 {
     if (metadata == mHdrMetadata) {
-        return Error::None;
+        return Error::NONE;
     }
 
     mHdrMetadata = metadata;
@@ -946,12 +894,16 @@
             mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas));
 
     if (validTypes & HdrMetadata::HDR10PLUS) {
+        if (CC_UNLIKELY(mHdrMetadata.hdr10plus.size() == 0)) {
+            return Error::BAD_PARAMETER;
+        }
+
         std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs;
         perFrameMetadataBlobs.push_back(
                 {Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI, mHdrMetadata.hdr10plus});
         Error setMetadataBlobsError = static_cast<Error>(
                 mComposer.setLayerPerFrameMetadataBlobs(mDisplayId, mId, perFrameMetadataBlobs));
-        if (error == Error::None) {
+        if (error == Error::NONE) {
             return setMetadataBlobsError;
         }
     }
@@ -974,10 +926,10 @@
 
 Error Layer::setSidebandStream(const native_handle_t* stream)
 {
-    if (mCapabilities.count(Capability::SidebandStream) == 0) {
+    if (mCapabilities.count(Capability::SIDEBAND_STREAM) == 0) {
         ALOGE("Attempted to call setSidebandStream without checking that the "
                 "device supports sideband streams");
-        return Error::Unsupported;
+        return Error::UNSUPPORTED;
     }
     auto intError = mComposer.setLayerSidebandStream(mDisplayId, mId, stream);
     return static_cast<Error>(intError);
@@ -1002,7 +954,7 @@
 {
     if (region.isRect() && mVisibleRegion.isRect() &&
         (region.getBounds() == mVisibleRegion.getBounds())) {
-        return Error::None;
+        return Error::NONE;
     }
     mVisibleRegion = region;
 
@@ -1034,16 +986,27 @@
 // Composer HAL 2.3
 Error Layer::setColorTransform(const android::mat4& matrix) {
     if (matrix == mColorMatrix) {
-        return Error::None;
+        return Error::NONE;
     }
     auto intError = mComposer.setLayerColorTransform(mDisplayId, mId, matrix.asArray());
     Error error = static_cast<Error>(intError);
-    if (error != Error::None) {
+    if (error != Error::NONE) {
         return error;
     }
     mColorMatrix = matrix;
     return error;
 }
 
+// Composer HAL 2.4
+Error Layer::setLayerGenericMetadata(const std::string& name, bool mandatory,
+                                     const std::vector<uint8_t>& value) {
+    auto intError = mComposer.setLayerGenericMetadata(mDisplayId, mId, name, mandatory, value);
+    return static_cast<Error>(intError);
+}
+
 } // namespace impl
 } // namespace HWC2
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index b7cdf7f..6819ff4 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -17,15 +17,9 @@
 #ifndef ANDROID_SF_HWC2_H
 #define ANDROID_SF_HWC2_H
 
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
-#include <ui/GraphicTypes.h>
+#include <ui/DisplayInfo.h>
 #include <ui/HdrCapabilities.h>
 #include <ui/Region.h>
 #include <utils/Log.h>
@@ -33,11 +27,14 @@
 #include <utils/Timers.h>
 
 #include <functional>
+#include <future>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
 
+#include "Hal.h"
+
 namespace android {
     struct DisplayedFrameStats;
     class Fence;
@@ -48,13 +45,13 @@
     }
 
     class TestableSurfaceFlinger;
-}
 
 namespace HWC2 {
 
-class Display;
 class Layer;
 
+namespace hal = android::hardware::graphics::composer::hal;
+
 // Implement this interface to receive hardware composer events.
 //
 // These callback functions will generally be called on a hwbinder thread, but
@@ -66,66 +63,20 @@
 // from different hardware composer instances.
 class ComposerCallback {
  public:
-    virtual void onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
-                                   Connection connection) = 0;
-    virtual void onRefreshReceived(int32_t sequenceId,
-                                   hwc2_display_t display) = 0;
-    virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
-                                 int64_t timestamp) = 0;
-    virtual ~ComposerCallback() = default;
+     virtual void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId display,
+                                    hal::Connection connection) = 0;
+     virtual void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId display) = 0;
+     virtual void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId display, int64_t timestamp,
+                                  std::optional<hal::VsyncPeriodNanos> vsyncPeriod) = 0;
+     virtual void onVsyncPeriodTimingChangedReceived(
+             int32_t sequenceId, hal::HWDisplayId display,
+             const hal::VsyncPeriodChangeTimeline& updatedTimeline) = 0;
+     virtual void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId display) = 0;
+
+     virtual ~ComposerCallback() = default;
 };
 
-// C++ Wrapper around hwc2_device_t. Load all functions pointers
-// and handle callback registration.
-class Device
-{
-public:
-    explicit Device(std::unique_ptr<android::Hwc2::Composer> composer);
-
-    void registerCallback(ComposerCallback* callback, int32_t sequenceId);
-
-    // Required by HWC2
-
-    std::string dump() const;
-
-    const std::unordered_set<Capability>& getCapabilities() const {
-        return mCapabilities;
-    };
-
-    uint32_t getMaxVirtualDisplayCount() const;
-    Error getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
-                                       std::vector<uint8_t>* outData) const;
-
-    Error createVirtualDisplay(uint32_t width, uint32_t height,
-            android::ui::PixelFormat* format, Display** outDisplay);
-    void destroyDisplay(hwc2_display_t displayId);
-
-    void onHotplug(hwc2_display_t displayId, Connection connection);
-
-    // Other Device methods
-
-    Display* getDisplayById(hwc2_display_t id);
-
-    android::Hwc2::Composer* getComposer() { return mComposer.get(); }
-
-    // We buffer most state changes and flush them implicitly with
-    // Display::validate, Display::present, and Display::presentOrValidate.
-    // This method provides an explicit way to flush state changes to HWC.
-    Error flushCommands();
-
-private:
-    // Initialization methods
-
-    void loadCapabilities();
-
-    // Member variables
-    std::unique_ptr<android::Hwc2::Composer> mComposer;
-    std::unordered_set<Capability> mCapabilities;
-    std::unordered_map<hwc2_display_t, std::unique_ptr<Display>> mDisplays;
-    bool mRegisteredCallback = false;
-};
-
-// Convenience C++ class to access hwc2_device_t Display functions directly.
+// Convenience C++ class to access per display functions directly.
 class Display {
 public:
     virtual ~Display();
@@ -135,7 +86,7 @@
         class Builder
         {
         public:
-            Builder(Display& display, hwc2_config_t id);
+            Builder(Display& display, hal::HWConfigId id);
 
             std::shared_ptr<const Config> build() {
                 return std::const_pointer_cast<const Config>(
@@ -170,176 +121,211 @@
                 }
                 return *this;
             }
+            Builder& setConfigGroup(int32_t configGroup) {
+                mConfig->mConfigGroup = configGroup;
+                return *this;
+            }
 
         private:
             float getDefaultDensity();
             std::shared_ptr<Config> mConfig;
         };
 
-        hwc2_display_t getDisplayId() const { return mDisplay.getId(); }
-        hwc2_config_t getId() const { return mId; }
+        hal::HWDisplayId getDisplayId() const { return mDisplay.getId(); }
+        hal::HWConfigId getId() const { return mId; }
 
         int32_t getWidth() const { return mWidth; }
         int32_t getHeight() const { return mHeight; }
         nsecs_t getVsyncPeriod() const { return mVsyncPeriod; }
         float getDpiX() const { return mDpiX; }
         float getDpiY() const { return mDpiY; }
+        int32_t getConfigGroup() const { return mConfigGroup; }
 
     private:
-        Config(Display& display, hwc2_config_t id);
+        Config(Display& display, hal::HWConfigId id);
 
         Display& mDisplay;
-        hwc2_config_t mId;
+        hal::HWConfigId mId;
 
         int32_t mWidth;
         int32_t mHeight;
         nsecs_t mVsyncPeriod;
         float mDpiX;
         float mDpiY;
+        int32_t mConfigGroup;
     };
 
-    virtual hwc2_display_t getId() const = 0;
+    virtual hal::HWDisplayId getId() const = 0;
     virtual bool isConnected() const = 0;
     virtual void setConnected(bool connected) = 0; // For use by Device only
-    virtual const std::unordered_set<DisplayCapability>& getCapabilities() const = 0;
+    virtual const std::unordered_set<hal::DisplayCapability>& getCapabilities() const = 0;
+    virtual bool isVsyncPeriodSwitchSupported() const = 0;
 
-    [[clang::warn_unused_result]] virtual Error acceptChanges() = 0;
-    [[clang::warn_unused_result]] virtual Error createLayer(Layer** outLayer) = 0;
-    [[clang::warn_unused_result]] virtual Error destroyLayer(Layer* layer) = 0;
-    [[clang::warn_unused_result]] virtual Error getActiveConfig(
+    [[clang::warn_unused_result]] virtual hal::Error acceptChanges() = 0;
+    [[clang::warn_unused_result]] virtual hal::Error createLayer(Layer** outLayer) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error destroyLayer(Layer* layer) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getActiveConfig(
             std::shared_ptr<const Config>* outConfig) const = 0;
-    [[clang::warn_unused_result]] virtual Error getActiveConfigIndex(int* outIndex) const = 0;
-    [[clang::warn_unused_result]] virtual Error getChangedCompositionTypes(
-            std::unordered_map<Layer*, Composition>* outTypes) = 0;
-    [[clang::warn_unused_result]] virtual Error getColorModes(
-            std::vector<android::ui::ColorMode>* outModes) const = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getActiveConfigIndex(int* outIndex) const = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getChangedCompositionTypes(
+            std::unordered_map<Layer*, hal::Composition>* outTypes) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getColorModes(
+            std::vector<hal::ColorMode>* outModes) const = 0;
     // Returns a bitmask which contains HdrMetadata::Type::*.
     [[clang::warn_unused_result]] virtual int32_t getSupportedPerFrameMetadata() const = 0;
-    [[clang::warn_unused_result]] virtual Error getRenderIntents(
-            android::ui::ColorMode colorMode,
-            std::vector<android::ui::RenderIntent>* outRenderIntents) const = 0;
-    [[clang::warn_unused_result]] virtual Error getDataspaceSaturationMatrix(
-            android::ui::Dataspace dataspace, android::mat4* outMatrix) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getRenderIntents(
+            hal::ColorMode colorMode, std::vector<hal::RenderIntent>* outRenderIntents) const = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getDataspaceSaturationMatrix(
+            hal::Dataspace dataspace, android::mat4* outMatrix) = 0;
 
-    // Doesn't call into the HWC2 device, so no Errors are possible
-    virtual std::vector<std::shared_ptr<const Config>> getConfigs() const = 0;
+    // Doesn't call into the HWC2 device, so no errors are possible
+    [[clang::warn_unused_result]] virtual std::vector<std::shared_ptr<const Config>> getConfigs()
+            const = 0;
 
-    [[clang::warn_unused_result]] virtual Error getName(std::string* outName) const = 0;
-    [[clang::warn_unused_result]] virtual Error getRequests(
-            DisplayRequest* outDisplayRequests,
-            std::unordered_map<Layer*, LayerRequest>* outLayerRequests) = 0;
-    [[clang::warn_unused_result]] virtual Error getType(DisplayType* outType) const = 0;
-    [[clang::warn_unused_result]] virtual Error supportsDoze(bool* outSupport) const = 0;
-    [[clang::warn_unused_result]] virtual Error getHdrCapabilities(
+    [[clang::warn_unused_result]] virtual hal::Error getName(std::string* outName) const = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getRequests(
+            hal::DisplayRequest* outDisplayRequests,
+            std::unordered_map<Layer*, hal::LayerRequest>* outLayerRequests) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getConnectionType(
+            android::DisplayConnectionType*) const = 0;
+    [[clang::warn_unused_result]] virtual hal::Error supportsDoze(bool* outSupport) const = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getHdrCapabilities(
             android::HdrCapabilities* outCapabilities) const = 0;
-    [[clang::warn_unused_result]] virtual Error getDisplayedContentSamplingAttributes(
-            android::ui::PixelFormat* outFormat, android::ui::Dataspace* outDataspace,
+    [[clang::warn_unused_result]] virtual hal::Error getDisplayedContentSamplingAttributes(
+            hal::PixelFormat* outFormat, hal::Dataspace* outDataspace,
             uint8_t* outComponentMask) const = 0;
-    [[clang::warn_unused_result]] virtual Error setDisplayContentSamplingEnabled(
+    [[clang::warn_unused_result]] virtual hal::Error setDisplayContentSamplingEnabled(
             bool enabled, uint8_t componentMask, uint64_t maxFrames) const = 0;
-    [[clang::warn_unused_result]] virtual Error getDisplayedContentSample(
+    [[clang::warn_unused_result]] virtual hal::Error getDisplayedContentSample(
             uint64_t maxFrames, uint64_t timestamp,
             android::DisplayedFrameStats* outStats) const = 0;
-    [[clang::warn_unused_result]] virtual Error getReleaseFences(
+    [[clang::warn_unused_result]] virtual hal::Error getReleaseFences(
             std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const = 0;
-    [[clang::warn_unused_result]] virtual Error present(
+    [[clang::warn_unused_result]] virtual hal::Error present(
             android::sp<android::Fence>* outPresentFence) = 0;
-    [[clang::warn_unused_result]] virtual Error setActiveConfig(
+    [[clang::warn_unused_result]] virtual hal::Error setActiveConfig(
             const std::shared_ptr<const Config>& config) = 0;
-    [[clang::warn_unused_result]] virtual Error setClientTarget(
+    [[clang::warn_unused_result]] virtual hal::Error setClientTarget(
             uint32_t slot, const android::sp<android::GraphicBuffer>& target,
-            const android::sp<android::Fence>& acquireFence, android::ui::Dataspace dataspace) = 0;
-    [[clang::warn_unused_result]] virtual Error setColorMode(
-            android::ui::ColorMode mode, android::ui::RenderIntent renderIntent) = 0;
-    [[clang::warn_unused_result]] virtual Error setColorTransform(
-            const android::mat4& matrix, android_color_transform_t hint) = 0;
-    [[clang::warn_unused_result]] virtual Error setOutputBuffer(
+            const android::sp<android::Fence>& acquireFence, hal::Dataspace dataspace) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setColorMode(
+            hal::ColorMode mode, hal::RenderIntent renderIntent) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setColorTransform(
+            const android::mat4& matrix, hal::ColorTransform hint) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setOutputBuffer(
             const android::sp<android::GraphicBuffer>& buffer,
             const android::sp<android::Fence>& releaseFence) = 0;
-    [[clang::warn_unused_result]] virtual Error setPowerMode(PowerMode mode) = 0;
-    [[clang::warn_unused_result]] virtual Error setVsyncEnabled(Vsync enabled) = 0;
-    [[clang::warn_unused_result]] virtual Error validate(uint32_t* outNumTypes,
-                                                         uint32_t* outNumRequests) = 0;
-    [[clang::warn_unused_result]] virtual Error presentOrValidate(
+    [[clang::warn_unused_result]] virtual hal::Error setPowerMode(hal::PowerMode mode) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setVsyncEnabled(hal::Vsync enabled) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error validate(uint32_t* outNumTypes,
+                                                              uint32_t* outNumRequests) = 0;
+    [[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 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) = 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) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error getClientTargetProperty(
+            hal::ClientTargetProperty* outClientTargetProperty) = 0;
 };
 
 namespace impl {
 
 class Display : public HWC2::Display {
 public:
-    Display(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities,
-            hwc2_display_t id, DisplayType type);
+    Display(android::Hwc2::Composer& composer,
+            const std::unordered_set<hal::Capability>& capabilities, hal::HWDisplayId id,
+            hal::DisplayType type);
     ~Display() override;
 
     // Required by HWC2
-    Error acceptChanges() override;
-    Error createLayer(Layer** outLayer) override;
-    Error destroyLayer(Layer* layer) override;
-    Error getActiveConfig(std::shared_ptr<const Config>* outConfig) const override;
-    Error getActiveConfigIndex(int* outIndex) const override;
-    Error getChangedCompositionTypes(std::unordered_map<Layer*, Composition>* outTypes) override;
-    Error getColorModes(std::vector<android::ui::ColorMode>* outModes) const override;
+    hal::Error acceptChanges() override;
+    hal::Error createLayer(Layer** outLayer) override;
+    hal::Error destroyLayer(Layer* layer) override;
+    hal::Error getActiveConfig(std::shared_ptr<const Config>* outConfig) const override;
+    hal::Error getActiveConfigIndex(int* outIndex) const override;
+    hal::Error getChangedCompositionTypes(
+            std::unordered_map<Layer*, hal::Composition>* outTypes) override;
+    hal::Error getColorModes(std::vector<hal::ColorMode>* outModes) const override;
     // Returns a bitmask which contains HdrMetadata::Type::*.
     int32_t getSupportedPerFrameMetadata() const override;
-    Error getRenderIntents(android::ui::ColorMode colorMode,
-                           std::vector<android::ui::RenderIntent>* outRenderIntents) const override;
-    Error getDataspaceSaturationMatrix(android::ui::Dataspace dataspace,
-                                       android::mat4* outMatrix) override;
+    hal::Error getRenderIntents(hal::ColorMode colorMode,
+                                std::vector<hal::RenderIntent>* outRenderIntents) const override;
+    hal::Error getDataspaceSaturationMatrix(hal::Dataspace dataspace,
+                                            android::mat4* outMatrix) override;
 
     // Doesn't call into the HWC2 device, so no errors are possible
     std::vector<std::shared_ptr<const Config>> getConfigs() const override;
 
-    Error getName(std::string* outName) const override;
-    Error getRequests(DisplayRequest* outDisplayRequests,
-                      std::unordered_map<Layer*, LayerRequest>* outLayerRequests) override;
-    Error getType(DisplayType* outType) const override;
-    Error supportsDoze(bool* outSupport) const override;
-    Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
-    Error getDisplayedContentSamplingAttributes(android::ui::PixelFormat* outFormat,
-                                                android::ui::Dataspace* outDataspace,
-                                                uint8_t* outComponentMask) const override;
-    Error setDisplayContentSamplingEnabled(bool enabled, uint8_t componentMask,
-                                           uint64_t maxFrames) const override;
-    Error getDisplayedContentSample(uint64_t maxFrames, uint64_t timestamp,
-                                    android::DisplayedFrameStats* outStats) const override;
-    Error getReleaseFences(
+    hal::Error getName(std::string* outName) const override;
+    hal::Error getRequests(
+            hal::DisplayRequest* outDisplayRequests,
+            std::unordered_map<Layer*, hal::LayerRequest>* outLayerRequests) override;
+    hal::Error getConnectionType(android::DisplayConnectionType*) const override;
+    hal::Error supportsDoze(bool* outSupport) const override;
+    hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
+    hal::Error getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat,
+                                                     hal::Dataspace* outDataspace,
+                                                     uint8_t* outComponentMask) const override;
+    hal::Error setDisplayContentSamplingEnabled(bool enabled, uint8_t componentMask,
+                                                uint64_t maxFrames) const override;
+    hal::Error getDisplayedContentSample(uint64_t maxFrames, uint64_t timestamp,
+                                         android::DisplayedFrameStats* outStats) const override;
+    hal::Error getReleaseFences(
             std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const override;
-    Error present(android::sp<android::Fence>* outPresentFence) override;
-    Error setActiveConfig(const std::shared_ptr<const HWC2::Display::Config>& config) override;
-    Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
-                          const android::sp<android::Fence>& acquireFence,
-                          android::ui::Dataspace dataspace) override;
-    Error setColorMode(android::ui::ColorMode mode,
-                       android::ui::RenderIntent renderIntent) override;
-    Error setColorTransform(const android::mat4& matrix, android_color_transform_t hint) override;
-    Error setOutputBuffer(const android::sp<android::GraphicBuffer>& buffer,
-                          const android::sp<android::Fence>& releaseFence) override;
-    Error setPowerMode(PowerMode mode) override;
-    Error setVsyncEnabled(Vsync enabled) override;
-    Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override;
-    Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
-                            android::sp<android::Fence>* outPresentFence, uint32_t* state) override;
-    Error setDisplayBrightness(float brightness) const override;
+    hal::Error present(android::sp<android::Fence>* outPresentFence) override;
+    hal::Error setActiveConfig(const std::shared_ptr<const HWC2::Display::Config>& config) override;
+    hal::Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target,
+                               const android::sp<android::Fence>& acquireFence,
+                               hal::Dataspace dataspace) override;
+    hal::Error setColorMode(hal::ColorMode mode, hal::RenderIntent renderIntent) override;
+    hal::Error setColorTransform(const android::mat4& matrix, hal::ColorTransform hint) override;
+    hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>& buffer,
+                               const android::sp<android::Fence>& releaseFence) override;
+    hal::Error setPowerMode(hal::PowerMode mode) override;
+    hal::Error setVsyncEnabled(hal::Vsync enabled) override;
+    hal::Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override;
+    hal::Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
+                                 android::sp<android::Fence>* outPresentFence,
+                                 uint32_t* state) 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) override;
+    hal::Error getSupportedContentTypes(
+            std::vector<hal::ContentType>* outSupportedContentTypes) const override;
+    hal::Error setContentType(hal::ContentType) override;
+    hal::Error getClientTargetProperty(hal::ClientTargetProperty* outClientTargetProperty) override;
 
     // Other Display methods
-    hwc2_display_t getId() const override { return mId; }
+    hal::HWDisplayId getId() const override { return mId; }
     bool isConnected() const override { return mIsConnected; }
     void setConnected(bool connected) override; // For use by Device only
-    const std::unordered_set<DisplayCapability>& getCapabilities() const override {
+    const std::unordered_set<hal::DisplayCapability>& getCapabilities() const override {
         return mDisplayCapabilities;
     };
+    virtual bool isVsyncPeriodSwitchSupported() const override;
 
 private:
-    int32_t getAttribute(hwc2_config_t configId, Attribute attribute);
-    void loadConfig(hwc2_config_t configId);
+    int32_t getAttribute(hal::HWConfigId configId, hal::Attribute attribute);
+    void loadConfig(hal::HWConfigId configId);
     void loadConfigs();
 
     // This may fail (and return a null pointer) if no layer with this ID exists
     // on this display
-    Layer* getLayerById(hwc2_layer_t id) const;
+    Layer* getLayerById(hal::HWLayerId id) const;
 
     friend android::TestableSurfaceFlinger;
 
@@ -349,108 +335,124 @@
     // this HWC2::Display, so these references are guaranteed to be valid for
     // the lifetime of this object.
     android::Hwc2::Composer& mComposer;
-    const std::unordered_set<Capability>& mCapabilities;
+    const std::unordered_set<hal::Capability>& mCapabilities;
 
-    hwc2_display_t mId;
-    bool mIsConnected;
-    DisplayType mType;
-    std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers;
-    std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
+    const hal::HWDisplayId mId;
+    hal::DisplayType mType;
+    bool mIsConnected = false;
+
+    std::unordered_map<hal::HWLayerId, std::unique_ptr<Layer>> mLayers;
+    std::unordered_map<hal::HWConfigId, std::shared_ptr<const Config>> mConfigs;
+
     std::once_flag mDisplayCapabilityQueryFlag;
-    std::unordered_set<DisplayCapability> mDisplayCapabilities;
+    std::unordered_set<hal::DisplayCapability> mDisplayCapabilities;
 };
+
 } // namespace impl
 
 class Layer {
 public:
     virtual ~Layer();
 
-    virtual hwc2_layer_t getId() const = 0;
+    virtual hal::HWLayerId getId() const = 0;
 
-    [[clang::warn_unused_result]] virtual Error setCursorPosition(int32_t x, int32_t y) = 0;
-    [[clang::warn_unused_result]] virtual Error setBuffer(
+    [[clang::warn_unused_result]] virtual hal::Error setCursorPosition(int32_t x, int32_t y) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setBuffer(
             uint32_t slot, const android::sp<android::GraphicBuffer>& buffer,
             const android::sp<android::Fence>& acquireFence) = 0;
-    [[clang::warn_unused_result]] virtual Error setSurfaceDamage(const android::Region& damage) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setSurfaceDamage(
+            const android::Region& damage) = 0;
 
-    [[clang::warn_unused_result]] virtual Error setBlendMode(BlendMode mode) = 0;
-    [[clang::warn_unused_result]] virtual Error setColor(hwc_color_t color) = 0;
-    [[clang::warn_unused_result]] virtual Error setCompositionType(Composition type) = 0;
-    [[clang::warn_unused_result]] virtual Error setDataspace(android::ui::Dataspace dataspace) = 0;
-    [[clang::warn_unused_result]] virtual Error setPerFrameMetadata(
+    [[clang::warn_unused_result]] virtual hal::Error setBlendMode(hal::BlendMode mode) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setColor(hal::Color color) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setCompositionType(hal::Composition type) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setDataspace(hal::Dataspace dataspace) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setPerFrameMetadata(
             const int32_t supportedPerFrameMetadata, const android::HdrMetadata& metadata) = 0;
-    [[clang::warn_unused_result]] virtual Error setDisplayFrame(const android::Rect& frame) = 0;
-    [[clang::warn_unused_result]] virtual Error setPlaneAlpha(float alpha) = 0;
-    [[clang::warn_unused_result]] virtual Error setSidebandStream(
+    [[clang::warn_unused_result]] virtual hal::Error setDisplayFrame(
+            const android::Rect& frame) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setPlaneAlpha(float alpha) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setSidebandStream(
             const native_handle_t* stream) = 0;
-    [[clang::warn_unused_result]] virtual Error setSourceCrop(const android::FloatRect& crop) = 0;
-    [[clang::warn_unused_result]] virtual Error setTransform(Transform transform) = 0;
-    [[clang::warn_unused_result]] virtual Error setVisibleRegion(const android::Region& region) = 0;
-    [[clang::warn_unused_result]] virtual Error setZOrder(uint32_t z) = 0;
-    [[clang::warn_unused_result]] virtual Error setInfo(uint32_t type, uint32_t appId) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setSourceCrop(
+            const android::FloatRect& crop) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setTransform(hal::Transform transform) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setVisibleRegion(
+            const android::Region& region) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setZOrder(uint32_t z) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setInfo(uint32_t type, uint32_t appId) = 0;
 
     // Composer HAL 2.3
-    [[clang::warn_unused_result]] virtual Error setColorTransform(const android::mat4& matrix) = 0;
+    [[clang::warn_unused_result]] virtual hal::Error setColorTransform(
+            const android::mat4& matrix) = 0;
+
+    // Composer HAL 2.4
+    [[clang::warn_unused_result]] virtual hal::Error setLayerGenericMetadata(
+            const std::string& name, bool mandatory, const std::vector<uint8_t>& value) = 0;
 };
 
 namespace impl {
 
-// Convenience C++ class to access hwc2_device_t Layer functions directly.
+// Convenience C++ class to access per layer functions directly.
 
 class Layer : public HWC2::Layer {
 public:
     Layer(android::Hwc2::Composer& composer,
-          const std::unordered_set<Capability>& capabilities,
-          hwc2_display_t displayId, hwc2_layer_t layerId);
+          const std::unordered_set<hal::Capability>& capabilities, hal::HWDisplayId displayId,
+          hal::HWLayerId layerId);
     ~Layer() override;
 
-    hwc2_layer_t getId() const override { return mId; }
+    hal::HWLayerId getId() const override { return mId; }
 
-    Error setCursorPosition(int32_t x, int32_t y) override;
-    Error setBuffer(uint32_t slot, const android::sp<android::GraphicBuffer>& buffer,
-                    const android::sp<android::Fence>& acquireFence) override;
-    Error setSurfaceDamage(const android::Region& damage) override;
+    hal::Error setCursorPosition(int32_t x, int32_t y) override;
+    hal::Error setBuffer(uint32_t slot, const android::sp<android::GraphicBuffer>& buffer,
+                         const android::sp<android::Fence>& acquireFence) override;
+    hal::Error setSurfaceDamage(const android::Region& damage) override;
 
-    Error setBlendMode(BlendMode mode) override;
-    Error setColor(hwc_color_t color) override;
-    Error setCompositionType(Composition type) override;
-    Error setDataspace(android::ui::Dataspace dataspace) override;
-    Error setPerFrameMetadata(const int32_t supportedPerFrameMetadata,
-                              const android::HdrMetadata& metadata) override;
-    Error setDisplayFrame(const android::Rect& frame) override;
-    Error setPlaneAlpha(float alpha) override;
-    Error setSidebandStream(const native_handle_t* stream) override;
-    Error setSourceCrop(const android::FloatRect& crop) override;
-    Error setTransform(Transform transform) override;
-    Error setVisibleRegion(const android::Region& region) override;
-    Error setZOrder(uint32_t z) override;
-    Error setInfo(uint32_t type, uint32_t appId) override;
+    hal::Error setBlendMode(hal::BlendMode mode) override;
+    hal::Error setColor(hal::Color color) override;
+    hal::Error setCompositionType(hal::Composition type) override;
+    hal::Error setDataspace(hal::Dataspace dataspace) override;
+    hal::Error setPerFrameMetadata(const int32_t supportedPerFrameMetadata,
+                                   const android::HdrMetadata& metadata) override;
+    hal::Error setDisplayFrame(const android::Rect& frame) override;
+    hal::Error setPlaneAlpha(float alpha) override;
+    hal::Error setSidebandStream(const native_handle_t* stream) override;
+    hal::Error setSourceCrop(const android::FloatRect& crop) override;
+    hal::Error setTransform(hal::Transform transform) override;
+    hal::Error setVisibleRegion(const android::Region& region) override;
+    hal::Error setZOrder(uint32_t z) override;
+    hal::Error setInfo(uint32_t type, uint32_t appId) override;
 
     // Composer HAL 2.3
-    Error setColorTransform(const android::mat4& matrix) override;
+    hal::Error setColorTransform(const android::mat4& matrix) override;
+
+    // Composer HAL 2.4
+    hal::Error setLayerGenericMetadata(const std::string& name, bool mandatory,
+                                       const std::vector<uint8_t>& value) override;
 
 private:
     // These are references to data owned by HWC2::Device, which will outlive
     // this HWC2::Layer, so these references are guaranteed to be valid for
     // the lifetime of this object.
     android::Hwc2::Composer& mComposer;
-    const std::unordered_set<Capability>& mCapabilities;
+    const std::unordered_set<hal::Capability>& mCapabilities;
 
-    hwc2_display_t mDisplayId;
-    hwc2_layer_t mId;
+    hal::HWDisplayId mDisplayId;
+    hal::HWLayerId mId;
 
     // Cached HWC2 data, to ensure the same commands aren't sent to the HWC
     // multiple times.
     android::Region mVisibleRegion = android::Region::INVALID_REGION;
     android::Region mDamageRegion = android::Region::INVALID_REGION;
-    android::ui::Dataspace mDataSpace = android::ui::Dataspace::UNKNOWN;
+    hal::Dataspace mDataSpace = hal::Dataspace::UNKNOWN;
     android::HdrMetadata mHdrMetadata;
     android::mat4 mColorMatrix;
     uint32_t mBufferSlot;
 };
 
 } // namespace impl
-
 } // namespace HWC2
+} // namespace android
 
 #endif // ANDROID_SF_HWC2_H
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 1099041..7a2f0f3 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -14,12 +14,18 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 
 #undef LOG_TAG
 #define LOG_TAG "HWComposer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include "HWComposer.h"
+
 #include <compositionengine/Output.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -29,12 +35,11 @@
 #include <utils/Errors.h>
 #include <utils/Trace.h>
 
-#include "HWComposer.h"
-#include "HWC2.h"
-#include "ComposerHal.h"
-
-#include "../Layer.h"           // needed only for debugging
+#include "../Layer.h" // needed only for debugging
+#include "../Promise.h"
 #include "../SurfaceFlinger.h"
+#include "ComposerHal.h"
+#include "HWC2.h"
 
 #define LOG_HWC_DISPLAY_ERROR(hwcDisplayId, msg) \
     ALOGE("%s failed for HWC display %" PRIu64 ": %s", __FUNCTION__, hwcDisplayId, msg)
@@ -56,7 +61,7 @@
 
 #define RETURN_IF_HWC_ERROR_FOR(what, error, displayId, ...) \
     do {                                                     \
-        if (error != HWC2::Error::None) {                    \
+        if (error != hal::Error::NONE) {                     \
             LOG_HWC_ERROR(what, error, displayId);           \
             return __VA_ARGS__;                              \
         }                                                    \
@@ -65,29 +70,111 @@
 #define RETURN_IF_HWC_ERROR(error, displayId, ...) \
     RETURN_IF_HWC_ERROR_FOR(__FUNCTION__, error, displayId, __VA_ARGS__)
 
+namespace hal = android::hardware::graphics::composer::hal;
+
+namespace {
+
+using android::hardware::Return;
+using android::hardware::Void;
+using android::HWC2::ComposerCallback;
+
+class ComposerCallbackBridge : public hal::IComposerCallback {
+public:
+    ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId,
+                           bool vsyncSwitchingSupported)
+          : mCallback(callback),
+            mSequenceId(sequenceId),
+            mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
+
+    android::hardware::Return<void> onHotplug(hal::HWDisplayId display,
+                                              hal::Connection conn) override {
+        mCallback->onHotplugReceived(mSequenceId, display, conn);
+        return android::hardware::Void();
+    }
+
+    android::hardware::Return<void> onRefresh(hal::HWDisplayId display) override {
+        mCallback->onRefreshReceived(mSequenceId, display);
+        return android::hardware::Void();
+    }
+
+    android::hardware::Return<void> onVsync(hal::HWDisplayId display, int64_t timestamp) override {
+        if (!mVsyncSwitchingSupported) {
+            mCallback->onVsyncReceived(mSequenceId, display, timestamp, std::nullopt);
+        } else {
+            ALOGW("Unexpected onVsync callback on composer >= 2.4, ignoring.");
+        }
+        return android::hardware::Void();
+    }
+
+    android::hardware::Return<void> onVsync_2_4(hal::HWDisplayId display, int64_t timestamp,
+                                                hal::VsyncPeriodNanos vsyncPeriodNanos) override {
+        if (mVsyncSwitchingSupported) {
+            mCallback->onVsyncReceived(mSequenceId, display, timestamp,
+                                       std::make_optional(vsyncPeriodNanos));
+        } else {
+            ALOGW("Unexpected onVsync_2_4 callback on composer <= 2.3, ignoring.");
+        }
+        return android::hardware::Void();
+    }
+
+    android::hardware::Return<void> onVsyncPeriodTimingChanged(
+            hal::HWDisplayId display,
+            const hal::VsyncPeriodChangeTimeline& updatedTimeline) override {
+        mCallback->onVsyncPeriodTimingChangedReceived(mSequenceId, display, updatedTimeline);
+        return android::hardware::Void();
+    }
+
+    android::hardware::Return<void> onSeamlessPossible(hal::HWDisplayId display) override {
+        mCallback->onSeamlessPossible(mSequenceId, display);
+        return android::hardware::Void();
+    }
+
+private:
+    ComposerCallback* mCallback;
+    const int32_t mSequenceId;
+    const bool mVsyncSwitchingSupported;
+};
+
+} // namespace
+
 namespace android {
 
 HWComposer::~HWComposer() = default;
 
 namespace impl {
 
-HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer)
-      : mHwcDevice(std::make_unique<HWC2::Device>(std::move(composer))) {}
+HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer) : mComposer(std::move(composer)) {
+}
+
+HWComposer::HWComposer(const std::string& composerServiceName)
+      : mComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {
+}
 
 HWComposer::~HWComposer() {
     mDisplayData.clear();
 }
 
-void HWComposer::registerCallback(HWC2::ComposerCallback* callback,
-                                  int32_t sequenceId) {
-    mHwcDevice->registerCallback(callback, sequenceId);
+void HWComposer::setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) {
+    loadCapabilities();
+    loadLayerMetadataSupport();
+
+    if (mRegisteredCallback) {
+        ALOGW("Callback already registered. Ignored extra registration attempt.");
+        return;
+    }
+    mRegisteredCallback = true;
+    sp<ComposerCallbackBridge> callbackBridge(
+            new ComposerCallbackBridge(callback, sequenceId,
+                                       mComposer->isVsyncPeriodSwitchSupported()));
+    mComposer->registerCallback(callbackBridge);
 }
 
-bool HWComposer::getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
+bool HWComposer::getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort,
                                               DisplayIdentificationData* outData) const {
-    const auto error = mHwcDevice->getDisplayIdentificationData(hwcDisplayId, outPort, outData);
-    if (error != HWC2::Error::None) {
-        if (error != HWC2::Error::Unsupported) {
+    const auto error = static_cast<hal::Error>(
+            mComposer->getDisplayIdentificationData(hwcDisplayId, outPort, outData));
+    if (error != hal::Error::NONE) {
+        if (error != hal::Error::UNSUPPORTED) {
             LOG_HWC_DISPLAY_ERROR(hwcDisplayId, to_string(error).c_str());
         }
         return false;
@@ -95,82 +182,29 @@
     return true;
 }
 
-bool HWComposer::hasCapability(HWC2::Capability capability) const
-{
-    return mHwcDevice->getCapabilities().count(capability) > 0;
+bool HWComposer::hasCapability(hal::Capability capability) const {
+    return mCapabilities.count(capability) > 0;
 }
 
-bool HWComposer::hasDisplayCapability(const std::optional<DisplayId>& displayId,
-                                      HWC2::DisplayCapability capability) const {
-    if (!displayId) {
-        // Checkout global capabilities for displays without a corresponding HWC display.
-        if (capability == HWC2::DisplayCapability::SkipClientColorTransform) {
-            return hasCapability(HWC2::Capability::SkipClientColorTransform);
-        }
-        return false;
-    }
-    RETURN_IF_INVALID_DISPLAY(*displayId, false);
-    return mDisplayData.at(*displayId).hwcDisplay->getCapabilities().count(capability) > 0;
+bool HWComposer::hasDisplayCapability(DisplayId displayId,
+                                      hal::DisplayCapability capability) const {
+    RETURN_IF_INVALID_DISPLAY(displayId, false);
+    return mDisplayData.at(displayId).hwcDisplay->getCapabilities().count(capability) > 0;
 }
 
-void HWComposer::validateChange(HWC2::Composition from, HWC2::Composition to) {
-    bool valid = true;
-    switch (from) {
-        case HWC2::Composition::Client:
-            valid = false;
-            break;
-        case HWC2::Composition::Device:
-        case HWC2::Composition::SolidColor:
-            valid = (to == HWC2::Composition::Client);
-            break;
-        case HWC2::Composition::Cursor:
-        case HWC2::Composition::Sideband:
-            valid = (to == HWC2::Composition::Client ||
-                    to == HWC2::Composition::Device);
-            break;
-        default:
-            break;
-    }
-
-    if (!valid) {
-        ALOGE("Invalid layer type change: %s --> %s", to_string(from).c_str(),
-                to_string(to).c_str());
-    }
-}
-
-std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hwc2_display_t hwcDisplayId,
-                                                               HWC2::Connection connection) {
-    std::optional<DisplayIdentificationInfo> info;
-
-    if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) {
-        info = DisplayIdentificationInfo{*displayId, std::string()};
-    } else {
-        if (connection == HWC2::Connection::Disconnected) {
-            ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hal::HWDisplayId hwcDisplayId,
+                                                               hal::Connection connection) {
+    switch (connection) {
+        case hal::Connection::CONNECTED:
+            return onHotplugConnect(hwcDisplayId);
+        case hal::Connection::DISCONNECTED:
+            return onHotplugDisconnect(hwcDisplayId);
+        case hal::Connection::INVALID:
             return {};
-        }
-
-        info = onHotplugConnect(hwcDisplayId);
-        if (!info) return {};
     }
-
-    ALOGV("%s: %s %s display %s with HWC ID %" PRIu64, __FUNCTION__, to_string(connection).c_str(),
-          hwcDisplayId == mInternalHwcDisplayId ? "internal" : "external",
-          to_string(info->id).c_str(), hwcDisplayId);
-
-    mHwcDevice->onHotplug(hwcDisplayId, connection);
-
-    // Disconnect is handled through HWComposer::disconnectDisplay via
-    // SurfaceFlinger's onHotplugReceived callback handling
-    if (connection == HWC2::Connection::Connected) {
-        mDisplayData[info->id].hwcDisplay = mHwcDevice->getDisplayById(hwcDisplayId);
-        mPhysicalDisplayIdMap[hwcDisplayId] = info->id;
-    }
-
-    return info;
 }
 
-bool HWComposer::onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp) {
+bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) {
     const auto displayId = toPhysicalDisplayId(hwcDisplayId);
     if (!displayId) {
         LOG_HWC_DISPLAY_ERROR(hwcDisplayId, "Invalid HWC display");
@@ -222,14 +256,18 @@
               height, SurfaceFlinger::maxVirtualDisplaySize);
         return {};
     }
-    HWC2::Display* display;
-    auto error = mHwcDevice->createVirtualDisplay(width, height, format,
-            &display);
-    if (error != HWC2::Error::None) {
+    hal::HWDisplayId hwcDisplayId = 0;
+    const auto error = static_cast<hal::Error>(
+            mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId));
+    if (error != hal::Error::NONE) {
         ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__);
         return {};
     }
 
+    auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities,
+                                                         hwcDisplayId, hal::DisplayType::VIRTUAL);
+    display->setConnected(true);
+
     DisplayId displayId;
     if (mFreeVirtualDisplayIds.empty()) {
         displayId = getVirtualDisplayId(mNextVirtualDisplayId++);
@@ -239,19 +277,34 @@
     }
 
     auto& displayData = mDisplayData[displayId];
-    displayData.hwcDisplay = display;
+    displayData.hwcDisplay = std::move(display);
     displayData.isVirtual = true;
 
     --mRemainingHwcVirtualDisplays;
     return displayId;
 }
 
+void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) {
+    if (!mInternalHwcDisplayId) {
+        mInternalHwcDisplayId = hwcDisplayId;
+    } else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) {
+        mExternalHwcDisplayId = hwcDisplayId;
+    }
+
+    auto& displayData = mDisplayData[displayId];
+    auto newDisplay =
+            std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
+                                                  hal::DisplayType::PHYSICAL);
+    newDisplay->setConnected(true);
+    displayData.hwcDisplay = std::move(newDisplay);
+    mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
+}
+
 HWC2::Layer* HWComposer::createLayer(DisplayId displayId) {
     RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
 
-    auto display = mDisplayData[displayId].hwcDisplay;
     HWC2::Layer* layer;
-    auto error = display->createLayer(&layer);
+    auto error = mDisplayData[displayId].hwcDisplay->createLayer(&layer);
     RETURN_IF_HWC_ERROR(error, displayId, nullptr);
     return layer;
 }
@@ -259,8 +312,7 @@
 void HWComposer::destroyLayer(DisplayId displayId, HWC2::Layer* layer) {
     RETURN_IF_INVALID_DISPLAY(displayId);
 
-    auto display = mDisplayData[displayId].hwcDisplay;
-    auto error = display->destroyLayer(layer);
+    auto error = mDisplayData[displayId].hwcDisplay->destroyLayer(layer);
     RETURN_IF_HWC_ERROR(error, displayId);
 }
 
@@ -272,8 +324,8 @@
     // the refresh period and whatever closest timestamp we have.
     std::lock_guard lock(displayData.lastHwVsyncLock);
     nsecs_t now = systemTime(CLOCK_MONOTONIC);
-    auto vsyncPeriod = getActiveConfig(displayId)->getVsyncPeriod();
-    return now - ((now - displayData.lastHwVsync) % vsyncPeriod);
+    auto vsyncPeriodNanos = getDisplayVsyncPeriod(displayId);
+    return now - ((now - displayData.lastHwVsync) % vsyncPeriodNanos);
 }
 
 bool HWComposer::isConnected(DisplayId displayId) const {
@@ -301,7 +353,7 @@
 
     std::shared_ptr<const HWC2::Display::Config> config;
     auto error = mDisplayData.at(displayId).hwcDisplay->getActiveConfig(&config);
-    if (error == HWC2::Error::BadConfig) {
+    if (error == hal::Error::BAD_CONFIG) {
         LOG_DISPLAY_ERROR(displayId, "No active config");
         return nullptr;
     }
@@ -316,12 +368,43 @@
     return config;
 }
 
+// Composer 2.4
+
+DisplayConnectionType HWComposer::getDisplayConnectionType(DisplayId displayId) const {
+    RETURN_IF_INVALID_DISPLAY(displayId, DisplayConnectionType::Internal);
+    const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay;
+
+    DisplayConnectionType type;
+    const auto error = hwcDisplay->getConnectionType(&type);
+
+    const auto FALLBACK_TYPE = hwcDisplay->getId() == mInternalHwcDisplayId
+            ? DisplayConnectionType::Internal
+            : DisplayConnectionType::External;
+
+    RETURN_IF_HWC_ERROR(error, displayId, FALLBACK_TYPE);
+    return type;
+}
+
+bool HWComposer::isVsyncPeriodSwitchSupported(DisplayId displayId) const {
+    RETURN_IF_INVALID_DISPLAY(displayId, false);
+    return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported();
+}
+
+nsecs_t HWComposer::getDisplayVsyncPeriod(DisplayId displayId) const {
+    RETURN_IF_INVALID_DISPLAY(displayId, 0);
+
+    nsecs_t vsyncPeriodNanos;
+    auto error = mDisplayData.at(displayId).hwcDisplay->getDisplayVsyncPeriod(&vsyncPeriodNanos);
+    RETURN_IF_HWC_ERROR(error, displayId, 0);
+    return vsyncPeriodNanos;
+}
+
 int HWComposer::getActiveConfigIndex(DisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, -1);
 
     int index;
     auto error = mDisplayData.at(displayId).hwcDisplay->getActiveConfigIndex(&index);
-    if (error == HWC2::Error::BadConfig) {
+    if (error == hal::Error::BAD_CONFIG) {
         LOG_DISPLAY_ERROR(displayId, "No active config");
         return -1;
     }
@@ -359,7 +442,7 @@
     return NO_ERROR;
 }
 
-void HWComposer::setVsyncEnabled(DisplayId displayId, HWC2::Vsync enabled) {
+void HWComposer::setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) {
     RETURN_IF_INVALID_DISPLAY(displayId);
     auto& displayData = mDisplayData[displayId];
 
@@ -384,7 +467,7 @@
     displayData.vsyncEnabled = enabled;
 
     const auto tag = "HW_VSYNC_ON_" + to_string(displayId);
-    ATRACE_INT(tag.c_str(), enabled == HWC2::Vsync::Enable ? 1 : 0);
+    ATRACE_INT(tag.c_str(), enabled == hal::Vsync::ENABLE ? 1 : 0);
 }
 
 status_t HWComposer::setClientTarget(DisplayId displayId, uint32_t slot,
@@ -399,7 +482,9 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::prepare(DisplayId displayId, const compositionengine::Output& output) {
+status_t HWComposer::getDeviceCompositionChanges(
+        DisplayId displayId, bool frameUsesClientComposition,
+        std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
     ATRACE_CALL();
 
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -413,22 +498,18 @@
     uint32_t numTypes = 0;
     uint32_t numRequests = 0;
 
-    HWC2::Error error = HWC2::Error::None;
+    hal::Error error = hal::Error::NONE;
 
     // First try to skip validate altogether when there is no client
     // composition.  When there is client composition, since we haven't
     // rendered to the client target yet, we should not attempt to skip
     // validate.
-    //
-    // displayData.hasClientComposition hasn't been updated for this frame.
-    // The check below is incorrect.  We actually rely on HWC here to fall
-    // back to validate when there is any client layer.
     displayData.validateWasSkipped = false;
-    if (!displayData.hasClientComposition) {
+    if (!frameUsesClientComposition) {
         sp<Fence> outPresentFence;
         uint32_t state = UINT32_MAX;
         error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
-        if (error != HWC2::Error::HasChanges) {
+        if (!hasChangesError(error)) {
             RETURN_IF_HWC_ERROR_FOR("presentOrValidate", error, displayId, UNKNOWN_ERROR);
         }
         if (state == 1) { //Present Succeeded.
@@ -445,103 +526,33 @@
         error = hwcDisplay->validate(&numTypes, &numRequests);
     }
     ALOGV("SkipValidate failed, Falling back to SLOW validate/present");
-    if (error != HWC2::Error::HasChanges) {
+    if (!hasChangesError(error)) {
         RETURN_IF_HWC_ERROR_FOR("validate", error, displayId, BAD_INDEX);
     }
 
-    std::unordered_map<HWC2::Layer*, HWC2::Composition> changedTypes;
+    android::HWComposer::DeviceRequestedChanges::ChangedTypes changedTypes;
     changedTypes.reserve(numTypes);
     error = hwcDisplay->getChangedCompositionTypes(&changedTypes);
     RETURN_IF_HWC_ERROR_FOR("getChangedCompositionTypes", error, displayId, BAD_INDEX);
 
-    displayData.displayRequests = static_cast<HWC2::DisplayRequest>(0);
-    std::unordered_map<HWC2::Layer*, HWC2::LayerRequest> layerRequests;
+    auto displayRequests = static_cast<hal::DisplayRequest>(0);
+    android::HWComposer::DeviceRequestedChanges::LayerRequests layerRequests;
     layerRequests.reserve(numRequests);
-    error = hwcDisplay->getRequests(&displayData.displayRequests,
-            &layerRequests);
+    error = hwcDisplay->getRequests(&displayRequests, &layerRequests);
     RETURN_IF_HWC_ERROR_FOR("getRequests", error, displayId, BAD_INDEX);
 
-    displayData.hasClientComposition = false;
-    displayData.hasDeviceComposition = false;
-    for (auto& outputLayer : output.getOutputLayersOrderedByZ()) {
-        auto& state = outputLayer->editState();
-        LOG_FATAL_IF(!state.hwc.);
-        auto hwcLayer = (*state.hwc).hwcLayer;
+    DeviceRequestedChanges::ClientTargetProperty clientTargetProperty;
+    error = hwcDisplay->getClientTargetProperty(&clientTargetProperty);
 
-        if (auto it = changedTypes.find(hwcLayer.get()); it != changedTypes.end()) {
-            auto newCompositionType = it->second;
-            validateChange(static_cast<HWC2::Composition>((*state.hwc).hwcCompositionType),
-                           newCompositionType);
-            (*state.hwc).hwcCompositionType =
-                    static_cast<Hwc2::IComposerClient::Composition>(newCompositionType);
-        }
-
-        switch ((*state.hwc).hwcCompositionType) {
-            case Hwc2::IComposerClient::Composition::CLIENT:
-                displayData.hasClientComposition = true;
-                break;
-            case Hwc2::IComposerClient::Composition::DEVICE:
-            case Hwc2::IComposerClient::Composition::SOLID_COLOR:
-            case Hwc2::IComposerClient::Composition::CURSOR:
-            case Hwc2::IComposerClient::Composition::SIDEBAND:
-                displayData.hasDeviceComposition = true;
-                break;
-            default:
-                break;
-        }
-
-        state.clearClientTarget = false;
-        if (auto it = layerRequests.find(hwcLayer.get()); it != layerRequests.end()) {
-            auto request = it->second;
-            if (request == HWC2::LayerRequest::ClearClientTarget) {
-                state.clearClientTarget = true;
-            } else {
-                LOG_DISPLAY_ERROR(displayId,
-                                  ("Unknown layer request " + to_string(request)).c_str());
-            }
-        }
-    }
-
+    outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
+                                               std::move(layerRequests),
+                                               std::move(clientTargetProperty)});
     error = hwcDisplay->acceptChanges();
     RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX);
 
     return NO_ERROR;
 }
 
-bool HWComposer::hasDeviceComposition(const std::optional<DisplayId>& displayId) const {
-    if (!displayId) {
-        // Displays without a corresponding HWC display are never composed by
-        // the device
-        return false;
-    }
-
-    RETURN_IF_INVALID_DISPLAY(*displayId, false);
-    return mDisplayData.at(*displayId).hasDeviceComposition;
-}
-
-bool HWComposer::hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const {
-    if (!displayId) {
-        // Displays without a corresponding HWC display are never composed by
-        // the device
-        return false;
-    }
-
-    RETURN_IF_INVALID_DISPLAY(*displayId, false);
-    return ((static_cast<uint32_t>(mDisplayData.at(*displayId).displayRequests) &
-             static_cast<uint32_t>(HWC2::DisplayRequest::FlipClientTarget)) != 0);
-}
-
-bool HWComposer::hasClientComposition(const std::optional<DisplayId>& displayId) const {
-    if (!displayId) {
-        // Displays without a corresponding HWC display are always composed by
-        // the client
-        return true;
-    }
-
-    RETURN_IF_INVALID_DISPLAY(*displayId, true);
-    return mDisplayData.at(*displayId).hasClientComposition;
-}
-
 sp<Fence> HWComposer::getPresentFence(DisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
     return mDisplayData.at(displayId).lastPresentFence;
@@ -549,12 +560,13 @@
 
 sp<Fence> HWComposer::getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
-    auto displayFences = mDisplayData.at(displayId).releaseFences;
-    if (displayFences.count(layer) == 0) {
+    const auto& displayFences = mDisplayData.at(displayId).releaseFences;
+    auto fence = displayFences.find(layer);
+    if (fence == displayFences.end()) {
         ALOGV("getLayerReleaseFence: Release fence not found");
         return Fence::NO_FENCE;
     }
-    return displayFences[layer];
+    return fence->second;
 }
 
 status_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) {
@@ -567,8 +579,8 @@
 
     if (displayData.validateWasSkipped) {
         // explicitly flush all pending commands
-        auto error = mHwcDevice->flushCommands();
-        RETURN_IF_HWC_ERROR_FOR("flushCommands", error, displayId, UNKNOWN_ERROR);
+        auto error = static_cast<hal::Error>(mComposer->executeCommands());
+        RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR);
         RETURN_IF_HWC_ERROR_FOR("present", displayData.presentError, displayId, UNKNOWN_ERROR);
         return NO_ERROR;
     }
@@ -585,7 +597,7 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setPowerMode(DisplayId displayId, int32_t intMode) {
+status_t HWComposer::setPowerMode(DisplayId displayId, hal::PowerMode mode) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     const auto& displayData = mDisplayData[displayId];
@@ -594,42 +606,41 @@
         return INVALID_OPERATION;
     }
 
-    auto mode = static_cast<HWC2::PowerMode>(intMode);
-    if (mode == HWC2::PowerMode::Off) {
-        setVsyncEnabled(displayId, HWC2::Vsync::Disable);
+    if (mode == hal::PowerMode::OFF) {
+        setVsyncEnabled(displayId, hal::Vsync::DISABLE);
     }
 
     auto& hwcDisplay = displayData.hwcDisplay;
     switch (mode) {
-        case HWC2::PowerMode::Off:
-        case HWC2::PowerMode::On:
+        case hal::PowerMode::OFF:
+        case hal::PowerMode::ON:
             ALOGV("setPowerMode: Calling HWC %s", to_string(mode).c_str());
             {
                 auto error = hwcDisplay->setPowerMode(mode);
-                if (error != HWC2::Error::None) {
-                    LOG_HWC_ERROR(("setPowerMode(" + to_string(mode) + ")").c_str(),
-                                  error, displayId);
+                if (error != hal::Error::NONE) {
+                    LOG_HWC_ERROR(("setPowerMode(" + to_string(mode) + ")").c_str(), error,
+                                  displayId);
                 }
             }
             break;
-        case HWC2::PowerMode::Doze:
-        case HWC2::PowerMode::DozeSuspend:
+        case hal::PowerMode::DOZE:
+        case hal::PowerMode::DOZE_SUSPEND:
             ALOGV("setPowerMode: Calling HWC %s", to_string(mode).c_str());
             {
                 bool supportsDoze = false;
                 auto error = hwcDisplay->supportsDoze(&supportsDoze);
-                if (error != HWC2::Error::None) {
+                if (error != hal::Error::NONE) {
                     LOG_HWC_ERROR("supportsDoze", error, displayId);
                 }
 
                 if (!supportsDoze) {
-                    mode = HWC2::PowerMode::On;
+                    mode = hal::PowerMode::ON;
                 }
 
                 error = hwcDisplay->setPowerMode(mode);
-                if (error != HWC2::Error::None) {
-                    LOG_HWC_ERROR(("setPowerMode(" + to_string(mode) + ")").c_str(),
-                                  error, displayId);
+                if (error != hal::Error::NONE) {
+                    LOG_HWC_ERROR(("setPowerMode(" + to_string(mode) + ")").c_str(), error,
+                                  displayId);
                 }
             }
             break;
@@ -641,7 +652,9 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setActiveConfig(DisplayId displayId, size_t configId) {
+status_t HWComposer::setActiveConfigWithConstraints(
+        DisplayId displayId, size_t configId, const hal::VsyncPeriodChangeConstraints& constraints,
+        hal::VsyncPeriodChangeTimeline* outTimeline) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& displayData = mDisplayData[displayId];
@@ -650,7 +663,9 @@
         return BAD_INDEX;
     }
 
-    auto error = displayData.hwcDisplay->setActiveConfig(displayData.configMap[configId]);
+    auto error =
+            displayData.hwcDisplay->setActiveConfigWithConstraints(displayData.configMap[configId],
+                                                                   constraints, outTimeline);
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
 }
@@ -660,9 +675,10 @@
 
     auto& displayData = mDisplayData[displayId];
     bool isIdentity = transform == mat4();
-    auto error = displayData.hwcDisplay->setColorTransform(transform,
-            isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY :
-            HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX);
+    auto error = displayData.hwcDisplay
+                         ->setColorTransform(transform,
+                                             isIdentity ? hal::ColorTransform::IDENTITY
+                                                        : hal::ColorTransform::ARBITRARY_MATRIX);
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
 }
@@ -679,8 +695,6 @@
     }
 
     const auto hwcDisplayId = displayData.hwcDisplay->getId();
-    mPhysicalDisplayIdMap.erase(hwcDisplayId);
-    mDisplayData.erase(displayId);
 
     // TODO(b/74619554): Select internal/external display from remaining displays.
     if (hwcDisplayId == mInternalHwcDisplayId) {
@@ -688,8 +702,8 @@
     } else if (hwcDisplayId == mExternalHwcDisplayId) {
         mExternalHwcDisplayId.reset();
     }
-
-    mHwcDevice->destroyDisplay(hwcDisplayId);
+    mPhysicalDisplayIdMap.erase(hwcDisplayId);
+    mDisplayData.erase(displayId);
 }
 
 status_t HWComposer::setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
@@ -755,7 +769,7 @@
             mDisplayData[displayId]
                     .hwcDisplay->getDisplayedContentSamplingAttributes(outFormat, outDataspace,
                                                                        outComponentMask);
-    if (error == HWC2::Error::Unsupported) RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+    if (error == hal::Error::UNSUPPORTED) RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
 }
@@ -768,8 +782,8 @@
                                                                                  componentMask,
                                                                                  maxFrames);
 
-    if (error == HWC2::Error::Unsupported) RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
-    if (error == HWC2::Error::BadParameter) RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+    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;
 }
@@ -784,31 +798,74 @@
     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 == HWC2::Error::Unsupported) {
-        RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
-    }
-    if (error == HWC2::Error::BadParameter) {
-        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 {
     return getComposer()->isUsingVrComposer();
 }
 
-void HWComposer::dump(std::string& result) const {
-    // TODO: In order to provide a dump equivalent to HWC1, we need to shadow
-    // all the state going into the layers. This is probably better done in
-    // Layer itself, but it's going to take a bit of work to get there.
-    result.append(mHwcDevice->dump());
+status_t HWComposer::setAutoLowLatencyMode(DisplayId displayId, bool on) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
+    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::optional<DisplayId> HWComposer::toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const {
+status_t HWComposer::getSupportedContentTypes(
+        DisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error =
+            mDisplayData[displayId].hwcDisplay->getSupportedContentTypes(outSupportedContentTypes);
+
+    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+
+    return NO_ERROR;
+}
+
+status_t HWComposer::setContentType(DisplayId displayId, hal::ContentType contentType) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error = mDisplayData[displayId].hwcDisplay->setContentType(contentType);
+    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;
+}
+
+const std::unordered_map<std::string, bool>& HWComposer::getSupportedLayerGenericMetadata() const {
+    return mSupportedLayerGenericMetadata;
+}
+
+void HWComposer::dump(std::string& result) const {
+    result.append(mComposer->dumpDebugInfo());
+}
+
+std::optional<DisplayId> HWComposer::toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const {
     if (const auto it = mPhysicalDisplayIdMap.find(hwcDisplayId);
         it != mPhysicalDisplayIdMap.end()) {
         return it->second;
@@ -816,7 +873,7 @@
     return {};
 }
 
-std::optional<hwc2_display_t> HWComposer::fromPhysicalDisplayId(DisplayId displayId) const {
+std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId(DisplayId displayId) const {
     if (const auto it = mDisplayData.find(displayId);
         it != mDisplayData.end() && !it->second.isVirtual) {
         return it->second.hwcDisplay->getId();
@@ -824,52 +881,127 @@
     return {};
 }
 
-std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect(hwc2_display_t hwcDisplayId) {
+bool HWComposer::shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId,
+                                            bool hasDisplayIdentificationData) const {
     if (isUsingVrComposer() && mInternalHwcDisplayId) {
         ALOGE("Ignoring connection of external display %" PRIu64 " in VR mode", hwcDisplayId);
-        return {};
+        return true;
     }
 
-    uint8_t port;
-    DisplayIdentificationData data;
-    const bool hasMultiDisplaySupport = getDisplayIdentificationData(hwcDisplayId, &port, &data);
-
-    if (mPhysicalDisplayIdMap.empty()) {
-        mHasMultiDisplaySupport = hasMultiDisplaySupport;
-        ALOGI("Switching to %s multi-display mode",
-              hasMultiDisplaySupport ? "generalized" : "legacy");
-    } else if (mHasMultiDisplaySupport && !hasMultiDisplaySupport) {
+    if (mHasMultiDisplaySupport && !hasDisplayIdentificationData) {
         ALOGE("Ignoring connection of display %" PRIu64 " without identification data",
               hwcDisplayId);
-        return {};
+        return true;
     }
 
-    std::optional<DisplayIdentificationInfo> info;
-
-    if (mHasMultiDisplaySupport) {
-        info = parseDisplayIdentificationData(port, data);
-        ALOGE_IF(!info, "Failed to parse identification data for display %" PRIu64, hwcDisplayId);
-    } else if (mInternalHwcDisplayId && mExternalHwcDisplayId) {
+    if (!mHasMultiDisplaySupport && mInternalHwcDisplayId && mExternalHwcDisplayId) {
         ALOGE("Ignoring connection of tertiary display %" PRIu64, hwcDisplayId);
-        return {};
+        return true;
+    }
+
+    return false;
+}
+
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect(
+        hal::HWDisplayId hwcDisplayId) {
+    std::optional<DisplayIdentificationInfo> info;
+    if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) {
+        info = DisplayIdentificationInfo{.id = *displayId,
+                                         .name = std::string(),
+                                         .deviceProductInfo = std::nullopt};
     } else {
-        ALOGW_IF(hasMultiDisplaySupport, "Ignoring identification data for display %" PRIu64,
-                 hwcDisplayId);
-        port = mInternalHwcDisplayId ? HWC_DISPLAY_EXTERNAL : HWC_DISPLAY_PRIMARY;
+        uint8_t port;
+        DisplayIdentificationData data;
+        const bool hasDisplayIdentificationData =
+                getDisplayIdentificationData(hwcDisplayId, &port, &data);
+        if (mPhysicalDisplayIdMap.empty()) {
+            mHasMultiDisplaySupport = hasDisplayIdentificationData;
+            ALOGI("Switching to %s multi-display mode",
+                  mHasMultiDisplaySupport ? "generalized" : "legacy");
+        }
+
+        if (shouldIgnoreHotplugConnect(hwcDisplayId, hasDisplayIdentificationData)) {
+            return {};
+        }
+
+        info = [this, hwcDisplayId, &port, &data, hasDisplayIdentificationData] {
+            const bool isPrimary = !mInternalHwcDisplayId;
+            if (mHasMultiDisplaySupport) {
+                if (const auto info = parseDisplayIdentificationData(port, data)) {
+                    return *info;
+                }
+                ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId);
+            } else {
+                ALOGW_IF(hasDisplayIdentificationData,
+                         "Ignoring identification data for display %" PRIu64, hwcDisplayId);
+                port = isPrimary ? LEGACY_DISPLAY_TYPE_PRIMARY : LEGACY_DISPLAY_TYPE_EXTERNAL;
+            }
+
+            return DisplayIdentificationInfo{.id = getFallbackDisplayId(port),
+                                             .name = isPrimary ? "Internal display"
+                                                               : "External display",
+                                             .deviceProductInfo = std::nullopt};
+        }();
     }
 
-    if (!mInternalHwcDisplayId) {
-        mInternalHwcDisplayId = hwcDisplayId;
-    } else if (!mExternalHwcDisplayId) {
-        mExternalHwcDisplayId = hwcDisplayId;
+    if (!isConnected(info->id)) {
+        allocatePhysicalDisplay(hwcDisplayId, info->id);
+    }
+    return info;
+}
+
+std::optional<DisplayIdentificationInfo> HWComposer::onHotplugDisconnect(
+        hal::HWDisplayId hwcDisplayId) {
+    const auto displayId = toPhysicalDisplayId(hwcDisplayId);
+    if (!displayId) {
+        ALOGE("Ignoring disconnection of invalid HWC display %" PRIu64, hwcDisplayId);
+        return {};
     }
 
-    if (info) return info;
+    // The display will later be destroyed by a call to
+    // destroyDisplay(). For now we just mark it disconnected.
+    if (isConnected(*displayId)) {
+        mDisplayData[*displayId].hwcDisplay->setConnected(false);
+    } else {
+        ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
+    }
+    // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
+    // via SurfaceFlinger's onHotplugReceived callback handling
+    return DisplayIdentificationInfo{.id = *displayId,
+                                     .name = std::string(),
+                                     .deviceProductInfo = std::nullopt};
+}
 
-    return DisplayIdentificationInfo{getFallbackDisplayId(port),
-                                     hwcDisplayId == mInternalHwcDisplayId ? "Internal display"
-                                                                           : "External display"};
+void HWComposer::loadCapabilities() {
+    static_assert(sizeof(hal::Capability) == sizeof(int32_t), "Capability size has changed");
+    auto capabilities = mComposer->getCapabilities();
+    for (auto capability : capabilities) {
+        mCapabilities.emplace(static_cast<hal::Capability>(capability));
+    }
+}
+
+void HWComposer::loadLayerMetadataSupport() {
+    mSupportedLayerGenericMetadata.clear();
+
+    std::vector<Hwc2::IComposerClient::LayerGenericMetadataKey> supportedMetadataKeyInfo;
+    const auto error = mComposer->getLayerGenericMetadataKeys(&supportedMetadataKeyInfo);
+    if (error != hardware::graphics::composer::V2_4::Error::NONE) {
+        ALOGE("%s: %s failed: %s (%d)", __FUNCTION__, "getLayerGenericMetadataKeys",
+              toString(error).c_str(), static_cast<int32_t>(error));
+        return;
+    }
+
+    for (const auto& [name, mandatory] : supportedMetadataKeyInfo) {
+        mSupportedLayerGenericMetadata.emplace(name, mandatory);
+    }
+}
+
+uint32_t HWComposer::getMaxVirtualDisplayCount() const {
+    return mComposer->getMaxVirtualDisplayCount();
 }
 
 } // namespace impl
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index de863b8..c355ebd 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>
@@ -27,15 +28,24 @@
 
 #include <android-base/thread_annotations.h>
 #include <ui/Fence.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 #include <ui/GraphicTypes.h>
+#pragma clang diagnostic pop
+
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
 
 #include "DisplayIdentification.h"
 #include "HWC2.h"
+#include "Hal.h"
 
 namespace android {
 
+namespace hal = hardware::graphics::composer::hal;
+
 struct DisplayedFrameStats;
 class GraphicBuffer;
 class TestableSurfaceFlinger;
@@ -49,30 +59,57 @@
 class Output;
 } // namespace compositionengine
 
+struct KnownHWCGenericLayerMetadata {
+    const char* name;
+    const uint32_t id;
+};
+
 class HWComposer {
 public:
+    struct DeviceRequestedChanges {
+        using ChangedTypes = std::unordered_map<HWC2::Layer*, hal::Composition>;
+        using ClientTargetProperty = hal::ClientTargetProperty;
+        using DisplayRequests = hal::DisplayRequest;
+        using LayerRequests = std::unordered_map<HWC2::Layer*, hal::LayerRequest>;
+
+        ChangedTypes changedTypes;
+        DisplayRequests displayRequests;
+        LayerRequests layerRequests;
+        ClientTargetProperty clientTargetProperty;
+    };
+
     virtual ~HWComposer();
 
-    virtual void registerCallback(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
+    virtual void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
 
-    virtual bool getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
+    virtual bool getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort,
                                               DisplayIdentificationData* outData) const = 0;
 
-    virtual bool hasCapability(HWC2::Capability capability) const = 0;
-    virtual bool hasDisplayCapability(const std::optional<DisplayId>& displayId,
-                                      HWC2::DisplayCapability capability) const = 0;
+    virtual bool hasCapability(hal::Capability capability) const = 0;
+    virtual bool hasDisplayCapability(DisplayId displayId,
+                                      hal::DisplayCapability capability) const = 0;
 
     // Attempts to allocate a virtual display and returns its ID if created on the HWC device.
     virtual std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
                                                             ui::PixelFormat* format) = 0;
 
+    virtual void allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) = 0;
+
     // Attempts to create a new layer on this display
     virtual HWC2::Layer* createLayer(DisplayId displayId) = 0;
     // Destroy a previously created layer
     virtual void destroyLayer(DisplayId displayId, HWC2::Layer* layer) = 0;
 
-    // Asks the HAL what it can do
-    virtual status_t prepare(DisplayId displayId, const compositionengine::Output&) = 0;
+    // Gets any required composition change requests from the HWC device.
+    //
+    // Note that frameUsesClientComposition must be set correctly based on
+    // whether the current frame appears to use client composition. If it is
+    // false some internal optimizations are allowed to present the display
+    // with fewer handshakes, but this does not work if client composition is
+    // expected.
+    virtual status_t getDeviceCompositionChanges(
+            DisplayId, bool frameUsesClientComposition,
+            std::optional<DeviceRequestedChanges>* outChanges) = 0;
 
     virtual status_t setClientTarget(DisplayId displayId, uint32_t slot,
                                      const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
@@ -82,10 +119,7 @@
     virtual status_t presentAndGetReleaseFences(DisplayId displayId) = 0;
 
     // set power mode
-    virtual status_t setPowerMode(DisplayId displayId, int mode) = 0;
-
-    // set active config
-    virtual status_t setActiveConfig(DisplayId displayId, size_t configId) = 0;
+    virtual status_t setPowerMode(DisplayId displayId, hal::PowerMode mode) = 0;
 
     // Sets a color transform to be applied to the result of composition
     virtual status_t setColorTransform(DisplayId displayId, const mat4& transform) = 0;
@@ -93,15 +127,6 @@
     // reset state when an external, non-virtual display is disconnected
     virtual void disconnectDisplay(DisplayId displayId) = 0;
 
-    // does this display have layers handled by HWC
-    virtual bool hasDeviceComposition(const std::optional<DisplayId>& displayId) const = 0;
-
-    // does this display have pending request to flip client target
-    virtual bool hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const = 0;
-
-    // does this display have layers handled by GLES
-    virtual bool hasClientComposition(const std::optional<DisplayId>& displayId) const = 0;
-
     // get the present fence received from the last call to present.
     virtual sp<Fence> getPresentFence(DisplayId displayId) const = 0;
 
@@ -141,17 +166,18 @@
                                                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 ---------------------------------------------------------
 
     // Returns stable display ID (and display name on connection of new or previously disconnected
     // display), or std::nullopt if hotplug event was ignored.
-    virtual std::optional<DisplayIdentificationInfo> onHotplug(hwc2_display_t hwcDisplayId,
-                                                               HWC2::Connection connection) = 0;
+    // This function is called from SurfaceFlinger.
+    virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId hwcDisplayId,
+                                                               hal::Connection connection) = 0;
 
-    virtual bool onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp) = 0;
-    virtual void setVsyncEnabled(DisplayId displayId, HWC2::Vsync enabled) = 0;
+    virtual bool onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) = 0;
+    virtual void setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) = 0;
 
     virtual nsecs_t getRefreshTimestamp(DisplayId displayId) const = 0;
     virtual bool isConnected(DisplayId displayId) const = 0;
@@ -171,17 +197,32 @@
 
     virtual bool isUsingVrComposer() const = 0;
 
+    // Composer 2.4
+    virtual DisplayConnectionType getDisplayConnectionType(DisplayId) const = 0;
+    virtual bool isVsyncPeriodSwitchSupported(DisplayId displayId) const = 0;
+    virtual nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const = 0;
+    virtual status_t setActiveConfigWithConstraints(
+            DisplayId displayId, size_t configId,
+            const hal::VsyncPeriodChangeConstraints& constraints,
+            hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
+    virtual status_t setAutoLowLatencyMode(DisplayId displayId, bool on) = 0;
+    virtual status_t getSupportedContentTypes(
+            DisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0;
+    virtual status_t setContentType(DisplayId displayId, hal::ContentType contentType) = 0;
+    virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
+            const = 0;
+
     // for debugging ----------------------------------------------------------
     virtual void dump(std::string& out) const = 0;
 
     virtual Hwc2::Composer* getComposer() const = 0;
 
     // TODO(b/74619554): Remove special cases for internal/external display.
-    virtual std::optional<hwc2_display_t> getInternalHwcDisplayId() const = 0;
-    virtual std::optional<hwc2_display_t> getExternalHwcDisplayId() const = 0;
+    virtual std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const = 0;
+    virtual std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const = 0;
 
-    virtual std::optional<DisplayId> toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const = 0;
-    virtual std::optional<hwc2_display_t> fromPhysicalDisplayId(DisplayId displayId) const = 0;
+    virtual std::optional<DisplayId> toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const = 0;
+    virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(DisplayId displayId) const = 0;
 };
 
 namespace impl {
@@ -189,29 +230,34 @@
 class HWComposer final : public android::HWComposer {
 public:
     explicit HWComposer(std::unique_ptr<Hwc2::Composer> composer);
+    explicit HWComposer(const std::string& composerServiceName);
 
     ~HWComposer() override;
 
-    void registerCallback(HWC2::ComposerCallback* callback, int32_t sequenceId) override;
+    void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) override;
 
-    bool getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
+    bool getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort,
                                       DisplayIdentificationData* outData) const override;
 
-    bool hasCapability(HWC2::Capability capability) const override;
-    bool hasDisplayCapability(const std::optional<DisplayId>& displayId,
-                              HWC2::DisplayCapability capability) const override;
+    bool hasCapability(hal::Capability capability) const override;
+    bool hasDisplayCapability(DisplayId displayId,
+                              hal::DisplayCapability capability) const override;
 
     // Attempts to allocate a virtual display and returns its ID if created on the HWC device.
     std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height,
                                                     ui::PixelFormat* format) override;
 
+    // Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
+    void allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) override;
+
     // Attempts to create a new layer on this display
     HWC2::Layer* createLayer(DisplayId displayId) override;
     // Destroy a previously created layer
     void destroyLayer(DisplayId displayId, HWC2::Layer* layer) override;
 
-    // Asks the HAL what it can do
-    status_t prepare(DisplayId displayId, const compositionengine::Output&) override;
+    status_t getDeviceCompositionChanges(
+            DisplayId, bool frameUsesClientComposition,
+            std::optional<DeviceRequestedChanges>* outChanges) override;
 
     status_t setClientTarget(DisplayId displayId, uint32_t slot, const sp<Fence>& acquireFence,
                              const sp<GraphicBuffer>& target, ui::Dataspace dataspace) override;
@@ -220,10 +266,7 @@
     status_t presentAndGetReleaseFences(DisplayId displayId) override;
 
     // set power mode
-    status_t setPowerMode(DisplayId displayId, int mode) override;
-
-    // set active config
-    status_t setActiveConfig(DisplayId displayId, size_t configId) override;
+    status_t setPowerMode(DisplayId displayId, hal::PowerMode mode) override;
 
     // Sets a color transform to be applied to the result of composition
     status_t setColorTransform(DisplayId displayId, const mat4& transform) override;
@@ -231,15 +274,6 @@
     // reset state when an external, non-virtual display is disconnected
     void disconnectDisplay(DisplayId displayId) override;
 
-    // does this display have layers handled by HWC
-    bool hasDeviceComposition(const std::optional<DisplayId>& displayId) const override;
-
-    // does this display have pending request to flip client target
-    bool hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const override;
-
-    // does this display have layers handled by GLES
-    bool hasClientComposition(const std::optional<DisplayId>& displayId) const override;
-
     // get the present fence received from the last call to present.
     sp<Fence> getPresentFence(DisplayId displayId) const override;
 
@@ -274,17 +308,17 @@
                                               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 ---------------------------------------------------------
 
     // Returns stable display ID (and display name on connection of new or previously disconnected
     // display), or std::nullopt if hotplug event was ignored.
-    std::optional<DisplayIdentificationInfo> onHotplug(hwc2_display_t hwcDisplayId,
-                                                       HWC2::Connection connection) override;
+    std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId hwcDisplayId,
+                                                       hal::Connection connection) override;
 
-    bool onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp) override;
-    void setVsyncEnabled(DisplayId displayId, HWC2::Vsync enabled) override;
+    bool onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) override;
+    void setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) override;
 
     nsecs_t getRefreshTimestamp(DisplayId displayId) const override;
     bool isConnected(DisplayId displayId) const override;
@@ -304,36 +338,51 @@
 
     bool isUsingVrComposer() const override;
 
+    // Composer 2.4
+    DisplayConnectionType getDisplayConnectionType(DisplayId) const override;
+    bool isVsyncPeriodSwitchSupported(DisplayId displayId) const override;
+    nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const override;
+    status_t setActiveConfigWithConstraints(DisplayId displayId, size_t configId,
+                                            const hal::VsyncPeriodChangeConstraints& constraints,
+                                            hal::VsyncPeriodChangeTimeline* outTimeline) override;
+    status_t setAutoLowLatencyMode(DisplayId displayId, bool) override;
+    status_t getSupportedContentTypes(DisplayId displayId, std::vector<hal::ContentType>*) override;
+    status_t setContentType(DisplayId displayId, hal::ContentType) override;
+
+    const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const override;
+
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
 
-    Hwc2::Composer* getComposer() const override { return mHwcDevice->getComposer(); }
+    Hwc2::Composer* getComposer() const override { return mComposer.get(); }
 
     // TODO(b/74619554): Remove special cases for internal/external display.
-    std::optional<hwc2_display_t> getInternalHwcDisplayId() const override {
+    std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const override {
         return mInternalHwcDisplayId;
     }
-    std::optional<hwc2_display_t> getExternalHwcDisplayId() const override {
+    std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const override {
         return mExternalHwcDisplayId;
     }
 
-    std::optional<DisplayId> toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const override;
-    std::optional<hwc2_display_t> fromPhysicalDisplayId(DisplayId displayId) const override;
+    std::optional<DisplayId> toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const override;
+    std::optional<hal::HWDisplayId> fromPhysicalDisplayId(DisplayId displayId) const override;
 
 private:
     // For unit tests
     friend TestableSurfaceFlinger;
 
-    std::optional<DisplayIdentificationInfo> onHotplugConnect(hwc2_display_t hwcDisplayId);
+    std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId hwcDisplayId);
+    std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId hwcDisplayId);
+    bool shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId,
+                                    bool hasDisplayIdentificationData) const;
 
-    static void validateChange(HWC2::Composition from, HWC2::Composition to);
+    void loadCapabilities();
+    void loadLayerMetadataSupport();
+    uint32_t getMaxVirtualDisplayCount() const;
 
     struct DisplayData {
         bool isVirtual = false;
-        bool hasClientComposition = false;
-        bool hasDeviceComposition = false;
-        HWC2::Display* hwcDisplay = nullptr;
-        HWC2::DisplayRequest displayRequests;
+        std::unique_ptr<HWC2::Display> hwcDisplay;
         sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
         std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
         buffer_handle_t outbufHandle = nullptr;
@@ -342,12 +391,12 @@
                 std::shared_ptr<const HWC2::Display::Config>> configMap;
 
         bool validateWasSkipped;
-        HWC2::Error presentError;
+        hal::Error presentError;
 
         bool vsyncTraceToggle = false;
 
         std::mutex vsyncEnabledLock;
-        HWC2::Vsync vsyncEnabled GUARDED_BY(vsyncEnabledLock) = HWC2::Vsync::Disable;
+        hal::Vsync vsyncEnabled GUARDED_BY(vsyncEnabledLock) = hal::Vsync::DISABLE;
 
         mutable std::mutex lastHwVsyncLock;
         nsecs_t lastHwVsync GUARDED_BY(lastHwVsyncLock) = 0;
@@ -355,18 +404,19 @@
 
     std::unordered_map<DisplayId, DisplayData> mDisplayData;
 
-    // This must be destroyed before mDisplayData, because destructor may call back into HWComposer
-    // and look up DisplayData.
-    std::unique_ptr<HWC2::Device> mHwcDevice;
+    std::unique_ptr<android::Hwc2::Composer> mComposer;
+    std::unordered_set<hal::Capability> mCapabilities;
+    std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata;
+    bool mRegisteredCallback = false;
 
-    std::unordered_map<hwc2_display_t, DisplayId> mPhysicalDisplayIdMap;
-    std::optional<hwc2_display_t> mInternalHwcDisplayId;
-    std::optional<hwc2_display_t> mExternalHwcDisplayId;
+    std::unordered_map<hal::HWDisplayId, DisplayId> mPhysicalDisplayIdMap;
+    std::optional<hal::HWDisplayId> mInternalHwcDisplayId;
+    std::optional<hal::HWDisplayId> mExternalHwcDisplayId;
     bool mHasMultiDisplaySupport = false;
 
     std::unordered_set<DisplayId> mFreeVirtualDisplayIds;
     uint32_t mNextVirtualDisplayId = 0;
-    uint32_t mRemainingHwcVirtualDisplays{mHwcDevice->getMaxVirtualDisplayCount()};
+    uint32_t mRemainingHwcVirtualDisplays{getMaxVirtualDisplayCount()};
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
new file mode 100644
index 0000000..bb2888e
--- /dev/null
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -0,0 +1,169 @@
+/*
+ * 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/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/composer/2.4/IComposer.h>
+#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+
+#define ERROR_HAS_CHANGES 5
+
+namespace android {
+namespace hardware::graphics::composer::hal {
+
+namespace types = android::hardware::graphics::common;
+namespace V2_1 = android::hardware::graphics::composer::V2_1;
+namespace V2_2 = android::hardware::graphics::composer::V2_2;
+namespace V2_3 = android::hardware::graphics::composer::V2_3;
+namespace V2_4 = android::hardware::graphics::composer::V2_4;
+
+using types::V1_0::ColorTransform;
+using types::V1_0::Transform;
+using types::V1_1::RenderIntent;
+using types::V1_2::ColorMode;
+using types::V1_2::Dataspace;
+using types::V1_2::Hdr;
+using types::V1_2::PixelFormat;
+
+using V2_1::Error;
+using V2_4::IComposer;
+using V2_4::IComposerCallback;
+using V2_4::IComposerClient;
+using V2_4::VsyncPeriodChangeTimeline;
+using V2_4::VsyncPeriodNanos;
+
+using Attribute = IComposerClient::Attribute;
+using BlendMode = IComposerClient::BlendMode;
+using Color = IComposerClient::Color;
+using Composition = IComposerClient::Composition;
+using Connection = IComposerCallback::Connection;
+using ContentType = IComposerClient::ContentType;
+using Capability = IComposer::Capability;
+using ClientTargetProperty = IComposerClient::ClientTargetProperty;
+using DisplayCapability = IComposerClient::DisplayCapability;
+using DisplayRequest = IComposerClient::DisplayRequest;
+using DisplayType = IComposerClient::DisplayType;
+using HWConfigId = V2_1::Config;
+using HWDisplayId = V2_1::Display;
+using HWError = V2_1::Error;
+using HWLayerId = V2_1::Layer;
+using LayerGenericMetadataKey = IComposerClient::LayerGenericMetadataKey;
+using LayerRequest = IComposerClient::LayerRequest;
+using PerFrameMetadata = IComposerClient::PerFrameMetadata;
+using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
+using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob;
+using PowerMode = IComposerClient::PowerMode;
+using Vsync = IComposerClient::Vsync;
+using VsyncPeriodChangeConstraints = IComposerClient::VsyncPeriodChangeConstraints;
+
+} // namespace hardware::graphics::composer::hal
+
+inline bool hasChangesError(hardware::graphics::composer::hal::Error error) {
+    return ERROR_HAS_CHANGES == static_cast<int32_t>(error);
+}
+
+inline std::string to_string(hardware::graphics::composer::hal::Attribute attribute) {
+    switch (attribute) {
+        case hardware::graphics::composer::hal::Attribute::INVALID:
+            return "Invalid";
+        case hardware::graphics::composer::hal::Attribute::WIDTH:
+            return "Width";
+        case hardware::graphics::composer::hal::Attribute::HEIGHT:
+            return "Height";
+        case hardware::graphics::composer::hal::Attribute::VSYNC_PERIOD:
+            return "VsyncPeriod";
+        case hardware::graphics::composer::hal::Attribute::DPI_X:
+            return "DpiX";
+        case hardware::graphics::composer::hal::Attribute::DPI_Y:
+            return "DpiY";
+        default:
+            return "Unknown";
+    }
+}
+
+inline std::string to_string(hardware::graphics::composer::hal::Composition composition) {
+    switch (composition) {
+        case hardware::graphics::composer::hal::Composition::INVALID:
+            return "Invalid";
+        case hardware::graphics::composer::hal::Composition::CLIENT:
+            return "Client";
+        case hardware::graphics::composer::hal::Composition::DEVICE:
+            return "Device";
+        case hardware::graphics::composer::hal::Composition::SOLID_COLOR:
+            return "SolidColor";
+        case hardware::graphics::composer::hal::Composition::CURSOR:
+            return "Cursor";
+        case hardware::graphics::composer::hal::Composition::SIDEBAND:
+            return "Sideband";
+        default:
+            return "Unknown";
+    }
+}
+
+inline std::string to_string(hardware::graphics::composer::hal::V2_4::Error error) {
+    // 5 is reserved for historical reason, during validation 5 means has changes.
+    if (ERROR_HAS_CHANGES == static_cast<int32_t>(error)) {
+        return "HasChanges";
+    }
+    switch (error) {
+        case hardware::graphics::composer::hal::V2_4::Error::NONE:
+            return "None";
+        case hardware::graphics::composer::hal::V2_4::Error::BAD_CONFIG:
+            return "BadConfig";
+        case hardware::graphics::composer::hal::V2_4::Error::BAD_DISPLAY:
+            return "BadDisplay";
+        case hardware::graphics::composer::hal::V2_4::Error::BAD_LAYER:
+            return "BadLayer";
+        case hardware::graphics::composer::hal::V2_4::Error::BAD_PARAMETER:
+            return "BadParameter";
+        case hardware::graphics::composer::hal::V2_4::Error::NO_RESOURCES:
+            return "NoResources";
+        case hardware::graphics::composer::hal::V2_4::Error::NOT_VALIDATED:
+            return "NotValidated";
+        case hardware::graphics::composer::hal::V2_4::Error::UNSUPPORTED:
+            return "Unsupported";
+        case hardware::graphics::composer::hal::V2_4::Error::SEAMLESS_NOT_ALLOWED:
+            return "SeamlessNotAllowed";
+        case hardware::graphics::composer::hal::V2_4::Error::SEAMLESS_NOT_POSSIBLE:
+            return "SeamlessNotPossible";
+        default:
+            return "Unknown";
+    }
+}
+
+inline std::string to_string(hardware::graphics::composer::hal::Error error) {
+    return to_string(static_cast<hardware::graphics::composer::hal::V2_4::Error>(error));
+}
+
+inline std::string to_string(hardware::graphics::composer::hal::PowerMode mode) {
+    switch (mode) {
+        case hardware::graphics::composer::hal::PowerMode::OFF:
+            return "Off";
+        case hardware::graphics::composer::hal::PowerMode::DOZE:
+            return "Doze";
+        case hardware::graphics::composer::hal::PowerMode::ON:
+            return "On";
+        case hardware::graphics::composer::hal::PowerMode::DOZE_SUSPEND:
+            return "DozeSuspend";
+        case hardware::graphics::composer::hal::PowerMode::ON_SUSPEND:
+            return "OnSuspend";
+        default:
+            return "Unknown";
+    }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 039db73..4b4c050 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -14,14 +14,23 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+
 #undef LOG_TAG
 #define LOG_TAG "PowerAdvisor"
 
 #include <cinttypes>
 
+#include <android-base/properties.h>
 #include <utils/Log.h>
 #include <utils/Mutex.h>
 
+#include <android/hardware/power/1.3/IPower.h>
+#include <android/hardware/power/IPower.h>
+#include <binder/IServiceManager.h>
+
+#include "../SurfaceFlingerProperties.h"
+
 #include "PowerAdvisor.h"
 
 namespace android {
@@ -32,11 +41,40 @@
 namespace impl {
 
 namespace V1_0 = android::hardware::power::V1_0;
+namespace V1_3 = android::hardware::power::V1_3;
 using V1_3::PowerHint;
 
+using android::hardware::power::Boost;
+using android::hardware::power::IPower;
+using android::hardware::power::Mode;
+using base::GetIntProperty;
+using scheduler::OneShotTimer;
+
 PowerAdvisor::~PowerAdvisor() = default;
 
-PowerAdvisor::PowerAdvisor() = default;
+namespace {
+int32_t getUpdateTimeout() {
+    // Default to a timeout of 80ms if nothing else is specified
+    static int32_t timeout = sysprop::display_update_imminent_timeout_ms(80);
+    return timeout;
+}
+
+} // namespace
+
+PowerAdvisor::PowerAdvisor()
+      : mUseUpdateImminentTimer(getUpdateTimeout() > 0),
+        mUpdateImminentTimer(
+                OneShotTimer::Interval(getUpdateTimeout()),
+                /* resetCallback */ [this] { mSendUpdateImminent.store(false); },
+                /* timeoutCallback */ [this] { mSendUpdateImminent.store(true); }) {
+    if (mUseUpdateImminentTimer) {
+        mUpdateImminentTimer.start();
+    }
+}
+
+void PowerAdvisor::onBootFinished() {
+    mBootFinished.store(true);
+}
 
 void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) {
     if (expected) {
@@ -47,51 +85,183 @@
 
     const bool expectsExpensiveRendering = !mExpensiveDisplays.empty();
     if (mNotifiedExpensiveRendering != expectsExpensiveRendering) {
-        const sp<V1_3::IPower> powerHal = getPowerHal();
-        if (powerHal == nullptr) {
+        std::lock_guard lock(mPowerHalMutex);
+        HalWrapper* const halWrapper = getPowerHal();
+        if (halWrapper == nullptr) {
             return;
         }
-        auto ret = powerHal->powerHintAsync_1_3(PowerHint::EXPENSIVE_RENDERING,
-                                                expectsExpensiveRendering);
-        // If Power HAL 1.3 was available previously but now fails,
-        // it may restart, so attempt to reconnect next time
-        if (!ret.isOk()) {
+
+        if (!halWrapper->setExpensiveRendering(expectsExpensiveRendering)) {
+            // The HAL has become unavailable; attempt to reconnect later
             mReconnectPowerHal = true;
             return;
         }
+
         mNotifiedExpensiveRendering = expectsExpensiveRendering;
     }
 }
 
-sp<V1_3::IPower> PowerAdvisor::getPowerHal() {
-    static sp<V1_3::IPower> sPowerHal_1_3 = nullptr;
-    static bool sHasPowerHal_1_3 = true;
-
-    if (mReconnectPowerHal) {
-        sPowerHal_1_3 = nullptr;
-        mReconnectPowerHal = false;
+void PowerAdvisor::notifyDisplayUpdateImminent() {
+    // Only start sending this notification once the system has booted so we don't introduce an
+    // early-boot dependency on Power HAL
+    if (!mBootFinished.load()) {
+        return;
     }
 
-    // Power HAL 1.3 is not guaranteed to be available, thus we need to query
-    // Power HAL 1.0 first and try to cast it to Power HAL 1.3.
-    // Power HAL 1.0 is always available, thus if we fail to query it, it means
-    // Power HAL is not available temporarily and we should retry later. However,
-    // if Power HAL 1.0 is available and we can't cast it to Power HAL 1.3,
-    // it means Power HAL 1.3 is not available at all, so we should stop trying.
-    if (sHasPowerHal_1_3 && sPowerHal_1_3 == nullptr) {
+    if (mSendUpdateImminent.load()) {
+        std::lock_guard lock(mPowerHalMutex);
+        HalWrapper* const halWrapper = getPowerHal();
+        if (halWrapper == nullptr) {
+            return;
+        }
+
+        if (!halWrapper->notifyDisplayUpdateImminent()) {
+            // The HAL has become unavailable; attempt to reconnect later
+            mReconnectPowerHal = true;
+            return;
+        }
+    }
+
+    if (mUseUpdateImminentTimer) {
+        mUpdateImminentTimer.reset();
+    }
+}
+
+class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
+public:
+    HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {}
+
+    ~HidlPowerHalWrapper() override = default;
+
+    static std::unique_ptr<HalWrapper> connect() {
+        // Power HAL 1.3 is not guaranteed to be available, thus we need to query
+        // Power HAL 1.0 first and try to cast it to Power HAL 1.3.
+        sp<V1_3::IPower> powerHal = nullptr;
         sp<V1_0::IPower> powerHal_1_0 = V1_0::IPower::getService();
         if (powerHal_1_0 != nullptr) {
             // Try to cast to Power HAL 1.3
-            sPowerHal_1_3 =  V1_3::IPower::castFrom(powerHal_1_0);
-            if (sPowerHal_1_3 == nullptr) {
-                ALOGW("No Power HAL 1.3 service in system");
-                sHasPowerHal_1_3 = false;
+            powerHal = V1_3::IPower::castFrom(powerHal_1_0);
+            if (powerHal == nullptr) {
+                ALOGW("No Power HAL 1.3 service in system, disabling PowerAdvisor");
             } else {
                 ALOGI("Loaded Power HAL 1.3 service");
             }
+        } else {
+            ALOGW("No Power HAL found, disabling PowerAdvisor");
+        }
+
+        if (powerHal == nullptr) {
+            return nullptr;
+        }
+
+        return std::make_unique<HidlPowerHalWrapper>(std::move(powerHal));
+    }
+
+    bool setExpensiveRendering(bool enabled) override {
+        ALOGV("HIDL setExpensiveRendering %s", enabled ? "T" : "F");
+        auto ret = mPowerHal->powerHintAsync_1_3(PowerHint::EXPENSIVE_RENDERING, enabled);
+        return ret.isOk();
+    }
+
+    bool notifyDisplayUpdateImminent() override {
+        // Power HAL 1.x doesn't have a notification for this
+        ALOGV("HIDL notifyUpdateImminent received but can't send");
+        return true;
+    }
+
+private:
+    const sp<V1_3::IPower> mPowerHal = nullptr;
+};
+
+class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
+public:
+    AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::move(powerHal)) {
+        auto ret = mPowerHal->isModeSupported(Mode::EXPENSIVE_RENDERING, &mHasExpensiveRendering);
+        if (!ret.isOk()) {
+            mHasExpensiveRendering = false;
+        }
+
+        ret = mPowerHal->isBoostSupported(Boost::DISPLAY_UPDATE_IMMINENT,
+                                          &mHasDisplayUpdateImminent);
+        if (!ret.isOk()) {
+            mHasDisplayUpdateImminent = false;
         }
     }
-    return sPowerHal_1_3;
+
+    ~AidlPowerHalWrapper() override = default;
+
+    static std::unique_ptr<HalWrapper> connect() {
+        // This only waits if the service is actually declared
+        sp<IPower> powerHal = waitForVintfService<IPower>();
+        if (powerHal == nullptr) {
+            return nullptr;
+        }
+        ALOGI("Loaded AIDL Power HAL service");
+
+        return std::make_unique<AidlPowerHalWrapper>(std::move(powerHal));
+    }
+
+    bool setExpensiveRendering(bool enabled) override {
+        ALOGV("AIDL setExpensiveRendering %s", enabled ? "T" : "F");
+        if (!mHasExpensiveRendering) {
+            ALOGV("Skipped sending EXPENSIVE_RENDERING because HAL doesn't support it");
+            return true;
+        }
+
+        auto ret = mPowerHal->setMode(Mode::EXPENSIVE_RENDERING, enabled);
+        return ret.isOk();
+    }
+
+    bool notifyDisplayUpdateImminent() override {
+        ALOGV("AIDL notifyDisplayUpdateImminent");
+        if (!mHasDisplayUpdateImminent) {
+            ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
+            return true;
+        }
+
+        auto ret = mPowerHal->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0);
+        return ret.isOk();
+    }
+
+private:
+    const sp<IPower> mPowerHal = nullptr;
+    bool mHasExpensiveRendering = false;
+    bool mHasDisplayUpdateImminent = false;
+};
+
+PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
+    static std::unique_ptr<HalWrapper> sHalWrapper = nullptr;
+    static bool sHasHal = true;
+
+    if (!sHasHal) {
+        return nullptr;
+    }
+
+    // If we used to have a HAL, but it stopped responding, attempt to reconnect
+    if (mReconnectPowerHal) {
+        sHalWrapper = nullptr;
+        mReconnectPowerHal = false;
+    }
+
+    if (sHalWrapper != nullptr) {
+        return sHalWrapper.get();
+    }
+
+    // First attempt to connect to the AIDL Power HAL
+    sHalWrapper = AidlPowerHalWrapper::connect();
+
+    // If that didn't succeed, attempt to connect to the HIDL Power HAL
+    if (sHalWrapper == nullptr) {
+        sHalWrapper = HidlPowerHalWrapper::connect();
+    }
+
+    // If we make it to this point and still don't have a HAL, it's unlikely we
+    // will, so stop trying
+    if (sHalWrapper == nullptr) {
+        sHasHal = false;
+    }
+
+    return sHalWrapper.get();
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 5aa1f22..95eb0e2 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -16,17 +16,12 @@
 
 #pragma once
 
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
+#include <atomic>
 #include <unordered_set>
 
-#include <android/hardware/power/1.3/IPower.h>
-#include <utils/StrongPointer.h>
+#include <utils/Mutex.h>
 
+#include "../Scheduler/OneShotTimer.h"
 #include "DisplayIdentification.h"
 
 namespace android {
@@ -36,28 +31,45 @@
 public:
     virtual ~PowerAdvisor();
 
+    virtual void onBootFinished() = 0;
     virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
+    virtual void notifyDisplayUpdateImminent() = 0;
 };
 
 namespace impl {
 
-namespace V1_3 = android::hardware::power::V1_3;
-
 // PowerAdvisor is a wrapper around IPower HAL which takes into account the
 // full state of the system when sending out power hints to things like the GPU.
 class PowerAdvisor final : public Hwc2::PowerAdvisor {
 public:
+    class HalWrapper {
+    public:
+        virtual ~HalWrapper() = default;
+
+        virtual bool setExpensiveRendering(bool enabled) = 0;
+        virtual bool notifyDisplayUpdateImminent() = 0;
+    };
+
     PowerAdvisor();
     ~PowerAdvisor() override;
 
+    void onBootFinished() override;
     void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
+    void notifyDisplayUpdateImminent() override;
 
 private:
-    sp<V1_3::IPower> getPowerHal();
+    HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
+    bool mReconnectPowerHal GUARDED_BY(mPowerHalMutex) = false;
+    std::mutex mPowerHalMutex;
+
+    std::atomic_bool mBootFinished = false;
 
     std::unordered_set<DisplayId> mExpensiveDisplays;
     bool mNotifiedExpensiveRendering = false;
-    bool mReconnectPowerHal = false;
+
+    const bool mUseUpdateImminentTimer;
+    std::atomic_bool mSendUpdateImminent = true;
+    scheduler::OneShotTimer mUpdateImminentTimer;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 4e0e4df..fba3261 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 #include "VirtualDisplaySurface.h"
 
@@ -42,13 +46,14 @@
     switch (type) {
         case compositionengine::DisplaySurface::COMPOSITION_UNKNOWN:
             return "UNKNOWN";
-        case compositionengine::DisplaySurface::COMPOSITION_GLES:
-            return "GLES";
+        case compositionengine::DisplaySurface::COMPOSITION_GPU:
+            return "GPU";
         case compositionengine::DisplaySurface::COMPOSITION_HWC:
             return "HWC";
         case compositionengine::DisplaySurface::COMPOSITION_MIXED:
             return "MIXED";
-        default:                                  return "<INVALID>";
+        default:
+            return "<INVALID>";
     }
 }
 
@@ -92,7 +97,7 @@
     mSinkBufferHeight = sinkHeight;
 
     // Pick the buffer format to request from the sink when not rendering to it
-    // with GLES. If the consumer needs CPU access, use the default format
+    // with GPU. If the consumer needs CPU access, use the default format
     // set by the consumer. Otherwise allow gralloc to decide the format based
     // on usage bits.
     int sinkUsage;
@@ -143,10 +148,10 @@
     mDbgState = DBG_STATE_PREPARED;
 
     mCompositionType = compositionType;
-    if (mForceHwcCopy && mCompositionType == COMPOSITION_GLES) {
+    if (mForceHwcCopy && mCompositionType == COMPOSITION_GPU) {
         // Some hardware can do RGB->YUV conversion more efficiently in hardware
         // controlled by HWC than in hardware controlled by the video encoder.
-        // Forcing GLES-composed frames to go through an extra copy by the HWC
+        // Forcing GPU-composed frames to go through an extra copy by the HWC
         // allows the format conversion to happen there, rather than passing RGB
         // directly to the consumer.
         //
@@ -161,18 +166,17 @@
         mDbgLastCompositionType = mCompositionType;
     }
 
-    if (mCompositionType != COMPOSITION_GLES &&
-            (mOutputFormat != mDefaultOutputFormat ||
-             mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
-        // We must have just switched from GLES-only to MIXED or HWC
-        // composition. Stop using the format and usage requested by the GLES
+    if (mCompositionType != COMPOSITION_GPU &&
+        (mOutputFormat != mDefaultOutputFormat || mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
+        // We must have just switched from GPU-only to MIXED or HWC
+        // composition. Stop using the format and usage requested by the GPU
         // driver; they may be suboptimal when HWC is writing to the output
         // buffer. For example, if the output is going to a video encoder, and
         // HWC can write directly to YUV, some hardware can skip a
         // memory-to-memory RGB-to-YUV conversion step.
         //
-        // If we just switched *to* GLES-only mode, we'll change the
-        // format/usage and get a new buffer when the GLES driver calls
+        // If we just switched *to* GPU-only mode, we'll change the
+        // format/usage and get a new buffer when the GPU driver calls
         // dequeueBuffer().
         mOutputFormat = mDefaultOutputFormat;
         mOutputUsage = GRALLOC_USAGE_HW_COMPOSER;
@@ -192,17 +196,16 @@
                 "Unexpected advanceFrame() in %s state on HWC frame",
                 dbgStateStr());
     } else {
-        VDS_LOGW_IF(mDbgState != DBG_STATE_GLES_DONE,
-                "Unexpected advanceFrame() in %s state on GLES/MIXED frame",
-                dbgStateStr());
+        VDS_LOGW_IF(mDbgState != DBG_STATE_GPU_DONE,
+                    "Unexpected advanceFrame() in %s state on GPU/MIXED frame", dbgStateStr());
     }
     mDbgState = DBG_STATE_HWC;
 
     if (mOutputProducerSlot < 0 ||
             (mCompositionType != COMPOSITION_HWC && mFbProducerSlot < 0)) {
         // Last chance bailout if something bad happened earlier. For example,
-        // in a GLES configuration, if the sink disappears then dequeueBuffer
-        // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
+        // in a graphics API configuration, if the sink disappears then dequeueBuffer
+        // will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
         // will soldier on. So we end up here without a buffer. There should
         // be lots of scary messages in the log just before this.
         VDS_LOGE("advanceFrame: no buffer, bailing out");
@@ -302,9 +305,8 @@
         return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
     }
 
-    VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
-            "Unexpected requestBuffer pslot=%d in %s state",
-            pslot, dbgStateStr());
+    VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected requestBuffer pslot=%d in %s state", pslot,
+                dbgStateStr());
 
     *outBuf = mProducerBuffers[pslot];
     return NO_ERROR;
@@ -374,7 +376,7 @@
 
     VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
             "Unexpected dequeueBuffer() in %s state", dbgStateStr());
-    mDbgState = DBG_STATE_GLES;
+    mDbgState = DBG_STATE_GPU;
 
     VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#" PRIx64, w, h, format, usage);
 
@@ -385,18 +387,18 @@
 
         if (mOutputProducerSlot < 0) {
             // Last chance bailout if something bad happened earlier. For example,
-            // in a GLES configuration, if the sink disappears then dequeueBuffer
-            // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
+            // in a graphics API configuration, if the sink disappears then dequeueBuffer
+            // will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
             // will soldier on. So we end up here without a buffer. There should
             // be lots of scary messages in the log just before this.
             VDS_LOGE("dequeueBuffer: no buffer, bailing out");
             return NO_MEMORY;
         }
 
-        // We already dequeued the output buffer. If the GLES driver wants
+        // We already dequeued the output buffer. If the GPU driver wants
         // something incompatible, we have to cancel and get a new one. This
         // will mean that HWC will see a different output buffer between
-        // prepare and set, but since we're in GLES-only mode already it
+        // prepare and set, but since we're in GPU-only mode already it
         // shouldn't matter.
 
         usage |= GRALLOC_USAGE_HW_COMPOSER;
@@ -458,10 +460,9 @@
         return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
     }
 
-    VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
-            "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
-            dbgStateStr());
-    mDbgState = DBG_STATE_GLES_DONE;
+    VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
+                dbgStateStr());
+    mDbgState = DBG_STATE_GPU_DONE;
 
     VDS_LOGV("queueBuffer pslot=%d", pslot);
 
@@ -488,11 +489,11 @@
         mFbFence = mSlots[item.mSlot].mFence;
 
     } else {
-        LOG_FATAL_IF(mCompositionType != COMPOSITION_GLES,
-                "Unexpected queueBuffer in state %s for compositionType %s",
-                dbgStateStr(), dbgCompositionTypeStr(mCompositionType));
+        LOG_FATAL_IF(mCompositionType != COMPOSITION_GPU,
+                     "Unexpected queueBuffer in state %s for compositionType %s", dbgStateStr(),
+                     dbgCompositionTypeStr(mCompositionType));
 
-        // Extract the GLES release fence for HWC to acquire
+        // Extract the GPU release fence for HWC to acquire
         int64_t timestamp;
         bool isAutoTimestamp;
         android_dataspace dataSpace;
@@ -517,9 +518,8 @@
         return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
     }
 
-    VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
-            "Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
-            dbgStateStr());
+    VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
+                dbgStateStr());
     VDS_LOGV("cancelBuffer pslot=%d", pslot);
     Source source = fbSourceForCompositionType(mCompositionType);
     return mSource[source]->cancelBuffer(
@@ -641,8 +641,8 @@
         return result;
     mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot);
 
-    // On GLES-only frames, we don't have the right output buffer acquire fence
-    // until after GLES calls queueBuffer(). So here we just set the buffer
+    // On GPU-only frames, we don't have the right output buffer acquire fence
+    // until after GPU calls queueBuffer(). So here we just set the buffer
     // (for use in HWC prepare) but not the fence; we'll call this again with
     // the proper fence once we have it.
     result = mHwc.setOutputBuffer(*mDisplayId, Fence::NO_FENCE,
@@ -672,12 +672,18 @@
 
 const char* VirtualDisplaySurface::dbgStateStr() const {
     switch (mDbgState) {
-        case DBG_STATE_IDLE:      return "IDLE";
-        case DBG_STATE_PREPARED:  return "PREPARED";
-        case DBG_STATE_GLES:      return "GLES";
-        case DBG_STATE_GLES_DONE: return "GLES_DONE";
-        case DBG_STATE_HWC:       return "HWC";
-        default:                  return "INVALID";
+        case DBG_STATE_IDLE:
+            return "IDLE";
+        case DBG_STATE_PREPARED:
+            return "PREPARED";
+        case DBG_STATE_GPU:
+            return "GPU";
+        case DBG_STATE_GPU_DONE:
+            return "GPU_DONE";
+        case DBG_STATE_HWC:
+            return "HWC";
+        default:
+            return "INVALID";
     }
 }
 
@@ -692,3 +698,6 @@
 // ---------------------------------------------------------------------------
 } // namespace android
 // ---------------------------------------------------------------------------
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index d6543d1..3cbad8f 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -34,23 +34,23 @@
 class HWComposer;
 class IProducerListener;
 
-/* This DisplaySurface implementation supports virtual displays, where GLES
+/* This DisplaySurface implementation supports virtual displays, where GPU
  * and/or HWC compose into a buffer that is then passed to an arbitrary
  * consumer (the sink) running in another process.
  *
  * The simplest case is when the virtual display will never use the h/w
  * composer -- either the h/w composer doesn't support writing to buffers, or
  * there are more virtual displays than it supports simultaneously. In this
- * case, the GLES driver works directly with the output buffer queue, and
+ * case, the GPU driver works directly with the output buffer queue, and
  * calls to the VirtualDisplay from SurfaceFlinger and DisplayHardware do
  * nothing.
  *
  * If h/w composer might be used, then each frame will fall into one of three
- * configurations: GLES-only, HWC-only, and MIXED composition. In all of these,
+ * configurations: GPU-only, HWC-only, and MIXED composition. In all of these,
  * we must provide a FB target buffer and output buffer for the HWC set() call.
  *
- * In GLES-only composition, the GLES driver is given a buffer from the sink to
- * render into. When the GLES driver queues the buffer to the
+ * In GPU-only composition, the GPU driver is given a buffer from the sink to
+ * render into. When the GPU driver queues the buffer to the
  * VirtualDisplaySurface, the VirtualDisplaySurface holds onto it instead of
  * immediately queueing it to the sink. The buffer is used as both the FB
  * target and output buffer for HWC, though on these frames the HWC doesn't
@@ -65,7 +65,7 @@
  *
  * On MIXED frames, things become more complicated, since some h/w composer
  * implementations can't read from and write to the same buffer. This class has
- * an internal BufferQueue that it uses as a scratch buffer pool. The GLES
+ * an internal BufferQueue that it uses as a scratch buffer pool. The GPU
  * driver is given a scratch buffer to render into. When it finishes rendering,
  * the buffer is queued and then immediately acquired by the
  * VirtualDisplaySurface. The scratch buffer is then used as the FB target
@@ -99,7 +99,7 @@
     virtual ~VirtualDisplaySurface();
 
     //
-    // IGraphicBufferProducer interface, used by the GLES driver.
+    // IGraphicBufferProducer interface, used by the GPU driver.
     //
     virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf);
     virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
@@ -144,7 +144,7 @@
 
     // Both the sink and scratch buffer pools have their own set of slots
     // ("source slots", or "sslot"). We have to merge these into the single
-    // set of slots used by the GLES producer ("producer slots" or "pslot") and
+    // set of slots used by the graphics producer ("producer slots" or "pslot") and
     // internally in the VirtualDisplaySurface. To minimize the number of times
     // a producer slot switches which source it comes from, we map source slot
     // numbers to producer slot numbers differently for each source.
@@ -166,12 +166,12 @@
 
     // To avoid buffer reallocations, we track the buffer usage and format
     // we used on the previous frame and use it again on the new frame. If
-    // the composition type changes or the GLES driver starts requesting
+    // the composition type changes or the GPU driver starts requesting
     // different usage/format, we'll get a new buffer.
     uint32_t mOutputFormat;
     uint64_t mOutputUsage;
 
-    // Since we present a single producer interface to the GLES driver, but
+    // Since we present a single producer interface to the GPU driver, but
     // are internally muxing between the sink and scratch producers, we have
     // to keep track of which source last returned each producer slot from
     // dequeueBuffer. Each bit in mProducerSlotSource corresponds to a producer
@@ -181,7 +181,7 @@
     sp<GraphicBuffer> mProducerBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
 
     // The QueueBufferOutput with the latest info from the sink, and with the
-    // transform hint cleared. Since we defer queueBuffer from the GLES driver
+    // transform hint cleared. Since we defer queueBuffer from the GPU driver
     // to the sink, we have to return the previous version.
     // Moves instead of copies are performed to avoid duplicate
     // FrameEventHistoryDeltas.
@@ -195,7 +195,7 @@
     // Intra-frame state
     //
 
-    // Composition type and GLES buffer source for the current frame.
+    // Composition type and graphics buffer source for the current frame.
     // Valid after prepareFrame(), cleared in onFrameCommitted.
     CompositionType mCompositionType;
 
@@ -221,13 +221,13 @@
     // +-----------+-------------------+-------------+
     // | IDLE      | beginFrame        || BEGUN      |
     // | BEGUN     | prepareFrame      || PREPARED   |
-    // | PREPARED  | dequeueBuffer [1] || GLES       |
+    // | PREPARED  | dequeueBuffer [1] || GPU        |
     // | PREPARED  | advanceFrame [2]  || HWC        |
-    // | GLES      | queueBuffer       || GLES_DONE  |
-    // | GLES_DONE | advanceFrame      || HWC        |
+    // | GPU       | queueBuffer       || GPU_DONE   |
+    // | GPU_DONE  | advanceFrame      || HWC        |
     // | HWC       | onFrameCommitted  || IDLE       |
     // +-----------+-------------------++------------+
-    // [1] COMPOSITION_GLES and COMPOSITION_MIXED frames.
+    // [1] COMPOSITION_GPU and COMPOSITION_MIXED frames.
     // [2] COMPOSITION_HWC frames.
     //
     enum DbgState {
@@ -236,12 +236,12 @@
         // output buffer dequeued, framebuffer source not yet known
         DBG_STATE_BEGUN,
         // output buffer dequeued, framebuffer source known but not provided
-        // to GLES yet.
+        // to GPU yet.
         DBG_STATE_PREPARED,
-        // GLES driver has a buffer dequeued
-        DBG_STATE_GLES,
-        // GLES driver has queued the buffer, we haven't sent it to HWC yet
-        DBG_STATE_GLES_DONE,
+        // GPU driver has a buffer dequeued
+        DBG_STATE_GPU,
+        // GPU driver has queued the buffer, we haven't sent it to HWC yet
+        DBG_STATE_GPU_DONE,
         // HWC has the buffer for this frame
         DBG_STATE_HWC,
     };
diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp
new file mode 100644
index 0000000..44d4d75
--- /dev/null
+++ b/services/surfaceflinger/EffectLayer.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "EffectLayer"
+
+#include "EffectLayer.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/LayerFECompositionState.h>
+#include <renderengine/RenderEngine.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include "DisplayDevice.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+EffectLayer::EffectLayer(const LayerCreationArgs& args)
+      : Layer(args),
+        mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()} {}
+
+EffectLayer::~EffectLayer() = default;
+
+std::vector<compositionengine::LayerFE::LayerSettings> EffectLayer::prepareClientCompositionList(
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
+    std::vector<compositionengine::LayerFE::LayerSettings> results;
+    std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
+            prepareClientComposition(targetSettings);
+    // Nothing to render.
+    if (!layerSettings) {
+        return {};
+    }
+
+    std::optional<compositionengine::LayerFE::LayerSettings> shadowSettings =
+            prepareShadowClientComposition(*layerSettings, targetSettings.viewport,
+                                           targetSettings.dataspace);
+    if (shadowSettings) {
+        results.push_back(*shadowSettings);
+    }
+
+    // If fill bounds are occluded or the fill color is invalid skip the fill settings.
+    if (targetSettings.realContentIsVisible && fillsColor()) {
+        // Set color for color fill settings.
+        layerSettings->source.solidColor = getColor().rgb;
+        results.push_back(*layerSettings);
+    } else if (hasBlur()) {
+        results.push_back(*layerSettings);
+    }
+
+    return results;
+}
+
+bool EffectLayer::isVisible() const {
+    return !isHiddenByPolicy() && getAlpha() > 0.0_hf && hasSomethingToDraw();
+}
+
+bool EffectLayer::setColor(const half3& color) {
+    if (mCurrentState.color.r == color.r && mCurrentState.color.g == color.g &&
+        mCurrentState.color.b == color.b) {
+        return false;
+    }
+
+    mCurrentState.sequence++;
+    mCurrentState.color.r = color.r;
+    mCurrentState.color.g = color.g;
+    mCurrentState.color.b = color.b;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool EffectLayer::setDataspace(ui::Dataspace dataspace) {
+    if (mCurrentState.dataspace == dataspace) {
+        return false;
+    }
+
+    mCurrentState.sequence++;
+    mCurrentState.dataspace = dataspace;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+void EffectLayer::preparePerFrameCompositionState() {
+    Layer::preparePerFrameCompositionState();
+
+    auto* compositionState = editCompositionState();
+    compositionState->color = getColor();
+    compositionState->compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+}
+
+sp<compositionengine::LayerFE> EffectLayer::getCompositionEngineLayerFE() const {
+    return asLayerFE();
+}
+
+compositionengine::LayerFECompositionState* EffectLayer::editCompositionState() {
+    return mCompositionState.get();
+}
+
+const compositionengine::LayerFECompositionState* EffectLayer::getCompositionState() const {
+    return mCompositionState.get();
+}
+
+bool EffectLayer::isOpaque(const Layer::State& s) const {
+    // Consider the layer to be opaque if its opaque flag is set or its effective
+    // alpha (considering the alpha of its parents as well) is 1.0;
+    return (s.flags & layer_state_t::eLayerOpaque) != 0 || getAlpha() == 1.0_hf;
+}
+
+ui::Dataspace EffectLayer::getDataSpace() const {
+    return mDrawingState.dataspace;
+}
+
+sp<Layer> EffectLayer::createClone() {
+    sp<EffectLayer> layer = mFlinger->getFactory().createEffectLayer(
+            LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0,
+                              LayerMetadata()));
+    layer->setInitialValuesForClone(this);
+    return layer;
+}
+
+bool EffectLayer::fillsColor() const {
+    return mDrawingState.color.r >= 0.0_hf && mDrawingState.color.g >= 0.0_hf &&
+            mDrawingState.color.b >= 0.0_hf;
+}
+
+bool EffectLayer::hasBlur() const {
+    return getBackgroundBlurRadius() > 0;
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/EffectLayer.h b/services/surfaceflinger/EffectLayer.h
new file mode 100644
index 0000000..1dcb633
--- /dev/null
+++ b/services/surfaceflinger/EffectLayer.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <sys/types.h>
+
+#include <cstdint>
+
+#include "Layer.h"
+
+namespace android {
+
+// A layer that can render a combination of the following effects.
+//   * fill the bounds of the layer with a color
+//   * render a shadow cast by the bounds of the layer
+// If no effects are enabled, the layer is considered to be invisible.
+class EffectLayer : public Layer {
+public:
+    explicit EffectLayer(const LayerCreationArgs&);
+    ~EffectLayer() override;
+
+    sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
+    compositionengine::LayerFECompositionState* editCompositionState() override;
+
+    const char* getType() const override { return "EffectLayer"; }
+    bool isVisible() const override;
+
+    bool setColor(const half3& color) override;
+
+    bool setDataspace(ui::Dataspace dataspace) override;
+
+    ui::Dataspace getDataSpace() const override;
+
+    bool isOpaque(const Layer::State& s) const override;
+
+protected:
+    /*
+     * compositionengine::LayerFE overrides
+     */
+    const compositionengine::LayerFECompositionState* getCompositionState() const override;
+    void preparePerFrameCompositionState() override;
+    std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
+            compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) override;
+
+    std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
+
+    sp<Layer> createClone() override;
+
+private:
+    // Returns true if there is a valid color to fill.
+    bool fillsColor() const;
+    // Returns true if this layer has a blur value.
+    bool hasBlur() const;
+    bool hasSomethingToDraw() const { return fillsColor() || drawShadows() || hasBlur(); }
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp
index 01c9c0f..a7090c5 100644
--- a/services/surfaceflinger/Effects/Daltonizer.cpp
+++ b/services/surfaceflinger/Effects/Daltonizer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "Daltonizer.h"
 #include <math/mat4.h>
 
@@ -171,3 +175,6 @@
 }
 
 } /* namespace android */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/EventLog/EventLog.cpp b/services/surfaceflinger/EventLog/EventLog.cpp
index 365a0bd..3b60952 100644
--- a/services/surfaceflinger/EventLog/EventLog.cpp
+++ b/services/surfaceflinger/EventLog/EventLog.cpp
@@ -14,16 +14,17 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <log/log.h>
-#include <utils/String8.h>
 
 #include "EventLog.h"
 
-// ---------------------------------------------------------------------------
 namespace android {
-// ---------------------------------------------------------------------------
 
 ANDROID_SINGLETON_STATIC_INSTANCE(EventLog)
 
@@ -31,11 +32,11 @@
 EventLog::EventLog() {
 }
 
-void EventLog::doLogFrameDurations(const String8& window,
-        const int32_t* durations, size_t numDurations) {
+void EventLog::doLogFrameDurations(const std::string_view& name, const int32_t* durations,
+                                   size_t numDurations) {
     EventLog::TagBuffer buffer(LOGTAG_SF_FRAME_DUR);
     buffer.startList(1 + numDurations);
-    buffer.writeString8(window);
+    buffer.writeString(name);
     for (size_t i = 0; i < numDurations; i++) {
         buffer.writeInt32(durations[i]);
     }
@@ -43,10 +44,9 @@
     buffer.log();
 }
 
-void EventLog::logFrameDurations(const String8& window,
-        const int32_t* durations, size_t numDurations) {
-    EventLog::getInstance().doLogFrameDurations(window, durations,
-            numDurations);
+void EventLog::logFrameDurations(const std::string_view& name, const int32_t* durations,
+                                 size_t numDurations) {
+    EventLog::getInstance().doLogFrameDurations(name, durations, numDurations);
 }
 
 // ---------------------------------------------------------------------------
@@ -113,9 +113,9 @@
     mPos += needed;
 }
 
-void EventLog::TagBuffer::writeString8(const String8& value) {
+void EventLog::TagBuffer::writeString(const std::string_view& value) {
     if (mOverflow) return;
-    const int32_t stringLen = value.length();
+    const size_t stringLen = value.length();
     const size_t needed = 1 + sizeof(int32_t) + stringLen;
     if (mPos + needed > STORAGE_MAX_SIZE) {
         mOverflow = true;
@@ -123,11 +123,11 @@
     }
     mStorage[mPos + 0] = EVENT_TYPE_STRING;
     memcpy(&mStorage[mPos + 1], &stringLen, sizeof(int32_t));
-    memcpy(&mStorage[mPos + 5], value.string(), stringLen);
+    memcpy(&mStorage[mPos + 5], value.data(), stringLen);
     mPos += needed;
 }
 
-// ---------------------------------------------------------------------------
-}// namespace android
+} // namespace android
 
-// ---------------------------------------------------------------------------
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/EventLog/EventLog.h b/services/surfaceflinger/EventLog/EventLog.h
index efc5d70..ee3587e 100644
--- a/services/surfaceflinger/EventLog/EventLog.h
+++ b/services/surfaceflinger/EventLog/EventLog.h
@@ -14,24 +14,21 @@
  * limitations under the License.
  */
 
-#include <stdint.h>
+#pragma once
+
 #include <utils/Errors.h>
 #include <utils/Singleton.h>
 
-#ifndef ANDROID_SF_EVENTLOG_H
-#define ANDROID_SF_EVENTLOG_H
+#include <cstdint>
+#include <string_view>
 
-// ---------------------------------------------------------------------------
 namespace android {
-// ---------------------------------------------------------------------------
-
-class String8;
 
 class EventLog : public Singleton<EventLog> {
 
 public:
-    static void logFrameDurations(const String8& window,
-            const int32_t* durations, size_t numDurations);
+    static void logFrameDurations(const std::string_view& name, const int32_t* durations,
+                                  size_t numDurations);
 
 protected:
     EventLog();
@@ -54,18 +51,13 @@
     public:
         explicit TagBuffer(int32_t tag);
 
-        // starts list of items
         void startList(int8_t count);
-        // terminates the list
         void endList();
-        // write a 32-bit integer
-        void writeInt32(int32_t value);
-        // write a 64-bit integer
-        void writeInt64(int64_t value);
-        // write a C string
-        void writeString8(const String8& value);
 
-        // outputs the the buffer to the log
+        void writeInt32(int32_t);
+        void writeInt64(int64_t);
+        void writeString(const std::string_view&);
+
         void log();
     };
 
@@ -74,12 +66,8 @@
     EventLog& operator =(const EventLog&);
 
     enum { LOGTAG_SF_FRAME_DUR = 60100 };
-    void doLogFrameDurations(const String8& window, const int32_t* durations,
-            size_t numDurations);
+    void doLogFrameDurations(const std::string_view& name, const int32_t* durations,
+                             size_t numDurations);
 };
 
-// ---------------------------------------------------------------------------
-}// namespace android
-// ---------------------------------------------------------------------------
-
-#endif /* ANDROID_SF_EVENTLOG_H */
+} // namespace android
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
new file mode 100644
index 0000000..b986f38
--- /dev/null
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "FrameTracer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "FrameTracer.h"
+
+#include <android-base/stringprintf.h>
+#include <perfetto/trace/clock_snapshot.pbzero.h>
+
+#include <algorithm>
+#include <mutex>
+
+PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::FrameTracer::FrameTracerDataSource);
+
+namespace android {
+
+using Clock = perfetto::protos::pbzero::ClockSnapshot::Clock;
+void FrameTracer::initialize() {
+    std::call_once(mInitializationFlag, [this]() {
+        perfetto::TracingInitArgs args;
+        args.backends = perfetto::kSystemBackend;
+        perfetto::Tracing::Initialize(args);
+        registerDataSource();
+    });
+}
+
+void FrameTracer::registerDataSource() {
+    perfetto::DataSourceDescriptor dsd;
+    dsd.set_name(kFrameTracerDataSource);
+    FrameTracerDataSource::Register(dsd);
+}
+
+void FrameTracer::traceNewLayer(int32_t layerId, const std::string& layerName) {
+    FrameTracerDataSource::Trace([this, layerId, &layerName](FrameTracerDataSource::TraceContext) {
+        if (mTraceTracker.find(layerId) == mTraceTracker.end()) {
+            std::lock_guard<std::mutex> lock(mTraceMutex);
+            mTraceTracker[layerId].layerName = layerName;
+        }
+    });
+}
+
+void FrameTracer::traceTimestamp(int32_t layerId, uint64_t bufferID, uint64_t frameNumber,
+                                 nsecs_t timestamp, FrameEvent::BufferEventType type,
+                                 nsecs_t duration) {
+    FrameTracerDataSource::Trace([this, layerId, bufferID, frameNumber, timestamp, type,
+                                  duration](FrameTracerDataSource::TraceContext ctx) {
+        std::lock_guard<std::mutex> lock(mTraceMutex);
+        if (mTraceTracker.find(layerId) == mTraceTracker.end()) {
+            return;
+        }
+
+        // Handle any pending fences for this buffer.
+        tracePendingFencesLocked(ctx, layerId, bufferID);
+
+        // Complete current trace.
+        traceLocked(ctx, layerId, bufferID, frameNumber, timestamp, type, duration);
+    });
+}
+
+void FrameTracer::traceFence(int32_t layerId, uint64_t bufferID, uint64_t frameNumber,
+                             const std::shared_ptr<FenceTime>& fence,
+                             FrameEvent::BufferEventType type, nsecs_t startTime) {
+    FrameTracerDataSource::Trace([this, layerId, bufferID, frameNumber, &fence, type,
+                                  startTime](FrameTracerDataSource::TraceContext ctx) {
+        const nsecs_t signalTime = fence->getSignalTime();
+        if (signalTime != Fence::SIGNAL_TIME_INVALID) {
+            std::lock_guard<std::mutex> lock(mTraceMutex);
+            if (mTraceTracker.find(layerId) == mTraceTracker.end()) {
+                return;
+            }
+
+            // Handle any pending fences for this buffer.
+            tracePendingFencesLocked(ctx, layerId, bufferID);
+
+            if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+                traceSpanLocked(ctx, layerId, bufferID, frameNumber, type, startTime, signalTime);
+            } else {
+                mTraceTracker[layerId].pendingFences[bufferID].push_back(
+                        {.frameNumber = frameNumber,
+                         .type = type,
+                         .fence = fence,
+                         .startTime = startTime});
+            }
+        }
+    });
+}
+
+void FrameTracer::tracePendingFencesLocked(FrameTracerDataSource::TraceContext& ctx,
+                                           int32_t layerId, uint64_t bufferID) {
+    if (mTraceTracker[layerId].pendingFences.count(bufferID)) {
+        auto& pendingFences = mTraceTracker[layerId].pendingFences[bufferID];
+        for (size_t i = 0; i < pendingFences.size(); ++i) {
+            auto& pendingFence = pendingFences[i];
+
+            nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
+            if (pendingFence.fence && pendingFence.fence->isValid()) {
+                signalTime = pendingFence.fence->getSignalTime();
+                if (signalTime == Fence::SIGNAL_TIME_PENDING) {
+                    continue;
+                }
+            }
+
+            if (signalTime != Fence::SIGNAL_TIME_INVALID &&
+                systemTime() - signalTime < kFenceSignallingDeadline) {
+                traceSpanLocked(ctx, layerId, bufferID, pendingFence.frameNumber, pendingFence.type,
+                                pendingFence.startTime, signalTime);
+            }
+
+            pendingFences.erase(pendingFences.begin() + i);
+            --i;
+        }
+    }
+}
+
+void FrameTracer::traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
+                              uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
+                              FrameEvent::BufferEventType type, nsecs_t duration) {
+    auto packet = ctx.NewTracePacket();
+    packet->set_timestamp_clock_id(Clock::MONOTONIC);
+    packet->set_timestamp(timestamp);
+    auto* event = packet->set_graphics_frame_event()->set_buffer_event();
+    event->set_buffer_id(static_cast<uint32_t>(bufferID));
+    if (frameNumber != UNSPECIFIED_FRAME_NUMBER) {
+        event->set_frame_number(frameNumber);
+    }
+    event->set_type(type);
+
+    if (mTraceTracker.find(layerId) != mTraceTracker.end() &&
+        !mTraceTracker[layerId].layerName.empty()) {
+        const std::string& layerName = mTraceTracker[layerId].layerName;
+        event->set_layer_name(layerName.c_str(), layerName.size());
+    }
+
+    if (duration > 0) {
+        event->set_duration_ns(duration);
+    }
+}
+
+void FrameTracer::traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
+                                  uint64_t bufferID, uint64_t frameNumber,
+                                  FrameEvent::BufferEventType type, nsecs_t startTime,
+                                  nsecs_t endTime) {
+    nsecs_t timestamp = endTime;
+    nsecs_t duration = 0;
+    if (startTime > 0 && startTime < endTime) {
+        timestamp = startTime;
+        duration = endTime - startTime;
+    }
+    traceLocked(ctx, layerId, bufferID, frameNumber, timestamp, type, duration);
+}
+
+void FrameTracer::onDestroy(int32_t layerId) {
+    std::lock_guard<std::mutex> traceLock(mTraceMutex);
+    mTraceTracker.erase(layerId);
+}
+
+std::string FrameTracer::miniDump() {
+    std::string result = "FrameTracer miniDump:\n";
+    std::lock_guard<std::mutex> lock(mTraceMutex);
+    android::base::StringAppendF(&result, "Number of layers currently being traced is %zu\n",
+                                 mTraceTracker.size());
+    return result;
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.h b/services/surfaceflinger/FrameTracer/FrameTracer.h
new file mode 100644
index 0000000..ef5df90
--- /dev/null
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <perfetto/trace/android/graphics_frame_event.pbzero.h>
+#include <perfetto/tracing.h>
+#include <ui/FenceTime.h>
+
+#include <mutex>
+#include <unordered_map>
+
+namespace android {
+
+class FrameTracer {
+public:
+    class FrameTracerDataSource : public perfetto::DataSource<FrameTracerDataSource> {
+        virtual void OnSetup(const SetupArgs&) override{};
+        virtual void OnStart(const StartArgs&) override{};
+        virtual void OnStop(const StopArgs&) override{};
+    };
+
+    static const uint64_t UNSPECIFIED_FRAME_NUMBER = std::numeric_limits<uint64_t>::max();
+
+    using FrameEvent = perfetto::protos::pbzero::GraphicsFrameEvent;
+
+    ~FrameTracer() = default;
+
+    // Sets up the perfetto tracing backend and data source.
+    void initialize();
+    // Registers the data source with the perfetto backend. Called as part of initialize()
+    // and should not be called manually outside of tests. Public to allow for substituting a
+    // perfetto::kInProcessBackend in tests.
+    void registerDataSource();
+    // Starts tracking a new layer for tracing. Needs to be called once before traceTimestamp() or
+    // traceFence() for each layer.
+    void traceNewLayer(int32_t layerId, const std::string& layerName);
+    // Creates a trace point at the timestamp provided.
+    void traceTimestamp(int32_t layerId, uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
+                        FrameEvent::BufferEventType type, nsecs_t duration = 0);
+    // Creates a trace point after the provided fence has been signalled. If a startTime is provided
+    // the trace will have be timestamped from startTime until fence signalling time. If no
+    // startTime is provided, a durationless trace point will be created timestamped at fence
+    // signalling time. If the fence hasn't signalled yet, the trace point will be created the next
+    // time after signalling a trace call for this buffer occurs.
+    void traceFence(int32_t layerId, uint64_t bufferID, uint64_t frameNumber,
+                    const std::shared_ptr<FenceTime>& fence, FrameEvent::BufferEventType type,
+                    nsecs_t startTime = 0);
+
+    // Takes care of cleanup when a layer is destroyed.
+    void onDestroy(int32_t layerId);
+
+    std::string miniDump();
+
+    static constexpr char kFrameTracerDataSource[] = "android.surfaceflinger.frame";
+
+    // The maximum amount of time a fence has to signal before it is discarded.
+    // Used to avoid fences from previous traces generating new trace points in later ones.
+    // Public for testing.
+    static constexpr nsecs_t kFenceSignallingDeadline = 60'000'000'000; // 60 seconds
+
+private:
+    struct PendingFence {
+        uint64_t frameNumber;
+        FrameEvent::BufferEventType type;
+        std::shared_ptr<FenceTime> fence;
+        nsecs_t startTime;
+    };
+
+    struct TraceRecord {
+        std::string layerName;
+        using BufferID = uint64_t;
+        std::unordered_map<BufferID, std::vector<PendingFence>> pendingFences;
+    };
+
+    // Checks if any pending fences for a layer and buffer have signalled and, if they have, creates
+    // trace points for them.
+    void tracePendingFencesLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
+                                  uint64_t bufferID);
+    // Creates a trace point by translating a start time and an end time to a timestamp and
+    // duration. If startTime is later than end time it sets end time as the timestamp and the
+    // duration to 0. Used by traceFence().
+    void traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
+                         uint64_t bufferID, uint64_t frameNumber, FrameEvent::BufferEventType type,
+                         nsecs_t startTime, nsecs_t endTime);
+    void traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId, uint64_t bufferID,
+                     uint64_t frameNumber, nsecs_t timestamp, FrameEvent::BufferEventType type,
+                     nsecs_t duration = 0);
+
+    std::mutex mTraceMutex;
+    std::unordered_map<int32_t, TraceRecord> mTraceTracker;
+    std::once_flag mInitializationFlag;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index f4cc49b..8ad805b 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // This is needed for stdint.h to define INT64_MAX in C++
 #define __STDC_LIMIT_MACROS
 
@@ -21,7 +25,6 @@
 
 #include <android-base/stringprintf.h>
 #include <android/log.h>
-#include <utils/String8.h>
 
 #include <ui/FrameStats.h>
 
@@ -139,7 +142,7 @@
     }
 }
 
-void FrameTracker::logAndResetStats(const String8& name) {
+void FrameTracker::logAndResetStats(const std::string_view& name) {
     Mutex::Autolock lock(mMutex);
     logStatsLocked(name);
     resetFrameCountersLocked();
@@ -217,7 +220,7 @@
     }
 }
 
-void FrameTracker::logStatsLocked(const String8& name) const {
+void FrameTracker::logStatsLocked(const std::string_view& name) const {
     for (int i = 0; i < NUM_FRAME_BUCKETS; i++) {
         if (mNumFrames[i] > 0) {
             EventLog::logFrameDurations(name, mNumFrames, NUM_FRAME_BUCKETS);
@@ -247,3 +250,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index 555dcc1..35382be 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -14,21 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_FRAMETRACKER_H
-#define ANDROID_FRAMETRACKER_H
+#pragma once
 
 #include <ui/FenceTime.h>
-
-#include <stddef.h>
-
 #include <utils/Mutex.h>
-#include <utils/Timers.h>
 #include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <cstddef>
+#include <string_view>
 
 namespace android {
 
-class String8;
-
 // FrameTracker tracks information about the most recently rendered frames. It
 // uses a circular buffer of frame records, and is *NOT* thread-safe -
 // mutexing must be done at a higher level if multi-threaded access is
@@ -87,7 +84,7 @@
 
     // logAndResetStats dumps the current statistics to the binary event log
     // and then resets the accumulated statistics to their initial values.
-    void logAndResetStats(const String8& name);
+    void logAndResetStats(const std::string_view& name);
 
     // dumpStats dump appends the current frame display time history to the result string.
     void dumpStats(std::string& result) const;
@@ -123,7 +120,7 @@
     void resetFrameCountersLocked();
 
     // logStatsLocked dumps the current statistics to the binary event log.
-    void logStatsLocked(const String8& name) const;
+    void logStatsLocked(const std::string_view& name) const;
 
     // isFrameValidLocked returns true if the data for the given frame index is
     // valid and has all arrived (i.e. there are no oustanding fences).
@@ -160,6 +157,4 @@
     mutable Mutex mMutex;
 };
 
-}
-
-#endif // ANDROID_FRAMETRACKER_H
+} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3e6dded..03903f6 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "Layer"
@@ -22,11 +26,11 @@
 #include "Layer.h"
 
 #include <android-base/stringprintf.h>
+#include <android/native_window.h>
+#include <binder/IPCThreadState.h>
 #include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <cutils/compiler.h>
 #include <cutils/native_handle.h>
@@ -53,10 +57,11 @@
 #include <sstream>
 
 #include "BufferLayer.h"
-#include "ColorLayer.h"
 #include "Colorizer.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWComposer.h"
+#include "EffectLayer.h"
+#include "FrameTracer/FrameTracer.h"
 #include "LayerProtoHelper.h"
 #include "LayerRejecter.h"
 #include "MonitoredProducer.h"
@@ -76,15 +81,11 @@
         mName(args.name),
         mClientRef(args.client),
         mWindowType(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0)) {
-    mCurrentCrop.makeInvalid();
-
     uint32_t layerFlags = 0;
     if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
     if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
     if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
 
-    mTransactionName = String8("TX - ") + mName;
-
     mCurrentState.active_legacy.w = args.w;
     mCurrentState.active_legacy.h = args.h;
     mCurrentState.flags = layerFlags;
@@ -99,18 +100,31 @@
     mCurrentState.active.w = UINT32_MAX;
     mCurrentState.active.h = UINT32_MAX;
     mCurrentState.active.transform.set(0, 0);
+    mCurrentState.frameNumber = 0;
     mCurrentState.transform = 0;
     mCurrentState.transformToDisplayInverse = false;
     mCurrentState.crop.makeInvalid();
     mCurrentState.acquireFence = new Fence(-1);
     mCurrentState.dataspace = ui::Dataspace::UNKNOWN;
     mCurrentState.hdrMetadata.validTypes = 0;
-    mCurrentState.surfaceDamageRegion.clear();
+    mCurrentState.surfaceDamageRegion = Region::INVALID_REGION;
     mCurrentState.cornerRadius = 0.0f;
+    mCurrentState.backgroundBlurRadius = 0;
     mCurrentState.api = -1;
     mCurrentState.hasColorTransform = false;
     mCurrentState.colorSpaceAgnostic = false;
+    mCurrentState.frameRateSelectionPriority = PRIORITY_UNSET;
     mCurrentState.metadata = args.metadata;
+    mCurrentState.shadowRadius = 0.f;
+    mCurrentState.treeHasFrameRateVote = false;
+    mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID;
+
+    if (args.flags & ISurfaceComposerClient::eNoColorFill) {
+        // Set an invalid color so there is no color fill.
+        mCurrentState.color.r = -1.0_hf;
+        mCurrentState.color.g = -1.0_hf;
+        mCurrentState.color.b = -1.0_hf;
+    }
 
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
@@ -120,9 +134,12 @@
     mFrameEventHistory.initializeCompositorTiming(compositorTiming);
     mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval);
 
-    mSchedulerLayerHandle = mFlinger->mScheduler->registerLayer(mName.c_str(), mWindowType);
+    mCallingPid = args.callingPid;
+    mCallingUid = args.callingUid;
+}
 
-    mFlinger->onLayerCreated();
+void Layer::onFirstRef() {
+    mFlinger->onLayerFirstRef(this);
 }
 
 Layer::~Layer() {
@@ -135,6 +152,20 @@
     mFlinger->onLayerDestroyed(this);
 }
 
+LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
+                                     uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata)
+      : flinger(flinger),
+        client(std::move(client)),
+        name(std::move(name)),
+        w(w),
+        h(h),
+        flags(flags),
+        metadata(std::move(metadata)) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    callingPid = ipc->getCallingPid();
+    callingUid = ipc->getCallingUid();
+}
+
 // ---------------------------------------------------------------------------
 // callbacks
 // ---------------------------------------------------------------------------
@@ -142,7 +173,7 @@
 /*
  * onLayerDisplayed is only meaningful for BufferLayer, but, is called through
  * Layer.  So, the implementation is done in BufferLayer.  When called on a
- * ColorLayer object, it's essentially a NOP.
+ * EffectLayer object, it's essentially a NOP.
  */
 void Layer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) {}
 
@@ -198,13 +229,23 @@
     mFlinger->markLayerPendingRemovalLocked(this);
 }
 
+sp<Layer> Layer::getRootLayer() {
+    sp<Layer> parent = getParent();
+    if (parent == nullptr) {
+        return this;
+    }
+    return parent->getRootLayer();
+}
+
 void Layer::onRemovedFromCurrentState() {
-    auto layersInTree = getLayersInTree(LayerVector::StateSet::Current);
+    // Use the root layer since we want to maintain the hierarchy for the entire subtree.
+    auto layersInTree = getRootLayer()->getLayersInTree(LayerVector::StateSet::Current);
     std::sort(layersInTree.begin(), layersInTree.end());
-    for (const auto& layer : layersInTree) {
+
+    traverse(LayerVector::StateSet::Current, [&](Layer* layer) {
         layer->removeFromCurrentState();
         layer->removeRelativeZ(layersInTree);
-    }
+    });
 }
 
 void Layer::addToCurrentState() {
@@ -219,10 +260,6 @@
 // set-up
 // ---------------------------------------------------------------------------
 
-const String8& Layer::getName() const {
-    return mName;
-}
-
 bool Layer::getPremultipledAlpha() const {
     return mPremultipliedAlpha;
 }
@@ -241,37 +278,6 @@
 // h/w composer set-up
 // ---------------------------------------------------------------------------
 
-bool Layer::hasHwcLayer(const sp<const DisplayDevice>& displayDevice) {
-    auto outputLayer = findOutputLayerForDisplay(displayDevice);
-    LOG_FATAL_IF(!outputLayer);
-    return outputLayer->getState().hwc && (*outputLayer->getState().hwc).hwcLayer != nullptr;
-}
-
-HWC2::Layer* Layer::getHwcLayer(const sp<const DisplayDevice>& displayDevice) {
-    auto outputLayer = findOutputLayerForDisplay(displayDevice);
-    if (!outputLayer || !outputLayer->getState().hwc) {
-        return nullptr;
-    }
-    return (*outputLayer->getState().hwc).hwcLayer.get();
-}
-
-Rect Layer::getContentCrop() const {
-    // this is the crop rectangle that applies to the buffer
-    // itself (as opposed to the window)
-    Rect crop;
-    if (!mCurrentCrop.isEmpty()) {
-        // if the buffer crop is defined, we use that
-        crop = mCurrentCrop;
-    } else if (mActiveBuffer != nullptr) {
-        // otherwise we use the whole buffer
-        crop = mActiveBuffer->getBounds();
-    } else {
-        // if we don't have a buffer yet, we use an empty/invalid crop
-        crop.makeInvalid();
-    }
-    return crop;
-}
-
 static Rect reduce(const Rect& win, const Region& exclude) {
     if (CC_LIKELY(exclude.isEmpty())) {
         return win;
@@ -316,7 +322,7 @@
     // If the layer is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
     // it isFixedSize) then there may be additional scaling not accounted
     // for in the layer transform.
-    if (!isFixedSize() || !mActiveBuffer) {
+    if (!isFixedSize() || getBuffer() == nullptr) {
         return {};
     }
 
@@ -328,10 +334,10 @@
         return {};
     }
 
-    int bufferWidth = mActiveBuffer->getWidth();
-    int bufferHeight = mActiveBuffer->getHeight();
+    int bufferWidth = getBuffer()->getWidth();
+    int bufferHeight = getBuffer()->getHeight();
 
-    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+    if (getBufferTransform() & NATIVE_WINDOW_TRANSFORM_ROT_90) {
         std::swap(bufferWidth, bufferHeight);
     }
 
@@ -346,7 +352,7 @@
 ui::Transform Layer::getTransformWithScale(const ui::Transform& bufferScaleTransform) const {
     // We need to mirror this scaling to child surfaces or we will break the contract where WM can
     // treat child surfaces as pixels in the parent surface.
-    if (!isFixedSize() || !mActiveBuffer) {
+    if (!isFixedSize() || getBuffer() == nullptr) {
         return mEffectiveTransform;
     }
     return mEffectiveTransform * bufferScaleTransform;
@@ -355,13 +361,14 @@
 FloatRect Layer::getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const {
     // We need the pre scaled layer bounds when computing child bounds to make sure the child is
     // cropped to its parent layer after any buffer transform scaling is applied.
-    if (!isFixedSize() || !mActiveBuffer) {
+    if (!isFixedSize() || getBuffer() == nullptr) {
         return mBounds;
     }
     return bufferScaleTransform.inverse().transform(mBounds);
 }
 
-void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) {
+void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
+                          float parentShadowRadius) {
     const State& s(getDrawingState());
 
     // Calculate effective layer transform
@@ -384,11 +391,23 @@
     mBounds = bounds;
     mScreenBounds = mEffectiveTransform.transform(mBounds);
 
+    // Use the layer's own shadow radius if set. Otherwise get the radius from
+    // parent.
+    if (s.shadowRadius > 0.f) {
+        mEffectiveShadowRadius = s.shadowRadius;
+    } else {
+        mEffectiveShadowRadius = parentShadowRadius;
+    }
+
+    // Shadow radius is passed down to only one layer so if the layer can draw shadows,
+    // don't pass it to its children.
+    const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius;
+
     // Add any buffer scaling to the layer's children.
     ui::Transform bufferScaleTransform = getBufferScaleTransform();
     for (const sp<Layer>& child : mDrawingChildren) {
         child->computeBounds(getBoundsPreScaling(bufferScaleTransform),
-                             getTransformWithScale(bufferScaleTransform));
+                             getTransformWithScale(bufferScaleTransform), childShadowRadius);
     }
 }
 
@@ -413,15 +432,43 @@
     win.bottom -= roundedCornersCrop.top;
 }
 
-void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositionState) const {
+void Layer::prepareBasicGeometryCompositionState() {
     const auto& drawingState{getDrawingState()};
-    auto alpha = static_cast<float>(getAlpha());
-    auto blendMode = HWC2::BlendMode::None;
-    if (!isOpaque(drawingState) || alpha != 1.0f) {
-        blendMode =
-                mPremultipliedAlpha ? HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage;
+    const uint32_t layerStack = getLayerStack();
+    const auto alpha = static_cast<float>(getAlpha());
+    const bool opaque = isOpaque(drawingState);
+    const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+
+    auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
+    if (!opaque || alpha != 1.0f) {
+        blendMode = mPremultipliedAlpha ? Hwc2::IComposerClient::BlendMode::PREMULTIPLIED
+                                        : Hwc2::IComposerClient::BlendMode::COVERAGE;
     }
 
+    auto* compositionState = editCompositionState();
+    compositionState->layerStackId =
+            (layerStack != ~0u) ? std::make_optional(layerStack) : std::nullopt;
+    compositionState->internalOnly = getPrimaryDisplayOnly();
+    compositionState->isVisible = isVisible();
+    compositionState->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
+    compositionState->shadowRadius = mEffectiveShadowRadius;
+
+    compositionState->contentDirty = contentDirty;
+    contentDirty = false;
+
+    compositionState->geomLayerBounds = mBounds;
+    compositionState->geomLayerTransform = getTransform();
+    compositionState->geomInverseLayerTransform = compositionState->geomLayerTransform.inverse();
+    compositionState->transparentRegionHint = getActiveTransparentRegion(drawingState);
+
+    compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
+    compositionState->alpha = alpha;
+    compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+}
+
+void Layer::prepareGeometryCompositionState() {
+    const auto& drawingState{getDrawingState()};
+
     int type = drawingState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
     int appId = drawingState.metadata.getInt32(METADATA_OWNER_UID, 0);
     sp<Layer> parent = mDrawingParent.promote();
@@ -429,174 +476,274 @@
         auto& parentState = parent->getDrawingState();
         const int parentType = parentState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
         const int parentAppId = parentState.metadata.getInt32(METADATA_OWNER_UID, 0);
-        if (parentType >= 0 || parentAppId >= 0) {
+        if (parentType > 0 && parentAppId > 0) {
             type = parentType;
             appId = parentAppId;
         }
     }
 
-    compositionState.geomLayerTransform = getTransform();
-    compositionState.geomInverseLayerTransform = compositionState.geomLayerTransform.inverse();
-    compositionState.geomBufferSize = getBufferSize(drawingState);
-    compositionState.geomContentCrop = getContentCrop();
-    compositionState.geomCrop = getCrop(drawingState);
-    compositionState.geomBufferTransform = mCurrentTransform;
-    compositionState.geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
-    compositionState.geomActiveTransparentRegion = getActiveTransparentRegion(drawingState);
-    compositionState.geomLayerBounds = mBounds;
-    compositionState.geomUsesSourceCrop = usesSourceCrop();
-    compositionState.isSecure = isSecure();
+    auto* compositionState = editCompositionState();
 
-    compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
-    compositionState.alpha = alpha;
-    compositionState.type = type;
-    compositionState.appId = appId;
+    compositionState->geomBufferSize = getBufferSize(drawingState);
+    compositionState->geomContentCrop = getBufferCrop();
+    compositionState->geomCrop = getCrop(drawingState);
+    compositionState->geomBufferTransform = getBufferTransform();
+    compositionState->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
+    compositionState->geomUsesSourceCrop = usesSourceCrop();
+    compositionState->isSecure = isSecure();
+
+    compositionState->type = type;
+    compositionState->appId = appId;
+
+    compositionState->metadata.clear();
+    const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata();
+    for (const auto& [key, mandatory] : supportedMetadata) {
+        const auto& genericLayerMetadataCompatibilityMap =
+                mFlinger->getGenericLayerMetadataKeyMap();
+        auto compatIter = genericLayerMetadataCompatibilityMap.find(key);
+        if (compatIter == std::end(genericLayerMetadataCompatibilityMap)) {
+            continue;
+        }
+        const uint32_t id = compatIter->second;
+
+        auto it = drawingState.metadata.mMap.find(id);
+        if (it == std::end(drawingState.metadata.mMap)) {
+            continue;
+        }
+
+        compositionState->metadata
+                .emplace(key, compositionengine::GenericLayerMetadataEntry{mandatory, it->second});
+    }
 }
 
-void Layer::latchCompositionState(compositionengine::LayerFECompositionState& compositionState,
-                                  bool includeGeometry) const {
-    if (includeGeometry) {
-        latchGeometry(compositionState);
+void Layer::preparePerFrameCompositionState() {
+    const auto& drawingState{getDrawingState()};
+    auto* compositionState = editCompositionState();
+
+    compositionState->forceClientComposition = false;
+
+    compositionState->isColorspaceAgnostic = isColorSpaceAgnostic();
+    compositionState->dataspace = getDataSpace();
+    compositionState->colorTransform = getColorTransform();
+    compositionState->colorTransformIsIdentity = !hasColorTransform();
+    compositionState->surfaceDamage = surfaceDamageRegion;
+    compositionState->hasProtectedContent = isProtected();
+
+    const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+
+    compositionState->isOpaque =
+            isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
+
+    // Force client composition for special cases known only to the front-end.
+    if (isHdrY410() || usesRoundedCorners || drawShadows()) {
+        compositionState->forceClientComposition = true;
+    }
+}
+
+void Layer::prepareCursorCompositionState() {
+    const State& drawingState{getDrawingState()};
+    auto* compositionState = editCompositionState();
+
+    // Apply the layer's transform, followed by the display's global transform
+    // Here we're guaranteed that the layer's transform preserves rects
+    Rect win = getCroppedBufferSize(drawingState);
+    // Subtract the transparent region and snap to the bounds
+    Rect bounds = reduce(win, getActiveTransparentRegion(drawingState));
+    Rect frame(getTransform().transform(bounds));
+
+    compositionState->cursorFrame = frame;
+}
+
+sp<compositionengine::LayerFE> Layer::asLayerFE() const {
+    return const_cast<compositionengine::LayerFE*>(
+            static_cast<const compositionengine::LayerFE*>(this));
+}
+
+sp<compositionengine::LayerFE> Layer::getCompositionEngineLayerFE() const {
+    return nullptr;
+}
+
+compositionengine::LayerFECompositionState* Layer::editCompositionState() {
+    return nullptr;
+}
+
+const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
+    return nullptr;
+}
+
+bool Layer::onPreComposition(nsecs_t) {
+    return false;
+}
+
+void Layer::prepareCompositionState(compositionengine::LayerFE::StateSubset subset) {
+    using StateSubset = compositionengine::LayerFE::StateSubset;
+
+    switch (subset) {
+        case StateSubset::BasicGeometry:
+            prepareBasicGeometryCompositionState();
+            break;
+
+        case StateSubset::GeometryAndContent:
+            prepareBasicGeometryCompositionState();
+            prepareGeometryCompositionState();
+            preparePerFrameCompositionState();
+            break;
+
+        case StateSubset::Content:
+            preparePerFrameCompositionState();
+            break;
+
+        case StateSubset::Cursor:
+            prepareCursorCompositionState();
+            break;
     }
 }
 
 const char* Layer::getDebugName() const {
-    return mName.string();
-}
-
-void Layer::forceClientComposition(const sp<DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    outputLayer->editState().forceClientComposition = true;
-}
-
-bool Layer::getForceClientComposition(const sp<DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    return outputLayer->getState().forceClientComposition;
-}
-
-void Layer::updateCursorPosition(const sp<const DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-
-    if (!outputLayer->getState().hwc ||
-        (*outputLayer->getState().hwc).hwcCompositionType !=
-                Hwc2::IComposerClient::Composition::CURSOR) {
-        return;
-    }
-
-    // This gives us only the "orientation" component of the transform
-    const State& s(getDrawingState());
-
-    // Apply the layer's transform, followed by the display's global transform
-    // Here we're guaranteed that the layer's transform preserves rects
-    Rect win = getCroppedBufferSize(s);
-    // Subtract the transparent region and snap to the bounds
-    Rect bounds = reduce(win, getActiveTransparentRegion(s));
-    Rect frame(getTransform().transform(bounds));
-    frame.intersect(display->getViewport(), &frame);
-    auto& displayTransform = display->getTransform();
-    auto position = displayTransform.transform(frame);
-
-    auto error =
-            (*outputLayer->getState().hwc).hwcLayer->setCursorPosition(position.left, position.top);
-    ALOGE_IF(error != HWC2::Error::None,
-             "[%s] Failed to set cursor position "
-             "to (%d, %d): %s (%d)",
-             mName.string(), position.left, position.top, to_string(error).c_str(),
-             static_cast<int32_t>(error));
+    return mName.c_str();
 }
 
 // ---------------------------------------------------------------------------
 // drawing...
 // ---------------------------------------------------------------------------
 
-bool Layer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                               Region& clearRegion, const bool supportProtectedContent,
-                               renderengine::LayerSettings& layer) {
-    return prepareClientLayer(renderArea, clip, false, clearRegion, supportProtectedContent, layer);
-}
+std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition(
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
+    if (!getCompositionState()) {
+        return {};
+    }
 
-bool Layer::prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
-                               Region& clearRegion, const bool supportProtectedContent,
-                               renderengine::LayerSettings& layer) {
-    return prepareClientLayer(renderArea, Region(renderArea.getBounds()), useIdentityTransform,
-                              clearRegion, supportProtectedContent, layer);
-}
-
-bool Layer::prepareClientLayer(const RenderArea& /*renderArea*/, const Region& /*clip*/,
-                               bool useIdentityTransform, Region& /*clearRegion*/,
-                               const bool /*supportProtectedContent*/,
-                               renderengine::LayerSettings& layer) {
     FloatRect bounds = getBounds();
     half alpha = getAlpha();
-    layer.geometry.boundaries = bounds;
-    if (useIdentityTransform) {
-        layer.geometry.positionTransform = mat4();
+
+    compositionengine::LayerFE::LayerSettings layerSettings;
+    layerSettings.geometry.boundaries = bounds;
+    if (targetSettings.useIdentityTransform) {
+        layerSettings.geometry.positionTransform = mat4();
     } else {
-        const ui::Transform transform = getTransform();
-        mat4 m;
-        m[0][0] = transform[0][0];
-        m[0][1] = transform[0][1];
-        m[0][3] = transform[0][2];
-        m[1][0] = transform[1][0];
-        m[1][1] = transform[1][1];
-        m[1][3] = transform[1][2];
-        m[3][0] = transform[2][0];
-        m[3][1] = transform[2][1];
-        m[3][3] = transform[2][2];
-        layer.geometry.positionTransform = m;
+        layerSettings.geometry.positionTransform = getTransform().asMatrix4();
     }
 
     if (hasColorTransform()) {
-        layer.colorTransform = getColorTransform();
+        layerSettings.colorTransform = getColorTransform();
     }
 
     const auto roundedCornerState = getRoundedCornerState();
-    layer.geometry.roundedCornersRadius = roundedCornerState.radius;
-    layer.geometry.roundedCornersCrop = roundedCornerState.cropRect;
+    layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius;
+    layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect;
 
-    layer.alpha = alpha;
-    layer.sourceDataspace = mCurrentDataSpace;
-    return true;
+    layerSettings.alpha = alpha;
+    layerSettings.sourceDataspace = getDataSpace();
+    layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
+    return layerSettings;
 }
 
-void Layer::setCompositionType(const sp<const DisplayDevice>& display,
-                               Hwc2::IComposerClient::Composition type) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    LOG_FATAL_IF(!outputLayer->getState().hwc);
-    auto& compositionState = outputLayer->editState();
-
-    ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", ((*compositionState.hwc).hwcLayer)->getId(),
-          toString(type).c_str(), 1);
-    if ((*compositionState.hwc).hwcCompositionType != type) {
-        ALOGV("    actually setting");
-        (*compositionState.hwc).hwcCompositionType = type;
-
-        auto error = (*compositionState.hwc)
-                             .hwcLayer->setCompositionType(static_cast<HWC2::Composition>(type));
-        ALOGE_IF(error != HWC2::Error::None,
-                 "[%s] Failed to set "
-                 "composition type %s: %s (%d)",
-                 mName.string(), toString(type).c_str(), to_string(error).c_str(),
-                 static_cast<int32_t>(error));
+std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareShadowClientComposition(
+        const LayerFE::LayerSettings& casterLayerSettings, const Rect& displayViewport,
+        ui::Dataspace outputDataspace) {
+    renderengine::ShadowSettings shadow = getShadowSettings(displayViewport);
+    if (shadow.length <= 0.f) {
+        return {};
     }
+
+    const float casterAlpha = casterLayerSettings.alpha;
+    const bool casterIsOpaque = ((casterLayerSettings.source.buffer.buffer != nullptr) &&
+                                 casterLayerSettings.source.buffer.isOpaque);
+
+    compositionengine::LayerFE::LayerSettings shadowLayer = casterLayerSettings;
+
+    shadowLayer.shadow = shadow;
+    shadowLayer.geometry.boundaries = mBounds; // ignore transparent region
+
+    // If the casting layer is translucent, we need to fill in the shadow underneath the layer.
+    // Otherwise the generated shadow will only be shown around the casting layer.
+    shadowLayer.shadow.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
+    shadowLayer.shadow.ambientColor *= casterAlpha;
+    shadowLayer.shadow.spotColor *= casterAlpha;
+    shadowLayer.sourceDataspace = outputDataspace;
+    shadowLayer.source.buffer.buffer = nullptr;
+    shadowLayer.source.buffer.fence = nullptr;
+    shadowLayer.frameNumber = 0;
+    shadowLayer.bufferId = 0;
+
+    if (shadowLayer.shadow.ambientColor.a <= 0.f && shadowLayer.shadow.spotColor.a <= 0.f) {
+        return {};
+    }
+
+    float casterCornerRadius = shadowLayer.geometry.roundedCornersRadius;
+    const FloatRect& cornerRadiusCropRect = shadowLayer.geometry.roundedCornersCrop;
+    const FloatRect& casterRect = shadowLayer.geometry.boundaries;
+
+    // crop used to set the corner radius may be larger than the content rect. Adjust the corner
+    // radius accordingly.
+    if (casterCornerRadius > 0.f) {
+        float cropRectOffset = std::max(std::abs(cornerRadiusCropRect.top - casterRect.top),
+                                        std::abs(cornerRadiusCropRect.left - casterRect.left));
+        if (cropRectOffset > casterCornerRadius) {
+            casterCornerRadius = 0;
+        } else {
+            casterCornerRadius -= cropRectOffset;
+        }
+        shadowLayer.geometry.roundedCornersRadius = casterCornerRadius;
+    }
+
+    return shadowLayer;
 }
 
-Hwc2::IComposerClient::Composition Layer::getCompositionType(
-        const sp<const DisplayDevice>& display) const {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    return outputLayer->getState().hwc ? (*outputLayer->getState().hwc).hwcCompositionType
-                                       : Hwc2::IComposerClient::Composition::CLIENT;
+void Layer::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings,
+                                          bool blackout) const {
+    layerSettings.source.buffer.buffer = nullptr;
+    layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
+    layerSettings.disableBlending = true;
+    layerSettings.bufferId = 0;
+    layerSettings.frameNumber = 0;
+
+    // If layer is blacked out, force alpha to 1 so that we draw a black color layer.
+    layerSettings.alpha = blackout ? 1.0f : 0.0f;
 }
 
-bool Layer::getClearClientTarget(const sp<const DisplayDevice>& display) const {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    return outputLayer->getState().clearClientTarget;
+std::vector<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionList(
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
+    std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
+            prepareClientComposition(targetSettings);
+    // Nothing to render.
+    if (!layerSettings) {
+        return {};
+    }
+
+    // HWC requests to clear this layer.
+    if (targetSettings.clearContent) {
+        prepareClearClientComposition(*layerSettings, false /* blackout */);
+        return {*layerSettings};
+    }
+
+    std::optional<compositionengine::LayerFE::LayerSettings> shadowSettings =
+            prepareShadowClientComposition(*layerSettings, targetSettings.viewport,
+                                           targetSettings.dataspace);
+    // There are no shadows to render.
+    if (!shadowSettings) {
+        return {*layerSettings};
+    }
+
+    // If the layer casts a shadow but the content casting the shadow is occluded, skip
+    // composing the non-shadow content and only draw the shadows.
+    if (targetSettings.realContentIsVisible) {
+        return {*shadowSettings, *layerSettings};
+    }
+
+    return {*shadowSettings};
+}
+
+Hwc2::IComposerClient::Composition Layer::getCompositionType(const DisplayDevice& display) const {
+    const auto outputLayer = findOutputLayerForDisplay(&display);
+    if (outputLayer == nullptr) {
+        return Hwc2::IComposerClient::Composition::INVALID;
+    }
+    if (outputLayer->getState().hwc) {
+        return (*outputLayer->getState().hwc).hwcCompositionType;
+    } else {
+        return Hwc2::IComposerClient::Composition::CLIENT;
+    }
 }
 
 bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) {
@@ -618,58 +765,11 @@
 // local state
 // ----------------------------------------------------------------------------
 
-void Layer::computeGeometry(const RenderArea& renderArea,
-                            renderengine::Mesh& mesh,
-                            bool useIdentityTransform) const {
-    const ui::Transform renderAreaTransform(renderArea.getTransform());
-    FloatRect win = getBounds();
-
-    vec2 lt = vec2(win.left, win.top);
-    vec2 lb = vec2(win.left, win.bottom);
-    vec2 rb = vec2(win.right, win.bottom);
-    vec2 rt = vec2(win.right, win.top);
-
-    ui::Transform layerTransform = getTransform();
-    if (!useIdentityTransform) {
-        lt = layerTransform.transform(lt);
-        lb = layerTransform.transform(lb);
-        rb = layerTransform.transform(rb);
-        rt = layerTransform.transform(rt);
-    }
-
-    renderengine::Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
-    position[0] = renderAreaTransform.transform(lt);
-    position[1] = renderAreaTransform.transform(lb);
-    position[2] = renderAreaTransform.transform(rb);
-    position[3] = renderAreaTransform.transform(rt);
-}
-
 bool Layer::isSecure() const {
     const State& s(mDrawingState);
     return (s.flags & layer_state_t::eLayerSecure);
 }
 
-void Layer::setVisibleRegion(const Region& visibleRegion) {
-    // always called from main thread
-    this->visibleRegion = visibleRegion;
-}
-
-void Layer::setCoveredRegion(const Region& coveredRegion) {
-    // always called from main thread
-    this->coveredRegion = coveredRegion;
-}
-
-void Layer::setVisibleNonTransparentRegion(const Region& setVisibleNonTransparentRegion) {
-    // always called from main thread
-    this->visibleNonTransparentRegion = setVisibleNonTransparentRegion;
-}
-
-void Layer::clearVisibilityRegions() {
-    visibleRegion.clear();
-    visibleNonTransparentRegion.clear();
-    coveredRegion.clear();
-}
-
 // ----------------------------------------------------------------------------
 // transaction
 // ----------------------------------------------------------------------------
@@ -687,7 +787,7 @@
     if (mCurrentState.barrierLayer_legacy != nullptr && !isRemovedFromCurrentState()) {
         sp<Layer> barrierLayer = mCurrentState.barrierLayer_legacy.promote();
         if (barrierLayer == nullptr) {
-            ALOGE("[%s] Unable to promote barrier Layer.", mName.string());
+            ALOGE("[%s] Unable to promote barrier Layer.", getDebugName());
             // If we can't promote the layer we are intended to wait on,
             // then it is expired or otherwise invalid. Allow this transaction
             // to be applied as per normal (no synchronization).
@@ -711,7 +811,7 @@
         mFlinger->setTransactionFlags(eTraversalNeeded);
     }
     mPendingStates.push_back(mCurrentState);
-    ATRACE_INT(mTransactionName.string(), mPendingStates.size());
+    ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
 }
 
 void Layer::popPendingState(State* stateToCommit) {
@@ -719,7 +819,7 @@
     *stateToCommit = mPendingStates[0];
 
     mPendingStates.removeAt(0);
-    ATRACE_INT(mTransactionName.string(), mPendingStates.size());
+    ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
 }
 
 bool Layer::applyPendingStates(State* stateToCommit) {
@@ -730,7 +830,7 @@
                 // If we don't have a sync point for this, apply it anyway. It
                 // will be visually wrong, but it should keep us from getting
                 // into too much trouble.
-                ALOGE("[%s] No local sync point found", mName.string());
+                ALOGE("[%s] No local sync point found", getDebugName());
                 popPendingState(stateToCommit);
                 stateUpdateAvailable = true;
                 continue;
@@ -738,7 +838,7 @@
 
             if (mRemoteSyncPoints.front()->getFrameNumber() !=
                 mPendingStates[0].frameNumber_legacy) {
-                ALOGE("[%s] Unexpected sync point frame number found", mName.string());
+                ALOGE("[%s] Unexpected sync point frame number found", getDebugName());
 
                 // Signal our end of the sync point and then dispose of it
                 mRemoteSyncPoints.front()->setTransactionApplied();
@@ -765,11 +865,13 @@
         }
     }
 
-    // If we still have pending updates, wake SurfaceFlinger back up and point
-    // it at this layer so we can process them
+    // If we still have pending updates, we need to ensure SurfaceFlinger
+    // will keep calling doTransaction, and so we force a traversal.
+    // However, our pending states won't clear until a frame is available,
+    // and so there is no need to specifically trigger a wakeup.
     if (!mPendingStates.empty()) {
         setTransactionFlags(eTransactionNeeded);
-        mFlinger->setTransactionFlags(eTraversalNeeded);
+        mFlinger->setTraversalNeeded();
     }
 
     mCurrentState.modified = false;
@@ -790,7 +892,7 @@
                  "            requested={ wh={%4u,%4u} }}\n"
                  "  drawing={ active   ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }\n"
                  "            requested={ wh={%4u,%4u} }}\n",
-                 this, getName().string(), mCurrentTransform, getEffectiveScalingMode(),
+                 this, getName().c_str(), getBufferTransform(), getEffectiveScalingMode(),
                  stateToCommit->active_legacy.w, stateToCommit->active_legacy.h,
                  stateToCommit->crop_legacy.left, stateToCommit->crop_legacy.top,
                  stateToCommit->crop_legacy.right, stateToCommit->crop_legacy.bottom,
@@ -822,7 +924,7 @@
     const bool resizePending =
             ((stateToCommit->requested_legacy.w != stateToCommit->active_legacy.w) ||
              (stateToCommit->requested_legacy.h != stateToCommit->active_legacy.h)) &&
-            (mActiveBuffer != nullptr);
+            (getBuffer() != nullptr);
     if (!isFixedSize()) {
         if (resizePending && mSidebandStream == nullptr) {
             flags |= eDontUpdateGeometryState;
@@ -835,11 +937,6 @@
     if (!(flags & eDontUpdateGeometryState)) {
         State& editCurrentState(getCurrentState());
 
-        // If mFreezeGeometryUpdates is true we are in the setGeometryAppliesWithResize
-        // mode, which causes attributes which normally latch regardless of scaling mode,
-        // to be delayed. We copy the requested state to the active state making sure
-        // to respect these rules (again see Layer.h for a detailed discussion).
-        //
         // There is an awkward asymmetry in the handling of the crop states in the position
         // states, as can be seen below. Largely this arises from position and transform
         // being stored in the same data structure while having different latching rules.
@@ -847,16 +944,8 @@
         //
         // Careful that "stateToCommit" and editCurrentState may not begin as equivalent due to
         // applyPendingStates in the presence of deferred transactions.
-        if (mFreezeGeometryUpdates) {
-            float tx = stateToCommit->active_legacy.transform.tx();
-            float ty = stateToCommit->active_legacy.transform.ty();
-            stateToCommit->active_legacy = stateToCommit->requested_legacy;
-            stateToCommit->active_legacy.transform.set(tx, ty);
-            editCurrentState.active_legacy = stateToCommit->active_legacy;
-        } else {
-            editCurrentState.active_legacy = editCurrentState.requested_legacy;
-            stateToCommit->active_legacy = stateToCommit->requested_legacy;
-        }
+        editCurrentState.active_legacy = editCurrentState.requested_legacy;
+        stateToCommit->active_legacy = stateToCommit->requested_legacy;
     }
 
     return flags;
@@ -866,6 +955,15 @@
     ATRACE_CALL();
 
     if (mLayerDetached) {
+        // Ensure BLAST buffer callbacks are processed.
+        // detachChildren and mLayerDetached were implemented to avoid geometry updates
+        // to layers in the cases of animation. For BufferQueue layers buffers are still
+        // consumed as normal. This is useful as otherwise the client could get hung
+        // inevitably waiting on a buffer to return. We recreate this semantic for BufferQueue
+        // even though it is a little consistent. detachChildren is shortly slated for removal
+        // by the hierarchy mirroring work so we don't need to worry about it too much.
+        forceSendCallbacks();
+        mCurrentState.callbackHandles = {};
         return flags;
     }
 
@@ -908,6 +1006,7 @@
     commitTransaction(c);
     mPendingStatesSnapshot = mPendingStates;
     mCurrentState.callbackHandles = {};
+
     return flags;
 }
 
@@ -923,7 +1022,7 @@
     return mTransactionFlags.fetch_or(flags);
 }
 
-bool Layer::setPosition(float x, float y, bool immediate) {
+bool Layer::setPosition(float x, float y) {
     if (mCurrentState.requested_legacy.transform.tx() == x &&
         mCurrentState.requested_legacy.transform.ty() == y)
         return false;
@@ -933,14 +1032,11 @@
     // we want to apply the position portion of the transform matrix immediately,
     // but still delay scaling when resizing a SCALING_MODE_FREEZE layer.
     mCurrentState.requested_legacy.transform.set(x, y);
-    if (immediate && !mFreezeGeometryUpdates) {
-        // Here we directly update the active state
-        // unlike other setters, because we store it within
-        // the transform, but use different latching rules.
-        // b/38182305
-        mCurrentState.active_legacy.transform.set(x, y);
-    }
-    mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate;
+    // Here we directly update the active state
+    // unlike other setters, because we store it within
+    // the transform, but use different latching rules.
+    // b/38182305
+    mCurrentState.active_legacy.transform.set(x, y);
 
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
@@ -1010,6 +1106,8 @@
     mCurrentState.zOrderRelativeOf = relativeOf;
     mCurrentState.sequence++;
     mCurrentState.modified = true;
+    mCurrentState.isRelativeOf = relativeOf != nullptr;
+
     setTransactionFlags(eTransactionNeeded);
 }
 
@@ -1076,10 +1174,11 @@
 
     if (!mCurrentState.bgColorLayer && alpha != 0) {
         // create background color layer if one does not yet exist
-        uint32_t flags = ISurfaceComposerClient::eFXSurfaceColor;
-        const String8& name = mName + "BackgroundColorLayer";
-        mCurrentState.bgColorLayer = new ColorLayer(
-                LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags, LayerMetadata()));
+        uint32_t flags = ISurfaceComposerClient::eFXSurfaceEffect;
+        std::string name = mName + "BackgroundColorLayer";
+        mCurrentState.bgColorLayer = mFlinger->getFactory().createEffectLayer(
+                LayerCreationArgs(mFlinger.get(), nullptr, std::move(name), 0, 0, flags,
+                                  LayerMetadata()));
 
         // add to child list
         addChild(mCurrentState.bgColorLayer);
@@ -1113,6 +1212,16 @@
     return true;
 }
 
+bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) {
+    if (mCurrentState.backgroundBlurRadius == backgroundBlurRadius) return false;
+
+    mCurrentState.sequence++;
+    mCurrentState.backgroundBlurRadius = backgroundBlurRadius;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix,
         bool allowNonRectPreservingTransforms) {
     ui::Transform t;
@@ -1136,6 +1245,7 @@
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
+
 bool Layer::setFlags(uint8_t flags, uint8_t mask) {
     const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask);
     if (mCurrentState.flags == newFlags) return false;
@@ -1146,14 +1256,11 @@
     return true;
 }
 
-bool Layer::setCrop_legacy(const Rect& crop, bool immediate) {
+bool Layer::setCrop_legacy(const Rect& crop) {
     if (mCurrentState.requestedCrop_legacy == crop) return false;
     mCurrentState.sequence++;
     mCurrentState.requestedCrop_legacy = crop;
-    if (immediate && !mFreezeGeometryUpdates) {
-        mCurrentState.crop_legacy = crop;
-    }
-    mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate;
+    mCurrentState.crop_legacy = crop;
 
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
@@ -1195,6 +1302,33 @@
     return true;
 }
 
+bool Layer::setFrameRateSelectionPriority(int32_t priority) {
+    if (mCurrentState.frameRateSelectionPriority == priority) return false;
+    mCurrentState.frameRateSelectionPriority = priority;
+    mCurrentState.sequence++;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+int32_t Layer::getFrameRateSelectionPriority() const {
+    // Check if layer has priority set.
+    if (mDrawingState.frameRateSelectionPriority != PRIORITY_UNSET) {
+        return mDrawingState.frameRateSelectionPriority;
+    }
+    // If not, search whether its parents have it set.
+    sp<Layer> parent = getParent();
+    if (parent != nullptr) {
+        return parent->getFrameRateSelectionPriority();
+    }
+
+    return Layer::PRIORITY_UNSET;
+}
+
+bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) {
+    return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
+};
+
 uint32_t Layer::getLayerStack() const {
     auto p = mDrawingParent.promote();
     if (p == nullptr) {
@@ -1203,6 +1337,112 @@
     return p->getLayerStack();
 }
 
+bool Layer::setShadowRadius(float shadowRadius) {
+    if (mCurrentState.shadowRadius == shadowRadius) {
+        return false;
+    }
+
+    mCurrentState.sequence++;
+    mCurrentState.shadowRadius = shadowRadius;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    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();
+        while (parent) {
+            visitor(parent.get());
+            parent = parent->getParent();
+        }
+
+        traverse(LayerVector::StateSet::Current, visitor);
+    };
+
+    // update parents and children about the vote
+    // First traverse the tree and count how many layers has votes
+    int layersWithVote = 0;
+    traverseTree([&layersWithVote](Layer* layer) {
+        const auto layerVotedWithDefaultCompatibility = layer->mCurrentState.frameRate.rate > 0 &&
+                layer->mCurrentState.frameRate.type == FrameRateCompatibility::Default;
+        const auto layerVotedWithNoVote =
+                layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote;
+
+        // We do not count layers that are ExactOrMultiple for the same reason
+        // we are allowing touch boost for those layers. See
+        // RefreshRateConfigs::getBestRefreshRate for more details.
+        if (layerVotedWithDefaultCompatibility || layerVotedWithNoVote) {
+            layersWithVote++;
+        }
+    });
+
+    // Now update the other layers
+    bool transactionNeeded = false;
+    traverseTree([layersWithVote, &transactionNeeded](Layer* layer) {
+        if (layer->mCurrentState.treeHasFrameRateVote != layersWithVote > 0) {
+            layer->mCurrentState.sequence++;
+            layer->mCurrentState.treeHasFrameRateVote = layersWithVote > 0;
+            layer->mCurrentState.modified = true;
+            layer->setTransactionFlags(eTransactionNeeded);
+            transactionNeeded = true;
+        }
+    });
+
+    if (transactionNeeded) {
+        mFlinger->setTransactionFlags(eTraversalNeeded);
+    }
+}
+
+bool Layer::setFrameRate(FrameRate frameRate) {
+    if (!mFlinger->useFrameRateApi) {
+        return false;
+    }
+    if (mCurrentState.frameRate == frameRate) {
+        return false;
+    }
+
+    // Activate the layer in Scheduler's LayerHistory
+    mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
+                                             LayerHistory::LayerUpdateType::SetFrameRate);
+
+    mCurrentState.sequence++;
+    mCurrentState.frameRate = frameRate;
+    mCurrentState.modified = true;
+
+    updateTreeHasFrameRateVote();
+
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+Layer::FrameRate Layer::getFrameRateForLayerTree() const {
+    const auto frameRate = getDrawingState().frameRate;
+    if (frameRate.rate > 0 || frameRate.type == FrameRateCompatibility::NoVote) {
+        return frameRate;
+    }
+
+    // This layer doesn't have a frame rate. If one of its ancestors or successors
+    // have a vote, return a NoVote for ancestors/successors to set the vote
+    if (getDrawingState().treeHasFrameRateVote) {
+        return {0, FrameRateCompatibility::NoVote};
+    }
+
+    return frameRate;
+}
+
 void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
     ATRACE_CALL();
     mCurrentState.barrierLayer_legacy = barrierLayer;
@@ -1255,20 +1495,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);
 }
 
 // ----------------------------------------------------------------------------
@@ -1276,15 +1508,18 @@
 // ----------------------------------------------------------------------------
 
 // 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;
     const State& ds = getDrawingState();
     info.mName = getName();
-    sp<Layer> parent = getParent();
-    info.mParentName = (parent == nullptr ? std::string("none") : parent->getName().string());
-    info.mType = std::string(getTypeId());
+    sp<Layer> parent = mDrawingParent.promote();
+    info.mParentName = parent ? parent->getName() : "none"s;
+    info.mType = getType();
     info.mTransparentRegion = ds.activeTransparentRegion_legacy;
-    info.mVisibleRegion = visibleRegion;
+
+    info.mVisibleRegion = getVisibleRegion(display);
     info.mSurfaceDamageRegion = surfaceDamageRegion;
     info.mLayerStack = getLayerStack();
     info.mX = ds.active_legacy.transform.tx();
@@ -1296,13 +1531,13 @@
     info.mColor = ds.color;
     info.mFlags = ds.flags;
     info.mPixelFormat = getPixelFormat();
-    info.mDataSpace = static_cast<android_dataspace>(mCurrentDataSpace);
+    info.mDataSpace = static_cast<android_dataspace>(getDataSpace());
     info.mMatrix[0][0] = ds.active_legacy.transform[0][0];
     info.mMatrix[0][1] = ds.active_legacy.transform[0][1];
     info.mMatrix[1][0] = ds.active_legacy.transform[1][0];
     info.mMatrix[1][1] = ds.active_legacy.transform[1][1];
     {
-        sp<const GraphicBuffer> buffer = mActiveBuffer;
+        sp<const GraphicBuffer> buffer = getBuffer();
         if (buffer != 0) {
             info.mActiveBufferWidth = buffer->getWidth();
             info.mActiveBufferHeight = buffer->getHeight();
@@ -1325,21 +1560,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) [Focused]\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;
     }
@@ -1347,18 +1598,18 @@
     std::string name;
     if (mName.length() > 77) {
         std::string shortened;
-        shortened.append(mName.string(), 36);
+        shortened.append(mName, 0, 36);
         shortened.append("[...]");
-        shortened.append(mName.string() + (mName.length() - 36), 36);
-        name = shortened;
+        shortened.append(mName, mName.length() - 36);
+        name = std::move(shortened);
     } else {
-        name = std::string(mName.string(), mName.size());
+        name = mName;
     }
 
     StringAppendF(&result, " %s\n", name.c_str());
 
     const State& layerState(getDrawingState());
-    const auto& compositionState = outputLayer->getState();
+    const auto& outputLayerState = outputLayer->getState();
 
     if (layerState.zOrderRelativeOf != nullptr || mDrawingParent != nullptr) {
         StringAppendF(&result, "  rel %6d | ", layerState.z);
@@ -1366,20 +1617,29 @@
         StringAppendF(&result, "  %10d | ", layerState.z);
     }
     StringAppendF(&result, "  %10d | ", mWindowType);
-    StringAppendF(&result, "%10s | ", toString(getCompositionType(displayDevice)).c_str());
-    StringAppendF(&result, "%10s | ",
-                  toString(getCompositionLayer() ? compositionState.bufferTransform
-                                                 : static_cast<Hwc2::Transform>(0))
-                          .c_str());
-    const Rect& frame = compositionState.displayFrame;
+    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 = compositionState.sourceCrop;
-    StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top, crop.right,
+    const FloatRect& crop = outputLayerState.sourceCrop;
+    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", layerState.frameRate.rate,
+                      frameRateCompatibilityString(layerState.frameRate.type).c_str());
+    } else {
+        StringAppendF(&result, "                         ");
+    }
 
-    result.append("- - - - - - - - - - - - - - - -");
-    result.append("- - - - - - - - - - - - - - - -");
-    result.append("- - - - - - - - - - - - - - -\n");
+    const auto focused = isLayerFocusedBasedOnPriority(getFrameRateSelectionPriority());
+    StringAppendF(&result, "    [%s]\n", focused ? "*" : " ");
+
+    result.append("- - - - - - - - - - - - - - - - ");
+    result.append("- - - - - - - - - - - - - - - - ");
+    result.append("- - - - - - - - - - - - - - - - ");
+    result.append("- - - - - - - - - - - - - - - - ");
+    result.append("- - - - - - - -\n");
 }
 
 void Layer::dumpFrameStats(std::string& result) const {
@@ -1399,16 +1659,23 @@
 }
 
 void Layer::dumpFrameEvents(std::string& result) {
-    StringAppendF(&result, "- Layer %s (%s, %p)\n", getName().string(), getTypeId(), this);
+    StringAppendF(&result, "- Layer %s (%s, %p)\n", getName().c_str(), getType(), this);
     Mutex::Autolock lock(mFrameEventHistoryMutex);
     mFrameEventHistory.checkFencesForCompletion();
     mFrameEventHistory.dump(result);
 }
 
+void Layer::dumpCallingUidPid(std::string& result) const {
+    StringAppendF(&result, "Layer %s (%s) pid:%d uid:%d\n", getName().c_str(), getType(),
+                  mCallingPid, mCallingUid);
+}
+
 void Layer::onDisconnect() {
     Mutex::Autolock lock(mFrameEventHistoryMutex);
     mFrameEventHistory.onDisconnect();
-    mFlinger->mTimeStats->onDestroy(getSequence());
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->onDestroy(layerId);
+    mFlinger->mFrameTracer->onDestroy(layerId);
 }
 
 void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
@@ -1416,6 +1683,8 @@
     if (newTimestamps) {
         mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
                                           getName().c_str(), newTimestamps->postedTime);
+        mFlinger->mTimeStats->setAcquireFence(getSequence(), newTimestamps->frameNumber,
+                                              newTimestamps->acquireFence);
     }
 
     Mutex::Autolock lock(mFrameEventHistoryMutex);
@@ -1449,6 +1718,7 @@
 
     mCurrentChildren.add(layer);
     layer->setParent(this);
+    updateTreeHasFrameRateVote();
 }
 
 ssize_t Layer::removeChild(const sp<Layer>& layer) {
@@ -1456,7 +1726,24 @@
     setTransactionFlags(eTransactionNeeded);
 
     layer->setParent(nullptr);
-    return mCurrentChildren.remove(layer);
+    const auto removeResult = mCurrentChildren.remove(layer);
+
+    updateTreeHasFrameRateVote();
+    layer->updateTreeHasFrameRateVote();
+
+    return removeResult;
+}
+
+void Layer::reparentChildren(const sp<Layer>& newParent) {
+    if (attachChildren()) {
+        setTransactionFlags(eTransactionNeeded);
+    }
+
+    for (const sp<Layer>& child : mCurrentChildren) {
+        newParent->addChild(child);
+    }
+    mCurrentChildren.clear();
+    updateTreeHasFrameRateVote();
 }
 
 bool Layer::reparentChildren(const sp<IBinder>& newParentHandle) {
@@ -1472,13 +1759,7 @@
         return false;
     }
 
-    if (attachChildren()) {
-        setTransactionFlags(eTransactionNeeded);
-    }
-    for (const sp<Layer>& child : mCurrentChildren) {
-        newParent->addChild(child);
-    }
-    mCurrentChildren.clear();
+    reparentChildren(newParent);
 
     return true;
 }
@@ -1487,8 +1768,8 @@
     for (const sp<Layer>& child : mDrawingChildren) {
         child->mDrawingParent = newParent;
         child->computeBounds(newParent->mBounds,
-                             newParent->getTransformWithScale(
-                                     newParent->getBufferScaleTransform()));
+                             newParent->getTransformWithScale(newParent->getBufferScaleTransform()),
+                             newParent->mEffectiveShadowRadius);
     }
 }
 
@@ -1608,22 +1889,25 @@
 
 bool Layer::isLegacyDataSpace() const {
     // return true when no higher bits are set
-    return !(mCurrentDataSpace & (ui::Dataspace::STANDARD_MASK |
-                ui::Dataspace::TRANSFER_MASK | ui::Dataspace::RANGE_MASK));
+    return !(getDataSpace() &
+             (ui::Dataspace::STANDARD_MASK | ui::Dataspace::TRANSFER_MASK |
+              ui::Dataspace::RANGE_MASK));
 }
 
 void Layer::setParent(const sp<Layer>& layer) {
     mCurrentParent = layer;
 }
 
-int32_t Layer::getZ() const {
-    return mDrawingState.z;
+int32_t Layer::getZ(LayerVector::StateSet stateSet) const {
+    const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
+    const State& state = useDrawing ? mDrawingState : mCurrentState;
+    return state.z;
 }
 
 bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) const {
     const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
     const State& state = useDrawing ? mDrawingState : mCurrentState;
-    return state.zOrderRelativeOf != nullptr;
+    return state.isRelativeOf;
 }
 
 __attribute__((no_sanitize("unsigned-integer-overflow"))) LayerVector Layer::makeTraversalList(
@@ -1648,8 +1932,7 @@
     }
 
     for (const sp<Layer>& child : children) {
-        const State& childState = useDrawing ? child->mDrawingState : child->mCurrentState;
-        if (childState.zOrderRelativeOf != nullptr) {
+        if (child->usingRelativeZ(stateSet)) {
             continue;
         }
         traverse.add(child);
@@ -1678,7 +1961,7 @@
             continue;
         }
 
-        if (relative->getZ() >= 0) {
+        if (relative->getZ(stateSet) >= 0) {
             break;
         }
         relative->traverseInZOrder(stateSet, visitor);
@@ -1712,7 +1995,7 @@
             continue;
         }
 
-        if (relative->getZ() < 0) {
+        if (relative->getZ(stateSet) < 0) {
             break;
         }
         relative->traverseInReverseZOrder(stateSet, visitor);
@@ -1729,6 +2012,15 @@
     }
 }
 
+void Layer::traverse(LayerVector::StateSet state, const LayerVector::Visitor& visitor) {
+    visitor(this);
+    const LayerVector& children =
+            state == LayerVector::StateSet::Drawing ? mDrawingChildren : mCurrentChildren;
+    for (const sp<Layer>& child : children) {
+        child->traverse(state, visitor);
+    }
+}
+
 LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                              const std::vector<Layer*>& layersInTree) {
     LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
@@ -1770,7 +2062,7 @@
     size_t i = 0;
     for (; i < list.size(); i++) {
         const auto& relative = list[i];
-        if (relative->getZ() >= 0) {
+        if (relative->getZ(stateSet) >= 0) {
             break;
         }
         relative->traverseChildrenInZOrderInner(layersInTree, stateSet, visitor);
@@ -1815,11 +2107,25 @@
     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());
 }
 
+int32_t Layer::getBackgroundBlurRadius() const {
+    return getDrawingState().backgroundBlurRadius;
+}
+
 Layer::RoundedCornerState Layer::getRoundedCornerState() const {
     const auto& p = mDrawingParent.promote();
     if (p != nullptr) {
@@ -1832,7 +2138,9 @@
             // but a transform matrix can define horizontal and vertical scales.
             // Let's take the average between both of them and pass into the shader, practically we
             // never do this type of transformation on windows anyway.
-            parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
+            auto scaleX = sqrtf(t[0][0] * t[0][0] + t[0][1] * t[0][1]);
+            auto scaleY = sqrtf(t[1][0] * t[1][0] + t[1][1] * t[1][1]);
+            parentState.radius *= (scaleX + scaleY) / 2.0f;
             return parentState;
         }
     }
@@ -1842,6 +2150,18 @@
             : RoundedCornerState();
 }
 
+renderengine::ShadowSettings Layer::getShadowSettings(const Rect& viewport) const {
+    renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings;
+
+    // Shift the spot light x-position to the middle of the display and then
+    // offset it by casting layer's screen pos.
+    state.lightPos.x = (viewport.width() / 2.f) - mScreenBounds.left;
+    state.lightPos.y -= mScreenBounds.top;
+
+    state.length = mEffectiveShadowRadius;
+    return state;
+}
+
 void Layer::commitChildList() {
     for (size_t i = 0; i < mCurrentChildren.size(); i++) {
         const auto& child = mCurrentChildren[i];
@@ -1874,7 +2194,29 @@
     setTransactionFlags(eTransactionNeeded);
 }
 
-void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags) const {
+LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags,
+                                const DisplayDevice* display) const {
+    LayerProto* layerProto = layersProto.add_layers();
+    writeToProtoDrawingState(layerProto, traceFlags, display);
+    writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
+
+    if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
+        // Only populate for the primary display.
+        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, display);
+    }
+
+    return layerProto;
+}
+
+void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
+                                     const DisplayDevice* display) const {
     ui::Transform transform = getTransform();
 
     if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
@@ -1887,17 +2229,16 @@
             }
         }
 
-        auto buffer = mActiveBuffer;
+        auto buffer = getBuffer();
         if (buffer != nullptr) {
             LayerProtoHelper::writeToProto(buffer,
                                            [&]() { return layerInfo->mutable_active_buffer(); });
-            LayerProtoHelper::writeToProto(ui::Transform(mCurrentTransform),
+            LayerProtoHelper::writeToProto(ui::Transform(getBufferTransform()),
                                            layerInfo->mutable_buffer_transform());
         }
         layerInfo->set_invalidate(contentDirty);
         layerInfo->set_is_protected(isProtected());
-        layerInfo->set_dataspace(
-                dataspaceDetails(static_cast<android_dataspace>(mCurrentDataSpace)));
+        layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
         layerInfo->set_queued_frames(getQueuedFrameCount());
         layerInfo->set_refresh_pending(isBufferLatched());
         layerInfo->set_curr_frame(mCurrentFrameNumber);
@@ -1908,18 +2249,26 @@
         LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
                                                [&]() { return layerInfo->mutable_position(); });
         LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
-        LayerProtoHelper::writeToProto(visibleRegion,
-                                       [&]() { return layerInfo->mutable_visible_region(); });
+        if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
+            LayerProtoHelper::writeToProto(getVisibleRegion(display),
+                                           [&]() { return layerInfo->mutable_visible_region(); });
+        }
         LayerProtoHelper::writeToProto(surfaceDamageRegion,
                                        [&]() { return layerInfo->mutable_damage_region(); });
+
+        if (hasColorTransform()) {
+            LayerProtoHelper::writeToProto(getColorTransform(),
+                                           layerInfo->mutable_color_transform());
+        }
     }
 
-    if (traceFlags & SurfaceTracing::TRACE_EXTRA) {
-        LayerProtoHelper::writeToProto(mSourceBounds,
-                                       [&]() { return layerInfo->mutable_source_bounds(); });
-        LayerProtoHelper::writeToProto(mScreenBounds,
-                                       [&]() { return layerInfo->mutable_screen_bounds(); });
-    }
+    LayerProtoHelper::writeToProto(mSourceBounds,
+                                   [&]() { return layerInfo->mutable_source_bounds(); });
+    LayerProtoHelper::writeToProto(mScreenBounds,
+                                   [&]() { return layerInfo->mutable_screen_bounds(); });
+    LayerProtoHelper::writeToProto(getRoundedCornerState().cropRect,
+                                   [&]() { return layerInfo->mutable_corner_radius_crop(); });
+    layerInfo->set_shadow_radius(mEffectiveShadowRadius);
 }
 
 void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet,
@@ -1933,7 +2282,7 @@
     if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
         layerInfo->set_id(sequence);
         layerInfo->set_name(getName().c_str());
-        layerInfo->set_type(String8(getTypeId()));
+        layerInfo->set_type(getType());
 
         for (const auto& child : children) {
             layerInfo->add_children(child->sequence);
@@ -1988,6 +2337,8 @@
         } else {
             layerInfo->set_z_order_relative_of(-1);
         }
+
+        layerInfo->set_is_relative_of(state.isRelativeOf);
     }
 
     if (traceFlags & SurfaceTracing::TRACE_INPUT) {
@@ -2003,41 +2354,23 @@
     }
 }
 
-void Layer::writeToProtoCompositionState(LayerProto* layerInfo,
-                                         const sp<DisplayDevice>& displayDevice,
-                                         uint32_t traceFlags) const {
-    auto outputLayer = findOutputLayerForDisplay(displayDevice);
-    if (!outputLayer) {
-        return;
-    }
-
-    writeToProtoDrawingState(layerInfo, traceFlags);
-    writeToProtoCommonState(layerInfo, LayerVector::StateSet::Drawing, traceFlags);
-
-    const auto& compositionState = outputLayer->getState();
-
-    const Rect& frame = compositionState.displayFrame;
-    LayerProtoHelper::writeToProto(frame, [&]() { return layerInfo->mutable_hwc_frame(); });
-
-    const FloatRect& crop = compositionState.sourceCrop;
-    LayerProtoHelper::writeToProto(crop, [&]() { return layerInfo->mutable_hwc_crop(); });
-
-    const int32_t transform =
-            getCompositionLayer() ? static_cast<int32_t>(compositionState.bufferTransform) : 0;
-    layerInfo->set_hwc_transform(transform);
-
-    const int32_t compositionType =
-            static_cast<int32_t>(compositionState.hwc ? (*compositionState.hwc).hwcCompositionType
-                                                      : Hwc2::IComposerClient::Composition::CLIENT);
-    layerInfo->set_hwc_composition_type(compositionType);
-}
-
 bool Layer::isRemovedFromCurrentState() const  {
     return mRemovedFromCurrentState;
 }
 
 InputWindowInfo Layer::fillInputInfo() {
+    if (!hasInputInfo()) {
+        mDrawingState.inputInfo.name = getName();
+        mDrawingState.inputInfo.ownerUid = mCallingUid;
+        mDrawingState.inputInfo.ownerPid = mCallingPid;
+        mDrawingState.inputInfo.inputFeatures =
+            InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL;
+        mDrawingState.inputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
+        mDrawingState.inputInfo.displayId = getLayerStack();
+    }
+
     InputWindowInfo info = mDrawingState.inputInfo;
+    info.id = sequence;
 
     if (info.displayId == ADISPLAY_ID_NONE) {
         info.displayId = getLayerStack();
@@ -2081,7 +2414,15 @@
     // Position the touchable region relative to frame screen location and restrict it to frame
     // bounds.
     info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop);
-    info.visible = canReceiveInput();
+    // For compatibility reasons we let layers which can receive input
+    // receive input before they have actually submitted a buffer. Because
+    // of this we use canReceiveInput instead of isVisible to check the
+    // policy-visibility, ignoring the buffer state. However for layers with
+    // hasInputInfo()==false we can use the real visibility state.
+    // We are just using these layers for occlusion detection in
+    // InputDispatcher, and obviously if they aren't visible they can't occlude
+    // anything.
+    info.visible = hasInputInfo() ? canReceiveInput() : isVisible();
 
     auto cropLayer = mDrawingState.touchableRegionCrop.promote();
     if (info.replaceTouchableRegionWithCrop) {
@@ -2094,15 +2435,31 @@
         info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds});
     }
 
+    // If the layer is a clone, we need to crop the input region to cloned root to prevent
+    // touches from going outside the cloned area.
+    if (isClone()) {
+        sp<Layer> clonedRoot = getClonedRoot();
+        if (clonedRoot != nullptr) {
+            Rect rect(clonedRoot->mScreenBounds);
+            info.touchableRegion = info.touchableRegion.intersect(rect);
+        }
+    }
+
     return info;
 }
 
-bool Layer::hasInput() const {
-    return mDrawingState.inputInfo.token != nullptr;
+sp<Layer> Layer::getClonedRoot() {
+    if (mClonedChild != nullptr) {
+        return this;
+    }
+    if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) {
+        return nullptr;
+    }
+    return mDrawingParent.promote()->getClonedRoot();
 }
 
-std::shared_ptr<compositionengine::Layer> Layer::getCompositionLayer() const {
-    return nullptr;
+bool Layer::hasInputInfo() const {
+    return mDrawingState.inputInfo.token != nullptr;
 }
 
 bool Layer::canReceiveInput() const {
@@ -2110,8 +2467,159 @@
 }
 
 compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
-        const sp<const DisplayDevice>& display) const {
-    return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionLayer().get());
+        const DisplayDevice* display) const {
+    if (!display) return nullptr;
+    return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionEngineLayerFE());
+}
+
+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) {
+    // copy drawing state from cloned layer
+    mDrawingState = clonedFrom->mDrawingState;
+    mClonedFrom = clonedFrom;
+}
+
+void Layer::updateMirrorInfo() {
+    if (mClonedChild == nullptr || !mClonedChild->isClonedFromAlive()) {
+        // If mClonedChild is null, there is nothing to mirror. If isClonedFromAlive returns false,
+        // it means that there is a clone, but the layer it was cloned from has been destroyed. In
+        // that case, we want to delete the reference to the clone since we want it to get
+        // destroyed. The root, this layer, will still be around since the client can continue
+        // to hold a reference, but no cloned layers will be displayed.
+        mClonedChild = nullptr;
+        return;
+    }
+
+    std::map<sp<Layer>, sp<Layer>> clonedLayersMap;
+    // If the real layer exists and is in current state, add the clone as a child of the root.
+    // There's no need to remove from drawingState when the layer is offscreen since currentState is
+    // copied to drawingState for the root layer. So the clonedChild is always removed from
+    // drawingState and then needs to be added back each traversal.
+    if (!mClonedChild->getClonedFrom()->isRemovedFromCurrentState()) {
+        addChildToDrawing(mClonedChild);
+    }
+
+    mClonedChild->updateClonedDrawingState(clonedLayersMap);
+    mClonedChild->updateClonedChildren(this, clonedLayersMap);
+    mClonedChild->updateClonedRelatives(clonedLayersMap);
+}
+
+void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
+    // If the layer the clone was cloned from is alive, copy the content of the drawingState
+    // to the clone. If the real layer is no longer alive, continue traversing the children
+    // since we may be able to pull out other children that are still alive.
+    if (isClonedFromAlive()) {
+        sp<Layer> clonedFrom = getClonedFrom();
+        mDrawingState = clonedFrom->mDrawingState;
+        clonedLayersMap.emplace(clonedFrom, this);
+    }
+
+    // The clone layer may have children in drawingState since they may have been created and
+    // added from a previous request to updateMirorInfo. This is to ensure we don't recreate clones
+    // that already exist, since we can just re-use them.
+    // The drawingChildren will not get overwritten by the currentChildren since the clones are
+    // not updated in the regular traversal. They are skipped since the root will lose the
+    // reference to them when it copies its currentChildren to drawing.
+    for (sp<Layer>& child : mDrawingChildren) {
+        child->updateClonedDrawingState(clonedLayersMap);
+    }
+}
+
+void Layer::updateClonedChildren(const sp<Layer>& mirrorRoot,
+                                 std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
+    mDrawingChildren.clear();
+
+    if (!isClonedFromAlive()) {
+        return;
+    }
+
+    sp<Layer> clonedFrom = getClonedFrom();
+    for (sp<Layer>& child : clonedFrom->mDrawingChildren) {
+        if (child == mirrorRoot) {
+            // This is to avoid cyclical mirroring.
+            continue;
+        }
+        sp<Layer> clonedChild = clonedLayersMap[child];
+        if (clonedChild == nullptr) {
+            clonedChild = child->createClone();
+            clonedLayersMap[child] = clonedChild;
+        }
+        addChildToDrawing(clonedChild);
+        clonedChild->updateClonedChildren(mirrorRoot, clonedLayersMap);
+    }
+}
+
+void Layer::updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
+    auto cropLayer = mDrawingState.touchableRegionCrop.promote();
+    if (cropLayer != nullptr) {
+        if (clonedLayersMap.count(cropLayer) == 0) {
+            // Real layer had a crop layer but it's not in the cloned hierarchy. Just set to
+            // self as crop layer to avoid going outside bounds.
+            mDrawingState.touchableRegionCrop = this;
+        } else {
+            const sp<Layer>& clonedCropLayer = clonedLayersMap.at(cropLayer);
+            mDrawingState.touchableRegionCrop = clonedCropLayer;
+        }
+    }
+    // Cloned layers shouldn't handle watch outside since their z order is not determined by
+    // WM or the client.
+    mDrawingState.inputInfo.layoutParamsFlags &= ~InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH;
+}
+
+void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
+    mDrawingState.zOrderRelativeOf = nullptr;
+    mDrawingState.zOrderRelatives.clear();
+
+    if (!isClonedFromAlive()) {
+        return;
+    }
+
+    const sp<Layer>& clonedFrom = getClonedFrom();
+    for (wp<Layer>& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) {
+        const sp<Layer>& relative = relativeWeak.promote();
+        if (clonedLayersMap.count(relative) > 0) {
+            auto& clonedRelative = clonedLayersMap.at(relative);
+            mDrawingState.zOrderRelatives.add(clonedRelative);
+        }
+    }
+
+    // Check if the relativeLayer for the real layer is part of the cloned hierarchy.
+    // It's possible that the layer it's relative to is outside the requested cloned hierarchy.
+    // In that case, we treat the layer as if the relativeOf has been removed. This way, it will
+    // still traverse the children, but the layer with the missing relativeOf will not be shown
+    // on screen.
+    const sp<Layer>& relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote();
+    if (clonedLayersMap.count(relativeOf) > 0) {
+        const sp<Layer>& clonedRelativeOf = clonedLayersMap.at(relativeOf);
+        mDrawingState.zOrderRelativeOf = clonedRelativeOf;
+    }
+
+    updateClonedInputInfo(clonedLayersMap);
+
+    for (sp<Layer>& child : mDrawingChildren) {
+        child->updateClonedRelatives(clonedLayersMap);
+    }
+}
+
+void Layer::addChildToDrawing(const sp<Layer>& layer) {
+    mDrawingChildren.add(layer);
+    layer->mDrawingParent = this;
+}
+
+Layer::FrameRateCompatibility Layer::FrameRate::convertCompatibility(int8_t compatibility) {
+    switch (compatibility) {
+        case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT:
+            return FrameRateCompatibility::Default;
+        case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE:
+            return FrameRateCompatibility::ExactOrMultiple;
+        default:
+            LOG_ALWAYS_FATAL("Invalid frame rate compatibility value %d", compatibility);
+            return FrameRateCompatibility::Default;
+    }
 }
 
 // ---------------------------------------------------------------------------
@@ -2125,3 +2633,6 @@
 #if defined(__gl2_h_)
 #error "don't include gl2/gl2.h in this file"
 #endif
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 3b4d873..2c90c92 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>
@@ -34,7 +33,6 @@
 #include <ui/Region.h>
 #include <ui/Transform.h>
 #include <utils/RefBase.h>
-#include <utils/String8.h>
 #include <utils/Timers.h>
 
 #include <cstdint>
@@ -67,7 +65,6 @@
 class LayerDebugInfo;
 
 namespace compositionengine {
-class Layer;
 class OutputLayer;
 struct LayerFECompositionState;
 }
@@ -79,29 +76,37 @@
 // ---------------------------------------------------------------------------
 
 struct LayerCreationArgs {
-    LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name,
-                      uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata)
-          : flinger(flinger), client(client), name(name), w(w), h(h), flags(flags),
-            metadata(std::move(metadata)) {}
+    LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h,
+                      uint32_t flags, LayerMetadata);
 
     SurfaceFlinger* flinger;
-    const sp<Client>& client;
-    const String8& name;
+    const sp<Client> client;
+    std::string name;
     uint32_t w;
     uint32_t h;
     uint32_t flags;
     LayerMetadata metadata;
+
+    pid_t callingPid;
+    uid_t callingUid;
+    uint32_t textureName;
 };
 
-class Layer : public virtual compositionengine::LayerFE {
+class Layer : public virtual RefBase, compositionengine::LayerFE {
     static std::atomic<int32_t> sSequence;
+    // The following constants represent priority of the window. SF uses this information when
+    // deciding which window has a priority when deciding about the refresh rate of the screen.
+    // Priority 0 is considered the highest priority. -1 means that the priority is unset.
+    static constexpr int32_t PRIORITY_UNSET = -1;
+    // Windows that are in focus and voted for the preferred mode ID
+    static constexpr int32_t PRIORITY_FOCUSED_WITH_MODE = 0;
+    // // Windows that are in focus, but have not requested a specific mode ID.
+    static constexpr int32_t PRIORITY_FOCUSED_WITHOUT_MODE = 1;
+    // Windows that are not in focus, but voted for a specific mode ID.
+    static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
 
 public:
     mutable bool contentDirty{false};
-    // regions below are in window-manager space
-    Region visibleRegion;
-    Region coveredRegion;
-    Region visibleNonTransparentRegion;
     Region surfaceDamageRegion;
 
     // Layer serial number.  This gives layers an explicit ordering, so we
@@ -138,6 +143,38 @@
         float radius = 0.0f;
     };
 
+    // FrameRateCompatibility specifies how we should interpret the frame rate associated with
+    // the layer.
+    enum class FrameRateCompatibility {
+        Default, // Layer didn't specify any specific handling strategy
+
+        ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
+                         // content properly. Any other value will result in a pull down.
+
+        NoVote, // Layer doesn't have any requirements for the refresh rate and
+                // should not be considered when the display refresh rate is determined.
+    };
+
+    // Encapsulates the frame rate and compatibility of the layer. This information will be used
+    // when the display refresh rate is determined.
+    struct FrameRate {
+        float rate;
+        FrameRateCompatibility type;
+
+        FrameRate() : rate(0), type(FrameRateCompatibility::Default) {}
+        FrameRate(float rate, FrameRateCompatibility type) : rate(rate), type(type) {}
+
+        bool operator==(const FrameRate& other) const {
+            return rate == other.rate && type == other.type;
+        }
+
+        bool operator!=(const FrameRate& other) const { return !(*this == other); }
+
+        // Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
+        // Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
+        static FrameRateCompatibility convertCompatibility(int8_t compatibility);
+    };
+
     struct State {
         Geometry active_legacy;
         Geometry requested_legacy;
@@ -174,21 +211,24 @@
 
         // If non-null, a Surface this Surface's Z-order is interpreted relative to.
         wp<Layer> zOrderRelativeOf;
+        bool isRelativeOf{false};
 
         // A list of surfaces whose Z-order is interpreted relative to ours.
         SortedVector<wp<Layer>> zOrderRelatives;
 
         half4 color;
         float cornerRadius;
+        int backgroundBlurRadius;
 
         bool inputInfoChanged;
         InputWindowInfo inputInfo;
         wp<Layer> touchableRegionCrop;
 
-        // dataspace is only used by BufferStateLayer and ColorLayer
+        // dataspace is only used by BufferStateLayer and EffectLayer
         ui::Dataspace dataspace;
 
         // The fields below this point are only used by BufferStateLayer
+        uint64_t frameNumber;
         Geometry active;
 
         uint32_t transform;
@@ -217,11 +257,37 @@
         // recent callback handle.
         std::deque<sp<CallbackHandle>> callbackHandles;
         bool colorSpaceAgnostic;
+        nsecs_t desiredPresentTime = -1;
+
+        // Length of the cast shadow. If the radius is > 0, a shadow of length shadowRadius will
+        // be rendered around the layer.
+        float shadowRadius;
+
+        // Priority of the layer assigned by Window Manager.
+        int32_t frameRateSelectionPriority;
+
+        FrameRate frameRate;
+
+        // 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);
     virtual ~Layer();
 
+    void onFirstRef() override;
+
+    int getWindowType() const { return mWindowType; }
+
     void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
     bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
 
@@ -267,9 +333,9 @@
 
     // setPosition operates in parent buffer space (pre parent-transform) or display
     // space for top-level layers.
-    virtual bool setPosition(float x, float y, bool immediate);
+    virtual bool setPosition(float x, float y);
     // Buffer space
-    virtual bool setCrop_legacy(const Rect& crop, bool immediate);
+    virtual bool setCrop_legacy(const Rect& crop);
 
     // TODO(b/38182121): Could we eliminate the various latching modes by
     // using the layer hierarchy?
@@ -286,6 +352,9 @@
     // The shape of the rounded corner rectangle is specified by the crop rectangle of the layer
     // from which we inferred the rounded corner radius.
     virtual bool setCornerRadius(float cornerRadius);
+    // When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which
+    // is specified in pixels.
+    virtual bool setBackgroundBlurRadius(int backgroundBlurRadius);
     virtual bool setTransparentRegionHint(const Region& transparent);
     virtual bool setFlags(uint8_t flags, uint8_t mask);
     virtual bool setLayerStack(uint32_t layerStack);
@@ -295,7 +364,8 @@
     virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber);
     virtual bool setOverrideScalingMode(int32_t overrideScalingMode);
     virtual bool setMetadata(const LayerMetadata& data);
-    virtual bool reparentChildren(const sp<IBinder>& layer);
+    bool reparentChildren(const sp<IBinder>& newParentHandle);
+    void reparentChildren(const sp<Layer>& newParent);
     virtual void setChildrenDrawingParent(const sp<Layer>& layer);
     virtual bool reparent(const sp<IBinder>& newParentHandle);
     virtual bool detachChildren();
@@ -311,8 +381,8 @@
     virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) { return false; };
     virtual bool setCrop(const Rect& /*crop*/) { return false; };
     virtual bool setFrame(const Rect& /*frame*/) { return false; };
-    virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, nsecs_t /*postTime*/,
-                           nsecs_t /*desiredPresentTime*/,
+    virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
+                           nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
                            const client_cache_t& /*clientCacheId*/) {
         return false;
     };
@@ -326,10 +396,22 @@
             const std::vector<sp<CallbackHandle>>& /*handles*/) {
         return false;
     };
+    virtual void forceSendCallbacks() {}
+    virtual bool addFrameEvent(const sp<Fence>& /*acquireFence*/, nsecs_t /*postedTime*/,
+                               nsecs_t /*requestedPresentTime*/) {
+        return false;
+    }
     virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
     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;
+    static bool isLayerFocusedBasedOnPriority(int32_t priority);
 
-    ui::Dataspace getDataSpace() const { return mCurrentDataSpace; }
+    virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; }
 
     // Before color management is introduced, contents on Android have to be
     // desaturated in order to match what they appears like visually.
@@ -338,7 +420,8 @@
     // visually.
     bool isLegacyDataSpace() const;
 
-    virtual std::shared_ptr<compositionengine::Layer> getCompositionLayer() const;
+    virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const;
+    virtual compositionengine::LayerFECompositionState* editCompositionState();
 
     // If we have received a new buffer this frame, we will pass its surface
     // damage down to hardware composer. Otherwise, we must send a region with
@@ -357,13 +440,11 @@
         return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
     }
 
-    void computeGeometry(const RenderArea& renderArea, renderengine::Mesh& mesh,
-                         bool useIdentityTransform) const;
     FloatRect getBounds(const Region& activeTransparentRegion) const;
     FloatRect getBounds() const;
 
     // Compute bounds for the layer and cache the results.
-    void computeBounds(FloatRect parentBounds, ui::Transform parentTransform);
+    void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
 
     // Returns the buffer scale transform if a scaling mode is set.
     ui::Transform getBufferScaleTransform() const;
@@ -377,9 +458,19 @@
 
     int32_t getSequence() const { return sequence; }
 
+    // For tracing.
+    // TODO: Replace with raw buffer id from buffer metadata when that becomes available.
+    // GraphicBuffer::getId() does not provide a reliable global identifier. Since the traces
+    // creates its tracks by buffer id and has no way of associating a buffer back to the process
+    // that created it, the current implementation is only sufficient for cases where a buffer is
+    // only used within a single layer.
+    uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; }
+
     // -----------------------------------------------------------------------
     // Virtuals
-    virtual const char* getTypeId() const = 0;
+
+    // Provide unique string for each class type in the Layer hierarchy
+    virtual const char* getType() const = 0;
 
     /*
      * isOpaque - true if this surface is opaque
@@ -437,16 +528,14 @@
 
     bool isRemovedFromCurrentState() 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;
-    // Write states that are modified by the main thread. This includes drawing
-    // state as well as buffer data and composition data for layers on the specified
-    // display. This should be called in the main or tracing thread.
-    void writeToProtoCompositionState(LayerProto* layerInfo, const sp<DisplayDevice>& displayDevice,
-                                      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.
@@ -463,80 +552,95 @@
         return s.activeTransparentRegion_legacy;
     }
     virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
+    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,
+    // where as this method transforms the displayFrame to layer-stack space
+    // first. This method should be used if there is no physical display to
+    // project onto when taking screenshots, as the filtering requirements are
+    // 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 DisplayDevice*, const ui::Transform&) const {
+        return false;
+    }
+
+    // This layer is not a clone, but it's the parent to the cloned hierarchy. The
+    // variable mClonedChild represents the top layer that will be cloned so this
+    // layer will be the parent of mClonedChild.
+    // The layers in the cloned hierarchy will match the lifetime of the real layers. That is
+    // if the real layer is destroyed, then the clone layer will also be destroyed.
+    sp<Layer> mClonedChild;
+
+    virtual sp<Layer> createClone() = 0;
+    void updateMirrorInfo();
+    virtual void updateCloneBufferInfo(){};
 
 protected:
-    virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                                    bool useIdentityTransform, Region& clearRegion,
-                                    const bool supportProtectedContent,
-                                    renderengine::LayerSettings& layer);
+    sp<compositionengine::LayerFE> asLayerFE() const;
+    sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
+    bool isClone() { return mClonedFrom != nullptr; }
+    bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
+
+    virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
+
+    void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+    void updateClonedChildren(const sp<Layer>& mirrorRoot,
+                              std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+    void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+    void addChildToDrawing(const sp<Layer>& layer);
+    void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+    virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
+            compositionengine::LayerFE::ClientCompositionTargetSettings&);
+    virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareShadowClientComposition(
+            const LayerFE::LayerSettings& layerSettings, const Rect& displayViewport,
+            ui::Dataspace outputDataspace);
+    // Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
+    // the settings clears the content with a solid black fill.
+    void prepareClearClientComposition(LayerFE::LayerSettings& layerSettings, bool blackout) const;
 
 public:
     /*
      * compositionengine::LayerFE overrides
      */
-    void latchCompositionState(compositionengine::LayerFECompositionState&,
-                               bool includeGeometry) const override;
+    const compositionengine::LayerFECompositionState* getCompositionState() const override;
+    bool onPreComposition(nsecs_t) override;
+    void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
+    std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
+            compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
     void onLayerDisplayed(const sp<Fence>& releaseFence) override;
     const char* getDebugName() const override;
 
 protected:
-    void latchGeometry(compositionengine::LayerFECompositionState& outState) const;
+    void prepareBasicGeometryCompositionState();
+    void prepareGeometryCompositionState();
+    virtual void preparePerFrameCompositionState();
+    void prepareCursorCompositionState();
 
 public:
     virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
 
     virtual bool isHdrY410() const { return false; }
 
-    void forceClientComposition(const sp<DisplayDevice>& display);
-    bool getForceClientComposition(const sp<DisplayDevice>& display);
-    virtual void setPerFrameData(const sp<const DisplayDevice>& display,
-                                 const ui::Transform& transform, const Rect& viewport,
-                                 int32_t supportedPerFrameMetadata,
-                                 const ui::Dataspace targetDataspace) = 0;
-
-    // callIntoHwc exists so we can update our local state and call
-    // acceptDisplayChanges without unnecessarily updating the device's state
-    void setCompositionType(const sp<const DisplayDevice>& display,
-                            Hwc2::IComposerClient::Composition type);
-    Hwc2::IComposerClient::Composition getCompositionType(
-            const sp<const DisplayDevice>& display) const;
-    bool getClearClientTarget(const sp<const DisplayDevice>& display) const;
-    void updateCursorPosition(const sp<const DisplayDevice>& display);
-
     virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; }
-    virtual void setTransformHint(uint32_t /*orientation*/) const { }
-
-    /*
-     * called before composition.
-     * returns true if the layer has pending updates.
-     */
-    virtual bool onPreComposition(nsecs_t refreshStartTime) = 0;
 
     /*
      * called after composition.
      * returns true if the layer latched a new buffer this frame.
      */
-    virtual bool onPostComposition(const std::optional<DisplayId>& /*displayId*/,
+    virtual bool onPostComposition(const DisplayDevice*,
                                    const std::shared_ptr<FenceTime>& /*glDoneFence*/,
                                    const std::shared_ptr<FenceTime>& /*presentFence*/,
-                                   const CompositorTiming& /*compositorTiming*/) {
+                                   const CompositorTiming&) {
         return false;
     }
 
     // If a buffer was replaced this frame, release the former buffer
     virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
 
-    /*
-     * prepareClientLayer - populates a renderengine::LayerSettings to passed to
-     * RenderEngine::drawLayers. Returns true if the layer can be used, and
-     * false otherwise.
-     */
-    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, Region& clearRegion,
-                            const bool supportProtectedContent, renderengine::LayerSettings& layer);
-    bool prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
-                            Region& clearRegion, const bool supportProtectedContent,
-                            renderengine::LayerSettings& layer);
-
+    virtual void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+                                           const CompositorTiming& /*compositorTiming*/) {}
     /*
      * doTransaction - process the transaction. This is a good place to figure
      * out which attributes of the surface have changed.
@@ -544,41 +648,20 @@
     uint32_t doTransaction(uint32_t transactionFlags);
 
     /*
-     * setVisibleRegion - called to set the new visible region. This gives
-     * a chance to update the new visible region or record the fact it changed.
-     */
-    void setVisibleRegion(const Region& visibleRegion);
-
-    /*
-     * setCoveredRegion - called when the covered region changes. The covered
-     * region corresponds to any area of the surface that is covered
-     * (transparently or not) by another surface.
-     */
-    void setCoveredRegion(const Region& coveredRegion);
-
-    /*
-     * setVisibleNonTransparentRegion - called when the visible and
-     * non-transparent region changes.
-     */
-    void setVisibleNonTransparentRegion(const Region& visibleNonTransparentRegion);
-
-    /*
-     * Clear the visible, covered, and non-transparent regions.
-     */
-    void clearVisibilityRegions();
-
-    /*
      * latchBuffer - called each time the screen is redrawn and returns whether
      * the visible regions need to be recomputed (this is a fairly heavy
      * operation, so this should be set only if needed). Typically this is used
      * to figure out if the content or size of a surface has changed.
      */
-    virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/) {
-        return {};
+    virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
+                             nsecs_t /*expectedPresentTime*/) {
+        return false;
     }
 
     virtual bool isBufferLatched() const { return false; }
 
+    virtual void latchAndReleaseBuffer() {}
+
     /*
      * Remove relative z for the layer if its relative parent is not part of the
      * provided layer tree.
@@ -601,15 +684,25 @@
      */
     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
      * to the layer's size.
      */
-    Rect getContentCrop() const;
+    virtual Rect getBufferCrop() const { return Rect(); }
+
+    /*
+     * Returns the transform applied to the buffer.
+     */
+    virtual uint32_t getBufferTransform() const { return 0; }
+
+    virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
+
+    virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; }
 
     /*
      * Returns if a frame is ready
@@ -619,21 +712,17 @@
     virtual int32_t getQueuedFrameCount() const { return 0; }
 
     // -----------------------------------------------------------------------
-
-    bool hasHwcLayer(const sp<const DisplayDevice>& displayDevice);
-    HWC2::Layer* getHwcLayer(const sp<const DisplayDevice>& displayDevice);
-
     inline const State& getDrawingState() const { return mDrawingState; }
     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;
     void clearFrameStats();
     void logFrameStats();
     void getFrameStats(FrameStats* outStats) const;
@@ -655,14 +744,33 @@
     // down the hierarchy).
     half getAlpha() const;
     half4 getColor() const;
+    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.
     // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
     // ignored.
-    RoundedCornerState getRoundedCornerState() const;
+    virtual RoundedCornerState getRoundedCornerState() const;
 
+    renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const;
+
+    /**
+     * Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder
+     * which will not emit children who have relativeZOrder to another layer, this method
+     * just directly emits all children. It also emits them in no particular order.
+     * So this method is not suitable for graphical operations, as it doesn't represent
+     * the scene state, but it's also more efficient than traverseInZOrder and so useful for
+     * book-keeping.
+     */
+    void traverse(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
     void traverseInReverseZOrder(LayerVector::StateSet stateSet,
                                  const LayerVector::Visitor& visitor);
     void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
@@ -675,6 +783,15 @@
                                   const LayerVector::Visitor& visitor);
 
     size_t getChildrenCount() const;
+
+    // ONLY CALL THIS FROM THE LAYER DTOR!
+    // See b/141111965.  We need to add current children to offscreen layers in
+    // the layer dtor so as not to dangle layers.  Since the layer has not
+    // committed its transaction when the layer is destroyed, we must add
+    // current children.  This is safe in the dtor as we will no longer update
+    // the current state, but should not be called anywhere else!
+    LayerVector& getCurrentChildren() { return mCurrentChildren; }
+
     void addChild(const sp<Layer>& layer);
     // Returns index if removed, or negative value otherwise
     // for symmetry with Vector::remove
@@ -689,7 +806,7 @@
     // Copy the current list of children to the drawing state. Called by
     // SurfaceFlinger to complete a transaction.
     void commitChildList();
-    int32_t getZ() const;
+    int32_t getZ(LayerVector::StateSet stateSet) const;
     virtual void pushPendingState();
 
     /**
@@ -707,8 +824,17 @@
         return parentBounds;
     }
 
-    compositionengine::OutputLayer* findOutputLayerForDisplay(
-            const sp<const DisplayDevice>& display) 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.
+     * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
+     * bounds are constrained by its parent bounds.
+     */
+    Rect getCroppedBufferSize(const Layer::State& s) const;
+
+    bool setFrameRate(FrameRate frameRate);
+    virtual FrameRate getFrameRateForLayerTree() const;
+    static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
 
 protected:
     // constant
@@ -736,6 +862,8 @@
 
     // For unit tests
     friend class TestableSurfaceFlinger;
+    friend class RefreshRateSelectionTest;
+    friend class SetFrameRateTest;
 
     virtual void commitTransaction(const State& stateToCommit);
 
@@ -820,8 +948,8 @@
     // Creates a new handle each time, so we only expect
     // this to be called once.
     sp<IBinder> getHandle();
-    const String8& getName() const;
-    virtual void notifyAvailableFrames() {}
+    const std::string& getName() const { return mName; }
+    virtual void notifyAvailableFrames(nsecs_t /*expectedPresentTime*/) {}
     virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
     bool getPremultipledAlpha() const;
 
@@ -829,15 +957,26 @@
     void setInputInfo(const InputWindowInfo& info);
 
     InputWindowInfo fillInputInfo();
-    bool hasInput() const;
+    /**
+     * Returns whether this layer has an explicitly set input-info.
+     */
+    bool hasInputInfo() const;
+    /**
+     * Return whether this layer needs an input info. For most layer types
+     * this is only true if they explicitly set an input-info but BufferLayer
+     * overrides this so we can generate input-info for Buffered layers that don't
+     * have them (for input occlusion detection checks).
+     */
+    virtual bool needsInputInfo() const { return hasInputInfo(); }
 
 protected:
-    // -----------------------------------------------------------------------
+    compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
+
     bool usingRelativeZ(LayerVector::StateSet stateSet) const;
 
     bool mPremultipliedAlpha{true};
-    String8 mName;
-    String8 mTransactionName; // A cached version of "TX - " + mName for systraces
+    const std::string mName;
+    const std::string mTransactionName{"TX - " + mName};
 
     bool mPrimaryDisplayOnly = false;
 
@@ -864,20 +1003,13 @@
 
     // main thread
     sp<NativeHandle> mSidebandStream;
-    // Active buffer fields
-    sp<GraphicBuffer> mActiveBuffer;
-    sp<Fence> mActiveBufferFence;
     // False if the buffer and its contents have been previously used for GPU
     // composition, true otherwise.
     bool mIsActiveBufferUpdatedForGpu = true;
 
-    ui::Dataspace mCurrentDataSpace = ui::Dataspace::UNKNOWN;
-    Rect mCurrentCrop;
-    uint32_t mCurrentTransform{0};
     // We encode unset as -1.
     int32_t mOverrideScalingMode{-1};
     std::atomic<uint64_t> mCurrentFrameNumber{0};
-    bool mFrameLatencyNeeded{false};
     // Whether filtering is needed b/c of the drawingstate
     bool mNeedsFiltering{false};
 
@@ -894,8 +1026,6 @@
     // This layer can be a cursor on some displays.
     bool mPotentialCursor{false};
 
-    bool mFreezeGeometryUpdates{false};
-
     // Child list about to be committed/used for editing.
     LayerVector mCurrentChildren{LayerVector::StateSet::Current};
     // Child list used for rendering.
@@ -912,10 +1042,12 @@
     // Window types from WindowManager.LayoutParams
     const int mWindowType;
 
-    // This is populated if the layer is registered with Scheduler for tracking purposes.
-    std::unique_ptr<scheduler::LayerHistory::LayerHandle> mSchedulerLayerHandle;
-
 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.
@@ -930,13 +1062,8 @@
                                        const LayerVector::Visitor& visitor);
     LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                           const std::vector<Layer*>& layersInTree);
-    /**
-     * 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.
-     * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
-     * bounds are constrained by its parent bounds.
-     */
-    Rect getCroppedBufferSize(const Layer::State& s) const;
+
+    void updateTreeHasFrameRateVote();
 
     // Cached properties computed from drawing state
     // Effective transform taking into account parent transforms and any parent scaling.
@@ -958,17 +1085,33 @@
     bool mGetHandleCalled = false;
 
     void removeRemoteSyncPoints();
+
+    // Tracks the process and user id of the caller when creating this layer
+    // to help debugging.
+    pid_t mCallingPid;
+    uid_t mCallingUid;
+
+    // The current layer is a clone of mClonedFrom. This means that this layer will update it's
+    // properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers,
+    // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
+    // and relatives, this layer will update as well.
+    wp<Layer> mClonedFrom;
+
+    // The inherited shadow radius after taking into account the layer hierarchy. This is the
+    // final shadow radius for this layer. If a shadow is specified for a layer, then effective
+    // shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
+    float mEffectiveShadowRadius = 0.f;
+
+    // Returns true if the layer can draw shadows on its border.
+    virtual bool canDrawShadows() const { return true; }
+
+    // Find the root of the cloned hierarchy, this means the first non cloned parent.
+    // This will return null if first non cloned parent is not found.
+    sp<Layer> getClonedRoot();
+
+    // Finds the top most layer in the hierarchy. This will find the root Layer where the parent is
+    // null.
+    sp<Layer> getRootLayer();
 };
 
 } // 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/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index c94e439..0fe1421 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "LayerProtoHelper.h"
 
 namespace android {
@@ -155,5 +159,16 @@
     }
 }
 
+void LayerProtoHelper::writeToProto(const mat4 matrix, ColorTransformProto* colorTransformProto) {
+    for (int i = 0; i < mat4::ROW_SIZE; i++) {
+        for (int j = 0; j < mat4::COL_SIZE; j++) {
+            colorTransformProto->add_val(matrix[i][j]);
+        }
+    }
+}
+
 } // namespace surfaceflinger
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 1754a3f..502238d 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -43,6 +43,7 @@
     static void writeToProto(const InputWindowInfo& inputInfo,
                              const wp<Layer>& touchableRegionBounds,
                              std::function<InputWindowInfoProto*()> getInputWindowInfoProto);
+    static void writeToProto(const mat4 matrix, ColorTransformProto* colorTransformProto);
 };
 
 } // namespace surfaceflinger
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index 72abea8..e6c8654 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "LayerRejecter.h"
 
 #include <gui/BufferItem.h>
@@ -23,22 +27,16 @@
 
 namespace android {
 
-LayerRejecter::LayerRejecter(Layer::State& front,
-                             Layer::State& current,
-                             bool& recomputeVisibleRegions,
-                             bool stickySet,
-                             const char* name,
-                             int32_t overrideScalingMode,
-                             bool transformToDisplayInverse,
-                             bool& freezePositionUpdates)
-  : mFront(front),
-    mCurrent(current),
-    mRecomputeVisibleRegions(recomputeVisibleRegions),
-    mStickyTransformSet(stickySet),
-    mName(name),
-    mOverrideScalingMode(overrideScalingMode),
-    mTransformToDisplayInverse(transformToDisplayInverse),
-    mFreezeGeometryUpdates(freezePositionUpdates) {}
+LayerRejecter::LayerRejecter(Layer::State& front, Layer::State& current,
+                             bool& recomputeVisibleRegions, bool stickySet, const std::string& name,
+                             int32_t overrideScalingMode, bool transformToDisplayInverse)
+      : mFront(front),
+        mCurrent(current),
+        mRecomputeVisibleRegions(recomputeVisibleRegions),
+        mStickyTransformSet(stickySet),
+        mName(name),
+        mOverrideScalingMode(overrideScalingMode),
+        mTransformToDisplayInverse(transformToDisplayInverse) {}
 
 bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
     if (buf == nullptr) {
@@ -55,7 +53,7 @@
     }
 
     if (mTransformToDisplayInverse) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
         if (invTransform & ui::Transform::ROT_90) {
             std::swap(bufWidth, bufHeight);
         }
@@ -83,8 +81,6 @@
             // recompute visible region
             mRecomputeVisibleRegions = true;
 
-            mFreezeGeometryUpdates = false;
-
             if (mFront.crop_legacy != mFront.requestedCrop_legacy) {
                 mFront.crop_legacy = mFront.requestedCrop_legacy;
                 mCurrent.crop_legacy = mFront.requestedCrop_legacy;
@@ -98,7 +94,7 @@
                  "(%4d,%4d) "
                  "}\n"
                  "            requested_legacy={ wh={%4u,%4u} }}\n",
-                 mName, bufWidth, bufHeight, item.mTransform, item.mScalingMode,
+                 mName.c_str(), bufWidth, bufHeight, item.mTransform, item.mScalingMode,
                  mFront.active_legacy.w, mFront.active_legacy.h, mFront.crop_legacy.left,
                  mFront.crop_legacy.top, mFront.crop_legacy.right, mFront.crop_legacy.bottom,
                  mFront.crop_legacy.getWidth(), mFront.crop_legacy.getHeight(),
@@ -110,7 +106,8 @@
             // reject this buffer
             ALOGE("[%s] rejecting buffer: "
                   "bufWidth=%d, bufHeight=%d, front.active_legacy.{w=%d, h=%d}",
-                  mName, bufWidth, bufHeight, mFront.active_legacy.w, mFront.active_legacy.h);
+                  mName.c_str(), bufWidth, bufHeight, mFront.active_legacy.w,
+                  mFront.active_legacy.h);
             return true;
         }
     }
@@ -123,7 +120,7 @@
     // We latch the transparent region here, instead of above where we latch
     // the rest of the geometry because it is only content but not necessarily
     // resize dependent.
-    if (!mFront.activeTransparentRegion_legacy.isTriviallyEqual(
+    if (!mFront.activeTransparentRegion_legacy.hasSameRects(
                 mFront.requestedTransparentRegion_legacy)) {
         mFront.activeTransparentRegion_legacy = mFront.requestedTransparentRegion_legacy;
 
@@ -143,3 +140,6 @@
 }
 
 }  // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
index 63d51de..fb5c750 100644
--- a/services/surfaceflinger/LayerRejecter.h
+++ b/services/surfaceflinger/LayerRejecter.h
@@ -14,36 +14,29 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_LAYER_REJECTER_H
-#define ANDROID_LAYER_REJECTER_H
+#pragma once
 
 #include "Layer.h"
 #include "BufferLayerConsumer.h"
 
 namespace android {
-    class LayerRejecter : public BufferLayerConsumer::BufferRejecter {
-    public:
-        LayerRejecter(Layer::State &front,
-                      Layer::State &current,
-                      bool &recomputeVisibleRegions,
-                      bool stickySet,
-                      const char *name,
-                      int32_t overrideScalingMode,
-                      bool transformToDisplayInverse,
-                      bool &freezePositionUpdates);
 
-        virtual bool reject(const sp<GraphicBuffer> &buf, const BufferItem &item);
+class LayerRejecter : public BufferLayerConsumer::BufferRejecter {
+public:
+    LayerRejecter(Layer::State& front, Layer::State& current, bool& recomputeVisibleRegions,
+                  bool stickySet, const std::string& name, int32_t overrideScalingMode,
+                  bool transformToDisplayInverse);
 
-    private:
-        Layer::State &mFront;
-        Layer::State &mCurrent;
-        bool &mRecomputeVisibleRegions;
-        bool mStickyTransformSet;
-        const char *mName;
-        int32_t mOverrideScalingMode;
-        bool mTransformToDisplayInverse;
-        bool &mFreezeGeometryUpdates;
-    };
-}  // namespace android
+    virtual bool reject(const sp<GraphicBuffer>&, const BufferItem&);
 
-#endif  // ANDROID_LAYER_REJECTER_H
+private:
+    Layer::State& mFront;
+    Layer::State& mCurrent;
+    bool& mRecomputeVisibleRegions;
+    const bool mStickyTransformSet;
+    const std::string& mName;
+    const int32_t mOverrideScalingMode;
+    const bool mTransformToDisplayInverse;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/LayerStats.cpp b/services/surfaceflinger/LayerStats.cpp
deleted file mode 100644
index a2d1feb..0000000
--- a/services/surfaceflinger/LayerStats.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#undef LOG_TAG
-#define LOG_TAG "LayerStats"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "LayerStats.h"
-#include "DisplayHardware/HWComposer.h"
-#include "ui/DebugUtils.h"
-
-#include <android-base/stringprintf.h>
-#include <log/log.h>
-#include <utils/Trace.h>
-
-namespace android {
-
-using base::StringAppendF;
-using base::StringPrintf;
-
-void LayerStats::enable() {
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (mEnabled) return;
-    mLayerShapeStatsMap.clear();
-    mEnabled = true;
-    ALOGD("Logging enabled");
-}
-
-void LayerStats::disable() {
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (!mEnabled) return;
-    mEnabled = false;
-    ALOGD("Logging disabled");
-}
-
-void LayerStats::clear() {
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> lock(mMutex);
-    mLayerShapeStatsMap.clear();
-    ALOGD("Cleared current layer stats");
-}
-
-bool LayerStats::isEnabled() {
-    return mEnabled;
-}
-
-void LayerStats::traverseLayerTreeStatsLocked(
-        const std::vector<LayerProtoParser::Layer*>& layerTree,
-        const LayerProtoParser::LayerGlobal& layerGlobal,
-        std::vector<std::string>* const outLayerShapeVec) {
-    for (const auto& layer : layerTree) {
-        if (!layer) continue;
-        traverseLayerTreeStatsLocked(layer->children, layerGlobal, outLayerShapeVec);
-        std::string key = "";
-        StringAppendF(&key, ",%s", layer->type.c_str());
-        StringAppendF(&key, ",%s", layerCompositionType(layer->hwcCompositionType));
-        StringAppendF(&key, ",%d", layer->isProtected);
-        StringAppendF(&key, ",%s", layerTransform(layer->hwcTransform));
-        StringAppendF(&key, ",%s", layerPixelFormat(layer->activeBuffer.format).c_str());
-        StringAppendF(&key, ",%s", layer->dataspace.c_str());
-        StringAppendF(&key, ",%s",
-                      destinationLocation(layer->hwcFrame.left, layerGlobal.resolution[0], true));
-        StringAppendF(&key, ",%s",
-                      destinationLocation(layer->hwcFrame.top, layerGlobal.resolution[1], false));
-        StringAppendF(&key, ",%s",
-                      destinationSize(layer->hwcFrame.right - layer->hwcFrame.left,
-                                      layerGlobal.resolution[0], true));
-        StringAppendF(&key, ",%s",
-                      destinationSize(layer->hwcFrame.bottom - layer->hwcFrame.top,
-                                      layerGlobal.resolution[1], false));
-        StringAppendF(&key, ",%s", scaleRatioWH(layer).c_str());
-        StringAppendF(&key, ",%s", alpha(static_cast<float>(layer->color.a)));
-
-        outLayerShapeVec->push_back(key);
-        ALOGV("%s", key.c_str());
-    }
-}
-
-void LayerStats::logLayerStats(const LayersProto& layersProto) {
-    ATRACE_CALL();
-    ALOGV("Logging");
-    auto layerGlobal = LayerProtoParser::generateLayerGlobalInfo(layersProto);
-    auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
-    std::vector<std::string> layerShapeVec;
-
-    std::lock_guard<std::mutex> lock(mMutex);
-    traverseLayerTreeStatsLocked(layerTree.topLevelLayers, layerGlobal, &layerShapeVec);
-
-    std::string layerShapeKey =
-            StringPrintf("%d,%s,%s,%s", static_cast<int32_t>(layerShapeVec.size()),
-                         layerGlobal.colorMode.c_str(), layerGlobal.colorTransform.c_str(),
-                         layerTransform(layerGlobal.globalTransform));
-    ALOGV("%s", layerShapeKey.c_str());
-
-    std::sort(layerShapeVec.begin(), layerShapeVec.end(), std::greater<std::string>());
-    for (auto const& s : layerShapeVec) {
-        layerShapeKey += s;
-    }
-
-    mLayerShapeStatsMap[layerShapeKey]++;
-}
-
-void LayerStats::dump(std::string& result) {
-    ATRACE_CALL();
-    ALOGD("Dumping");
-    std::lock_guard<std::mutex> lock(mMutex);
-    result.append("Frequency,LayerCount,ColorMode,ColorTransform,Orientation\n");
-    result.append("LayerType,CompositionType,IsProtected,Transform,PixelFormat,Dataspace,");
-    result.append("DstX,DstY,DstWidth,DstHeight,WScale,HScale,Alpha\n");
-    for (auto& u : mLayerShapeStatsMap) {
-        StringAppendF(&result, "%u,%s\n", u.second, u.first.c_str());
-    }
-}
-
-const char* LayerStats::destinationLocation(int32_t location, int32_t range, bool isHorizontal) {
-    static const char* locationArray[8] = {"0", "1/8", "1/4", "3/8", "1/2", "5/8", "3/4", "7/8"};
-    int32_t ratio = location * 8 / range;
-    if (ratio < 0) return "N/A";
-    if (isHorizontal) {
-        // X location is divided into 4 buckets {"0", "1/4", "1/2", "3/4"}
-        if (ratio > 6) return "3/4";
-        // use index 0, 2, 4, 6
-        return locationArray[ratio & ~1];
-    }
-    if (ratio > 7) return "7/8";
-    return locationArray[ratio];
-}
-
-const char* LayerStats::destinationSize(int32_t size, int32_t range, bool isWidth) {
-    static const char* sizeArray[8] = {"1/8", "1/4", "3/8", "1/2", "5/8", "3/4", "7/8", "1"};
-    int32_t ratio = size * 8 / range;
-    if (ratio < 0) return "N/A";
-    if (isWidth) {
-        // width is divided into 4 buckets {"1/4", "1/2", "3/4", "1"}
-        if (ratio > 6) return "1";
-        // use index 1, 3, 5, 7
-        return sizeArray[ratio | 1];
-    }
-    if (ratio > 7) return "1";
-    return sizeArray[ratio];
-}
-
-const char* LayerStats::layerTransform(int32_t transform) {
-    return getTransformName(static_cast<hwc_transform_t>(transform));
-}
-
-const char* LayerStats::layerCompositionType(int32_t compositionType) {
-    return getCompositionName(static_cast<hwc2_composition_t>(compositionType));
-}
-
-std::string LayerStats::layerPixelFormat(int32_t pixelFormat) {
-    return decodePixelFormat(pixelFormat);
-}
-
-std::string LayerStats::scaleRatioWH(const LayerProtoParser::Layer* layer) {
-    if (!layer->type.compare("ColorLayer")) return "N/A,N/A";
-    std::string ret = "";
-    if (isRotated(layer->hwcTransform)) {
-        ret += scaleRatio(layer->hwcFrame.right - layer->hwcFrame.left,
-                          static_cast<int32_t>(layer->hwcCrop.bottom - layer->hwcCrop.top));
-        ret += ",";
-        ret += scaleRatio(layer->hwcFrame.bottom - layer->hwcFrame.top,
-                          static_cast<int32_t>(layer->hwcCrop.right - layer->hwcCrop.left));
-    } else {
-        ret += scaleRatio(layer->hwcFrame.right - layer->hwcFrame.left,
-                          static_cast<int32_t>(layer->hwcCrop.right - layer->hwcCrop.left));
-        ret += ",";
-        ret += scaleRatio(layer->hwcFrame.bottom - layer->hwcFrame.top,
-                          static_cast<int32_t>(layer->hwcCrop.bottom - layer->hwcCrop.top));
-    }
-    return ret;
-}
-
-const char* LayerStats::scaleRatio(int32_t destinationScale, int32_t sourceScale) {
-    // Make scale buckets from <1/64 to >= 16, to avoid floating point
-    // calculation, x64 on destinationScale first
-    int32_t scale = destinationScale * 64 / sourceScale;
-    if (!scale) return "<1/64";
-    if (scale < 2) return "1/64";
-    if (scale < 4) return "1/32";
-    if (scale < 8) return "1/16";
-    if (scale < 16) return "1/8";
-    if (scale < 32) return "1/4";
-    if (scale < 64) return "1/2";
-    if (scale < 128) return "1";
-    if (scale < 256) return "2";
-    if (scale < 512) return "4";
-    if (scale < 1024) return "8";
-    return ">=16";
-}
-
-const char* LayerStats::alpha(float a) {
-    if (a == 1.0f) return "1.0";
-    if (a > 0.9f) return "0.99";
-    if (a > 0.8f) return "0.9";
-    if (a > 0.7f) return "0.8";
-    if (a > 0.6f) return "0.7";
-    if (a > 0.5f) return "0.6";
-    if (a > 0.4f) return "0.5";
-    if (a > 0.3f) return "0.4";
-    if (a > 0.2f) return "0.3";
-    if (a > 0.1f) return "0.2";
-    if (a > 0.0f) return "0.1";
-    return "0.0";
-}
-
-bool LayerStats::isRotated(int32_t transform) {
-    return transform & HWC_TRANSFORM_ROT_90;
-}
-
-bool LayerStats::isVFlipped(int32_t transform) {
-    return transform & HWC_TRANSFORM_FLIP_V;
-}
-
-bool LayerStats::isHFlipped(int32_t transform) {
-    return transform & HWC_TRANSFORM_FLIP_H;
-}
-
-}  // namespace android
diff --git a/services/surfaceflinger/LayerStats.h b/services/surfaceflinger/LayerStats.h
deleted file mode 100644
index 62b2688..0000000
--- a/services/surfaceflinger/LayerStats.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <layerproto/LayerProtoHeader.h>
-#include <layerproto/LayerProtoParser.h>
-#include <mutex>
-#include <unordered_map>
-
-using namespace android::surfaceflinger;
-
-namespace android {
-
-class LayerStats {
-public:
-    void enable();
-    void disable();
-    void clear();
-    bool isEnabled();
-    void logLayerStats(const LayersProto& layersProto);
-    void dump(std::string& result);
-
-private:
-    // Traverse layer tree to get all visible layers' stats
-    void traverseLayerTreeStatsLocked(
-            const std::vector<LayerProtoParser::Layer*>& layerTree,
-            const LayerProtoParser::LayerGlobal& layerGlobal,
-            std::vector<std::string>* const outLayerShapeVec);
-    // Convert layer's top-left position into 8x8 percentage of the display
-    static const char* destinationLocation(int32_t location, int32_t range, bool isHorizontal);
-    // Convert layer's size into 8x8 percentage of the display
-    static const char* destinationSize(int32_t size, int32_t range, bool isWidth);
-    // Return the name of the transform
-    static const char* layerTransform(int32_t transform);
-    // Return the name of the composition type
-    static const char* layerCompositionType(int32_t compositionType);
-    // Return the name of the pixel format
-    static std::string layerPixelFormat(int32_t pixelFormat);
-    // Calculate scale ratios of layer's width/height with rotation information
-    static std::string scaleRatioWH(const LayerProtoParser::Layer* layer);
-    // Calculate scale ratio from source to destination and convert to string
-    static const char* scaleRatio(int32_t destinationScale, int32_t sourceScale);
-    // Bucket the alpha into designed buckets
-    static const char* alpha(float a);
-    // Return whether the original buffer is rotated in final composition
-    static bool isRotated(int32_t transform);
-    // Return whether the original buffer is V-flipped in final composition
-    static bool isVFlipped(int32_t transform);
-    // Return whether the original buffer is H-flipped in final composition
-    static bool isHFlipped(int32_t transform);
-
-    bool mEnabled = false;
-    // Protect mLayersStatsMap
-    std::mutex mMutex;
-    // Hashmap for tracking the frame(layer shape) stats
-    // KEY is a concatenation of all layers' properties within a frame
-    // VALUE is the number of times this particular set has been scanned out
-    std::unordered_map<std::string, uint32_t> mLayerShapeStatsMap;
-};
-
-}  // namespace android
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index 8494524..9b94920 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "LayerVector.h"
 #include "Layer.h"
 
@@ -64,7 +68,7 @@
         const auto& layer = (*this)[i];
         auto& state = (stateSet == StateSet::Current) ? layer->getCurrentState()
                                                       : layer->getDrawingState();
-        if (state.zOrderRelativeOf != nullptr) {
+        if (state.isRelativeOf) {
             continue;
         }
         layer->traverseInZOrder(stateSet, visitor);
@@ -76,10 +80,21 @@
         const auto& layer = (*this)[i];
         auto& state = (stateSet == StateSet::Current) ? layer->getCurrentState()
                                                       : layer->getDrawingState();
-        if (state.zOrderRelativeOf != nullptr) {
+        if (state.isRelativeOf) {
             continue;
         }
         layer->traverseInReverseZOrder(stateSet, visitor);
      }
 }
+
+void LayerVector::traverse(const Visitor& visitor) const {
+    for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) {
+        const auto& layer = (*this)[i];
+        layer->traverse(mStateSet, visitor);
+    }
+}
+
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h
index 88d7711..a531f4f 100644
--- a/services/surfaceflinger/LayerVector.h
+++ b/services/surfaceflinger/LayerVector.h
@@ -50,7 +50,7 @@
     using Visitor = std::function<void(Layer*)>;
     void traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const;
     void traverseInZOrder(StateSet stateSet, const Visitor& visitor) const;
-
+    void traverse(const Visitor& visitor) const;
 private:
     const StateSet mStateSet;
 };
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index c60421b..18a2891 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "MonitoredProducer.h"
 #include "Layer.h"
 #include "SurfaceFlinger.h"
@@ -34,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) {
@@ -154,6 +152,10 @@
     return mProducer->getConsumerUsage(outUsage);
 }
 
+status_t MonitoredProducer::setAutoPrerotation(bool autoPrerotation) {
+    return mProducer->setAutoPrerotation(autoPrerotation);
+}
+
 IBinder* MonitoredProducer::onAsBinder() {
     return this;
 }
@@ -164,3 +166,6 @@
 
 // ---------------------------------------------------------------------------
 }; // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index d346f82..788919b 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -70,6 +70,7 @@
     virtual void getFrameTimestamps(FrameEventHistoryDelta *outDelta) override;
     virtual status_t getUniqueId(uint64_t* outId) const override;
     virtual status_t getConsumerUsage(uint64_t* outUsage) const override;
+    virtual status_t setAutoPrerotation(bool autoPrerotation) override;
 
     // The Layer which created this producer, and on which queued Buffer's will be displayed.
     sp<Layer> getLayer() const;
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 5b4bec9..f602412 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -14,32 +14,152 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "RefreshRateOverlay.h"
 #include "Client.h"
 #include "Layer.h"
 
+#include <gui/IProducerListener.h>
+
+#undef LOG_TAG
+#define LOG_TAG "RefreshRateOverlay"
+
 namespace android {
 
-using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+void RefreshRateOverlay::SevenSegmentDrawer::drawRect(const Rect& r, const half4& color,
+                                                      const sp<GraphicBuffer>& buffer,
+                                                      uint8_t* pixels) {
+    for (int32_t j = r.top; j < r.bottom; j++) {
+        if (j >= buffer->getHeight()) {
+            break;
+        }
+
+        for (int32_t i = r.left; i < r.right; i++) {
+            if (i >= buffer->getWidth()) {
+                break;
+            }
+
+            uint8_t* iter = pixels + 4 * (i + (buffer->getStride() * j));
+            iter[0] = uint8_t(color.r * 255);
+            iter[1] = uint8_t(color.g * 255);
+            iter[2] = uint8_t(color.b * 255);
+            iter[3] = uint8_t(color.a * 255);
+        }
+    }
+}
+
+void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left,
+                                                         const half4& color,
+                                                         const sp<GraphicBuffer>& buffer,
+                                                         uint8_t* pixels) {
+    const Rect rect = [&]() {
+        switch (segment) {
+            case Segment::Upper:
+                return Rect(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE);
+            case Segment::UpperLeft:
+                return Rect(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2);
+            case Segment::UpperRight:
+                return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH,
+                            DIGIT_HEIGHT / 2);
+            case Segment::Middle:
+                return Rect(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, left + DIGIT_WIDTH,
+                            DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2);
+            case Segment::LowerLeft:
+                return Rect(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT);
+            case Segment::LowerRight:
+                return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2, left + DIGIT_WIDTH,
+                            DIGIT_HEIGHT);
+            case Segment::Buttom:
+                return Rect(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH, DIGIT_HEIGHT);
+        }
+    }();
+
+    drawRect(rect, color, buffer, pixels);
+}
+
+void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, const half4& color,
+                                                       const sp<GraphicBuffer>& buffer,
+                                                       uint8_t* pixels) {
+    if (digit < 0 || digit > 9) return;
+
+    if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
+        digit == 8 || digit == 9)
+        drawSegment(Segment::Upper, left, color, buffer, pixels);
+    if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
+        drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+    if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
+        digit == 8 || digit == 9)
+        drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+    if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
+        digit == 9)
+        drawSegment(Segment::Middle, left, color, buffer, pixels);
+    if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
+        drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+    if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
+        digit == 7 || digit == 8 || digit == 9)
+        drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+    if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
+        digit == 9)
+        drawSegment(Segment::Buttom, left, color, buffer, pixels);
+}
+
+sp<GraphicBuffer> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number,
+                                                                     const half4& color) {
+    if (number < 0 || number > 1000) return nullptr;
+
+    const auto hundreds = number / 100;
+    const auto tens = (number / 10) % 10;
+    const auto ones = number % 10;
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                              GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
+                                      GRALLOC_USAGE_HW_TEXTURE,
+                              "RefreshRateOverlayBuffer");
+    uint8_t* pixels;
+    buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+    // Clear buffer content
+    drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
+    int left = 0;
+    if (hundreds != 0) {
+        drawDigit(hundreds, left, color, buffer, pixels);
+        left += DIGIT_WIDTH + DIGIT_SPACE;
+    }
+
+    if (tens != 0) {
+        drawDigit(tens, left, color, buffer, pixels);
+        left += DIGIT_WIDTH + DIGIT_SPACE;
+    }
+
+    drawDigit(ones, left, color, buffer, pixels);
+    buffer->unlock();
+    return buffer;
+}
 
 RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger)
       : mFlinger(flinger), mClient(new Client(&mFlinger)) {
     createLayer();
+    primeCache();
 }
 
 bool RefreshRateOverlay::createLayer() {
     const status_t ret =
-            mFlinger.createLayer(String8("RefreshRateOverlay"), mClient, 0, 0,
-                                 PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor,
-                                 LayerMetadata(), &mIBinder, &mGbp, nullptr);
+            mFlinger.createLayer(String8("RefreshRateOverlay"), mClient,
+                                 SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(),
+                                 PIXEL_FORMAT_RGBA_8888,
+                                 ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(),
+                                 &mIBinder, &mGbp, nullptr);
     if (ret) {
-        ALOGE("failed to create color layer");
+        ALOGE("failed to create buffer state layer");
         return false;
     }
 
     Mutex::Autolock _l(mFlinger.mStateLock);
     mLayer = mClient->getLayerUser(mIBinder);
-    mLayer->setCrop_legacy(Rect(50, 70, 200, 100), true);
+    mLayer->setFrameRate(Layer::FrameRate(0, Layer::FrameRateCompatibility::NoVote));
 
     // setting Layer's Z requires resorting layersSortedByZ
     ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer);
@@ -51,10 +171,51 @@
     return true;
 }
 
-void RefreshRateOverlay::changeRefreshRate(RefreshRateType type) {
-    const half3& color = (type == RefreshRateType::PERFORMANCE) ? GREEN : RED;
-    mLayer->setColor(color);
+void RefreshRateOverlay::primeCache() {
+    auto& allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates();
+    if (allRefreshRates.size() == 1) {
+        auto fps = allRefreshRates.begin()->second->getFps();
+        half4 color = {LOW_FPS_COLOR, ALPHA};
+        mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+        return;
+    }
+
+    std::vector<uint32_t> supportedFps;
+    supportedFps.reserve(allRefreshRates.size());
+    for (auto& [ignored, refreshRate] : allRefreshRates) {
+        supportedFps.push_back(refreshRate->getFps());
+    }
+
+    std::sort(supportedFps.begin(), supportedFps.end());
+    const auto mLowFps = supportedFps[0];
+    const auto mHighFps = supportedFps[supportedFps.size() - 1];
+    for (auto fps : supportedFps) {
+        const auto fpsScale = float(fps - mLowFps) / (mHighFps - mLowFps);
+        half4 color;
+        color.r = HIGH_FPS_COLOR.r * fpsScale + LOW_FPS_COLOR.r * (1 - fpsScale);
+        color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
+        color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
+        color.a = ALPHA;
+        mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+    }
+}
+
+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 ce29bc3..35c8020 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -13,31 +13,75 @@
  * 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 {
 
-using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+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(RefreshRateType type);
+    void setViewport(ui::Size);
+    void changeRefreshRate(const RefreshRate&);
 
 private:
+    class SevenSegmentDrawer {
+    public:
+        static sp<GraphicBuffer> drawNumber(int number, const half4& color);
+        static uint32_t getHeight() { return BUFFER_HEIGHT; }
+        static uint32_t getWidth() { return BUFFER_WIDTH; }
+
+    private:
+        enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Buttom };
+
+        static void drawRect(const Rect& r, const half4& color, const sp<GraphicBuffer>& buffer,
+                             uint8_t* pixels);
+        static void drawSegment(Segment segment, int left, const half4& color,
+                                const sp<GraphicBuffer>& buffer, uint8_t* pixels);
+        static void drawDigit(int digit, int left, const half4& color,
+                              const sp<GraphicBuffer>& buffer, uint8_t* pixels);
+
+        static constexpr uint32_t DIGIT_HEIGHT = 100;
+        static constexpr uint32_t DIGIT_WIDTH = 64;
+        static constexpr uint32_t DIGIT_SPACE = 16;
+        static constexpr uint32_t BUFFER_HEIGHT = DIGIT_HEIGHT;
+        static constexpr uint32_t BUFFER_WIDTH =
+                3 * DIGIT_WIDTH + 2 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit
+    };
+
     bool createLayer();
+    void primeCache();
 
     SurfaceFlinger& mFlinger;
-    sp<Client> mClient;
+    const sp<Client> mClient;
     sp<Layer> mLayer;
     sp<IBinder> mIBinder;
     sp<IGraphicBufferProducer> mGbp;
 
-    const half3 RED = half3(1.0f, 0.0f, 0.0f);
-    const half3 GREEN = half3(0.0f, 1.0f, 0.0f);
+    std::unordered_map<int, sp<GraphicBuffer>> mBufferCache;
+
+    static constexpr float ALPHA = 0.8f;
+    const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f);
+    const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f);
 };
 
-}; // namespace android
+} // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 80da154..27353d8 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #undef LOG_TAG
@@ -21,15 +25,18 @@
 
 #include "RegionSamplingThread.h"
 
-#include <cutils/properties.h>
-#include <gui/IRegionSamplingListener.h>
-#include <utils/Trace.h>
-#include <string>
-
 #include <compositionengine/Display.h>
 #include <compositionengine/impl/OutputCompositionState.h>
+#include <cutils/properties.h>
+#include <gui/IRegionSamplingListener.h>
+#include <ui/DisplayStatInfo.h>
+#include <utils/Trace.h>
+
+#include <string>
+
 #include "DisplayDevice.h"
 #include "Layer.h"
+#include "Scheduler/DispSync.h"
 #include "SurfaceFlinger.h"
 
 namespace android {
@@ -105,9 +112,8 @@
         if (mVsyncListening) return;
 
         mPhaseIntervalSetting = Phase::ZERO;
-        mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
-            sync.addEventListener("SamplingThreadDispSyncListener", 0, this, mLastCallbackTime);
-        });
+        mScheduler.getPrimaryDispSync().addEventListener("SamplingThreadDispSyncListener", 0, this,
+                                                         mLastCallbackTime);
         mVsyncListening = true;
     }
 
@@ -120,28 +126,23 @@
     void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
         if (!mVsyncListening) return;
 
-        mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
-            sync.removeEventListener(this, &mLastCallbackTime);
-        });
+        mScheduler.getPrimaryDispSync().removeEventListener(this, &mLastCallbackTime);
         mVsyncListening = false;
     }
 
-    void onDispSyncEvent(nsecs_t /* when */) final {
+    void onDispSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/) final {
         std::unique_lock<decltype(mMutex)> lock(mMutex);
 
         if (mPhaseIntervalSetting == Phase::ZERO) {
             ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
             mPhaseIntervalSetting = Phase::SAMPLING;
-            mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
-                sync.changePhaseOffset(this, mTargetSamplingOffset.count());
-            });
+            mScheduler.getPrimaryDispSync().changePhaseOffset(this, mTargetSamplingOffset.count());
             return;
         }
 
         if (mPhaseIntervalSetting == Phase::SAMPLING) {
             mPhaseIntervalSetting = Phase::ZERO;
-            mScheduler.withPrimaryDispSync(
-                    [this](android::DispSync& sync) { sync.changePhaseOffset(this, 0); });
+            mScheduler.getPrimaryDispSync().changePhaseOffset(this, 0);
             stopVsyncListenerLocked();
             lock.unlock();
             mRegionSamplingThread.notifySamplingOffset();
@@ -198,13 +199,8 @@
     }
 }
 
-void RegionSamplingThread::addListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
+void RegionSamplingThread::addListener(const Rect& samplingArea, const wp<Layer>& stopLayer,
                                        const sp<IRegionSamplingListener>& listener) {
-    wp<Layer> stopLayer;
-    if (stopLayerHandle != nullptr && stopLayerHandle->localBinder() != nullptr) {
-        stopLayer = static_cast<Layer::Handle*>(stopLayerHandle.get())->owner;
-    }
-
     sp<IBinder> asBinder = IInterface::asBinder(listener);
     asBinder->linkToDeath(this);
     std::lock_guard lock(mSamplingMutex);
@@ -272,16 +268,6 @@
     mDescriptors.erase(who);
 }
 
-namespace {
-// Using Rec. 709 primaries
-inline float getLuma(float r, float g, float b) {
-    constexpr auto rec709_red_primary = 0.2126f;
-    constexpr auto rec709_green_primary = 0.7152f;
-    constexpr auto rec709_blue_primary = 0.0722f;
-    return rec709_red_primary * r + rec709_green_primary * g + rec709_blue_primary * b;
-}
-} // anonymous namespace
-
 float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride,
                  uint32_t orientation, const Rect& sample_area) {
     if (!sample_area.isValid() || (sample_area.getWidth() > width) ||
@@ -302,30 +288,23 @@
         std::swap(area.left, area.right);
     }
 
-    std::array<int32_t, 256> brightnessBuckets = {};
-    const int32_t majoritySampleNum = area.getWidth() * area.getHeight() / 2;
+    const uint32_t pixelCount = (area.bottom - area.top) * (area.right - area.left);
+    uint32_t accumulatedLuma = 0;
 
+    // Calculates luma with approximation of Rec. 709 primaries
     for (int32_t row = area.top; row < area.bottom; ++row) {
         const uint32_t* rowBase = data + row * stride;
         for (int32_t column = area.left; column < area.right; ++column) {
             uint32_t pixel = rowBase[column];
-            const float r = pixel & 0xFF;
-            const float g = (pixel >> 8) & 0xFF;
-            const float b = (pixel >> 16) & 0xFF;
-            const uint8_t luma = std::round(getLuma(r, g, b));
-            ++brightnessBuckets[luma];
-            if (brightnessBuckets[luma] > majoritySampleNum) return luma / 255.0f;
+            const uint32_t r = pixel & 0xFF;
+            const uint32_t g = (pixel >> 8) & 0xFF;
+            const uint32_t b = (pixel >> 16) & 0xFF;
+            const uint32_t luma = (r * 7 + b * 2 + g * 23) >> 5;
+            accumulatedLuma += luma;
         }
     }
 
-    int32_t accumulated = 0;
-    size_t bucket = 0;
-    for (; bucket < brightnessBuckets.size(); bucket++) {
-        accumulated += brightnessBuckets[bucket];
-        if (accumulated > majoritySampleNum) break;
-    }
-
-    return bucket / 255.0f;
+    return accumulatedLuma / (255.0f * pixelCount);
 }
 
 std::vector<float> RegionSamplingThread::sampleBuffer(
@@ -358,19 +337,7 @@
     }
 
     const auto device = mFlinger.getDefaultDisplayDevice();
-    const auto orientation = [](uint32_t orientation) {
-        switch (orientation) {
-            default:
-            case DisplayState::eOrientationDefault:
-                return ui::Transform::ROT_0;
-            case DisplayState::eOrientation90:
-                return ui::Transform::ROT_90;
-            case DisplayState::eOrientation180:
-                return ui::Transform::ROT_180;
-            case DisplayState::eOrientation270:
-                return ui::Transform::ROT_270;
-        }
-    }(device->getOrientation());
+    const auto orientation = ui::Transform::toRotationFlags(device->getOrientation());
 
     std::vector<RegionSamplingThread::Descriptor> descriptors;
     Region sampleRegion;
@@ -440,7 +407,7 @@
             }
             if (!intersectsAnyArea) return;
 
-            ALOGV("Traversing [%s] [%d, %d, %d, %d]", layer->getName().string(), bounds.left,
+            ALOGV("Traversing [%s] [%d, %d, %d, %d]", layer->getDebugName(), bounds.left,
                   bounds.top, bounds.right, bounds.bottom);
             visitor(layer);
         };
@@ -458,7 +425,8 @@
     }
 
     bool ignored;
-    mFlinger.captureScreenCommon(renderArea, traverseLayers, buffer, false, ignored);
+    mFlinger.captureScreenCommon(renderArea, traverseLayers, buffer, false /* identityTransform */,
+                                 true /* regionSampling */, ignored);
 
     std::vector<Descriptor> activeDescriptors;
     for (const auto& descriptor : descriptors) {
@@ -505,3 +473,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 96ffe20..b9b7a3c 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -27,7 +27,7 @@
 #include <ui/GraphicBuffer.h>
 #include <ui/Rect.h>
 #include <utils/StrongPointer.h>
-#include "Scheduler/IdleTimer.h"
+#include "Scheduler/OneShotTimer.h"
 
 namespace android {
 
@@ -69,7 +69,7 @@
 
     // Add a listener to receive luma notifications. The luma reported via listener will
     // report the median luma for the layers under the stopLayerHandle, in the samplingArea region.
-    void addListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
+    void addListener(const Rect& samplingArea, const wp<Layer>& stopLayer,
                      const sp<IRegionSamplingListener>& listener);
     // Remove the listener to stop receiving median luma notifications.
     void removeListener(const sp<IRegionSamplingListener>& listener);
@@ -107,7 +107,7 @@
     SurfaceFlinger& mFlinger;
     Scheduler& mScheduler;
     const TimingTunables mTunables;
-    scheduler::IdleTimer mIdleTimer;
+    scheduler::OneShotTimer mIdleTimer;
 
     std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback;
 
diff --git a/services/surfaceflinger/RenderArea.cpp b/services/surfaceflinger/RenderArea.cpp
index 93759e8..9a6c853 100644
--- a/services/surfaceflinger/RenderArea.cpp
+++ b/services/surfaceflinger/RenderArea.cpp
@@ -1,3 +1,23 @@
+/*
+ * Copyright 2017 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "RenderArea.h"
 
 namespace android {
@@ -13,3 +33,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index edc6442..6b0455a 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -17,18 +17,21 @@
 // physical render area.
 class RenderArea {
 public:
+    using RotationFlags = ui::Transform::RotationFlags;
+
     enum class CaptureFill {CLEAR, OPAQUE};
 
     static float getCaptureFillValue(CaptureFill captureFill);
 
     RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
-               ui::Dataspace reqDataSpace,
-               ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
+               ui::Dataspace reqDataSpace, const Rect& displayViewport,
+               RotationFlags rotation = ui::Transform::ROT_0)
           : mReqWidth(reqWidth),
             mReqHeight(reqHeight),
             mReqDataSpace(reqDataSpace),
             mCaptureFill(captureFill),
-            mRotationFlags(rotation) {}
+            mRotationFlags(rotation),
+            mDisplayViewport(displayViewport) {}
 
     virtual ~RenderArea() = default;
 
@@ -58,34 +61,37 @@
     // render area.  It can be larger than the logical render area.  It can
     // also be optionally rotated.
     //
-    // Layers are first clipped to the source crop (in addition to being
-    // clipped to the logical render area already).  The source crop and the
-    // layers are then rotated around the center of the source crop, and
-    // scaled to the physical render area linearly.
+    // The source crop is specified in layer space (when rendering a layer and
+    // its children), or in layer-stack space (when rendering all layers visible
+    // on the display).
     virtual Rect getSourceCrop() const = 0;
 
     // Returns the rotation of the source crop and the layers.
-    ui::Transform::orientation_flags getRotationFlags() const { return mRotationFlags; };
+    RotationFlags getRotationFlags() const { return mRotationFlags; }
 
     // Returns the size of the physical render area.
-    int getReqWidth() const { return mReqWidth; };
-    int getReqHeight() const { return mReqHeight; };
+    int getReqWidth() const { return static_cast<int>(mReqWidth); }
+    int getReqHeight() const { return static_cast<int>(mReqHeight); }
 
     // Returns the composition data space of the render area.
     ui::Dataspace getReqDataSpace() const { return mReqDataSpace; }
 
     // Returns the fill color of the physical render area.  Regions not
     // covered by any rendered layer should be filled with this color.
-    CaptureFill getCaptureFill() const { return mCaptureFill; };
+    CaptureFill getCaptureFill() const { return mCaptureFill; }
 
-    virtual const sp<const DisplayDevice> getDisplayDevice() const = 0;
+    virtual sp<const DisplayDevice> getDisplayDevice() const = 0;
+
+    // Returns the source display viewport.
+    const Rect& getDisplayViewport() const { return mDisplayViewport; }
 
 private:
     const uint32_t mReqWidth;
     const uint32_t mReqHeight;
     const ui::Dataspace mReqDataSpace;
     const CaptureFill mCaptureFill;
-    const ui::Transform::orientation_flags mRotationFlags;
+    const RotationFlags mRotationFlags;
+    const Rect mDisplayViewport;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 04b5241..46112f5 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
 
@@ -64,7 +68,7 @@
     DispSyncThread(const char* name, bool showTraceDetailedInfo)
           : mName(name),
             mStop(false),
-            mModelLocked(false),
+            mModelLocked("DispSync:ModelLocked", false),
             mPeriod(0),
             mPhase(0),
             mReferenceTime(0),
@@ -121,13 +125,11 @@
     void lockModel() {
         Mutex::Autolock lock(mMutex);
         mModelLocked = true;
-        ATRACE_INT("DispSync:ModelLocked", mModelLocked);
     }
 
     void unlockModel() {
         Mutex::Autolock lock(mMutex);
         mModelLocked = false;
-        ATRACE_INT("DispSync:ModelLocked", mModelLocked);
     }
 
     virtual bool threadLoop() {
@@ -198,7 +200,8 @@
                     }
                 }
 
-                callbackInvocations = gatherCallbackInvocationsLocked(now);
+                callbackInvocations =
+                        gatherCallbackInvocationsLocked(now, computeNextRefreshLocked(0, now));
             }
 
             if (callbackInvocations.size() > 0) {
@@ -301,6 +304,11 @@
         return BAD_VALUE;
     }
 
+    nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const {
+        Mutex::Autolock lock(mMutex);
+        return computeNextRefreshLocked(periodOffset, now);
+    }
+
 private:
     struct EventListener {
         const char* mName;
@@ -313,6 +321,7 @@
     struct CallbackInvocation {
         DispSync::Callback* mCallback;
         nsecs_t mEventTime;
+        nsecs_t mExpectedVSyncTime;
     };
 
     nsecs_t computeNextEventTimeLocked(nsecs_t now) {
@@ -338,7 +347,8 @@
         return duration < (3 * mPeriod) / 5;
     }
 
-    std::vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
+    std::vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now,
+                                                                    nsecs_t expectedVSyncTime) {
         if (mTraceDetailedInfo) ATRACE_CALL();
         ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName, ns2us(now));
 
@@ -359,6 +369,10 @@
                 CallbackInvocation ci;
                 ci.mCallback = eventListener.mCallback;
                 ci.mEventTime = t;
+                ci.mExpectedVSyncTime = expectedVSyncTime;
+                if (eventListener.mPhase < 0) {
+                    ci.mExpectedVSyncTime += mPeriod;
+                }
                 ALOGV("[%s] [%s] Preparing to fire, latency: %" PRId64, mName, eventListener.mName,
                       t - eventListener.mLastEventTime);
                 callbackInvocations.push_back(ci);
@@ -424,14 +438,23 @@
     void fireCallbackInvocations(const std::vector<CallbackInvocation>& callbacks) {
         if (mTraceDetailedInfo) ATRACE_CALL();
         for (size_t i = 0; i < callbacks.size(); i++) {
-            callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
+            callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime,
+                                                    callbacks[i].mExpectedVSyncTime);
         }
     }
 
+    nsecs_t computeNextRefreshLocked(int periodOffset, nsecs_t now) const {
+        nsecs_t phase = mReferenceTime + mPhase;
+        if (mPeriod == 0) {
+            return 0;
+        }
+        return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase;
+    }
+
     const char* const mName;
 
     bool mStop;
-    bool mModelLocked;
+    TracedOrdinal<bool> mModelLocked;
 
     nsecs_t mPeriod;
     nsecs_t mPhase;
@@ -442,7 +465,7 @@
 
     std::vector<EventListener> mEventListeners;
 
-    Mutex mMutex;
+    mutable Mutex mMutex;
     Condition mCond;
 
     // Flag to turn on logging in systrace.
@@ -454,33 +477,24 @@
 
 class ZeroPhaseTracer : public DispSync::Callback {
 public:
-    ZeroPhaseTracer() : mParity(false) {}
+    ZeroPhaseTracer() : mParity("ZERO_PHASE_VSYNC", false) {}
 
-    virtual void onDispSyncEvent(nsecs_t /*when*/) {
+    virtual void onDispSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/) {
         mParity = !mParity;
-        ATRACE_INT("ZERO_PHASE_VSYNC", mParity ? 1 : 0);
     }
 
 private:
-    bool mParity;
+    TracedOrdinal<bool> mParity;
 };
 
-DispSync::DispSync(const char* name) : mName(name), mRefreshSkipCount(0) {
+DispSync::DispSync(const char* name, bool hasSyncFramework)
+      : mName(name), mIgnorePresentFences(!hasSyncFramework) {
     // This flag offers the ability to turn on systrace logging from the shell.
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sf.dispsync_trace_detailed_info", value, "0");
     mTraceDetailedInfo = atoi(value);
+
     mThread = new DispSyncThread(name, mTraceDetailedInfo);
-}
-
-DispSync::~DispSync() {
-    mThread->stop();
-    mThread->requestExitAndWait();
-}
-
-void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) {
-    mIgnorePresentFences = !hasSyncFramework;
-    mPresentTimeOffset = dispSyncPresentTimeOffset;
     mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
 
     // set DispSync to SCHED_FIFO to minimize jitter
@@ -498,6 +512,11 @@
     }
 }
 
+DispSync::~DispSync() {
+    mThread->stop();
+    mThread->requestExitAndWait();
+}
+
 void DispSync::reset() {
     Mutex::Autolock lock(mMutex);
     resetLocked();
@@ -544,7 +563,8 @@
     resetLocked();
 }
 
-bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
+bool DispSync::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> /*hwcVsyncPeriod*/,
+                               bool* periodFlushed) {
     Mutex::Autolock lock(mMutex);
 
     ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp));
@@ -623,13 +643,6 @@
     return mThread->addEventListener(name, phase, callback, lastCallbackTime);
 }
 
-void DispSync::setRefreshSkipCount(int count) {
-    Mutex::Autolock lock(mMutex);
-    ALOGD("setRefreshSkipCount(%d)", count);
-    mRefreshSkipCount = count;
-    updateModelLocked();
-}
-
 status_t DispSync::removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) {
     Mutex::Autolock lock(mMutex);
     return mThread->removeEventListener(callback, outLastCallbackTime);
@@ -712,9 +725,6 @@
             ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase));
         }
 
-        // Artificially inflate the period if requested.
-        mPeriod += mPeriod * mRefreshSkipCount;
-
         mThread->updateModel(mPeriod, mPhase, mReferenceTime);
         mModelUpdated = true;
     }
@@ -725,10 +735,6 @@
         return;
     }
 
-    // Need to compare present fences against the un-adjusted refresh period,
-    // since they might arrive between two events.
-    nsecs_t period = mPeriod / (1 + mRefreshSkipCount);
-
     int numErrSamples = 0;
     nsecs_t sqErrSum = 0;
 
@@ -747,9 +753,9 @@
             continue;
         }
 
-        nsecs_t sampleErr = (sample - mPhase) % period;
-        if (sampleErr > period / 2) {
-            sampleErr -= period;
+        nsecs_t sampleErr = (sample - mPhase) % mPeriod;
+        if (sampleErr > mPeriod / 2) {
+            sampleErr -= mPeriod;
         }
         sqErrSum += sampleErr * sampleErr;
         numErrSamples++;
@@ -783,9 +789,8 @@
     }
 }
 
-nsecs_t DispSync::computeNextRefresh(int periodOffset) const {
+nsecs_t DispSync::computeNextRefresh(int periodOffset, nsecs_t now) const {
     Mutex::Autolock lock(mMutex);
-    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
     nsecs_t phase = mReferenceTime + mPhase;
     if (mPeriod == 0) {
         return 0;
@@ -804,8 +809,7 @@
 void DispSync::dump(std::string& result) const {
     Mutex::Autolock lock(mMutex);
     StringAppendF(&result, "present fences are %s\n", mIgnorePresentFences ? "ignored" : "used");
-    StringAppendF(&result, "mPeriod: %" PRId64 " ns (%.3f fps; skipCount=%d)\n", mPeriod,
-                  1000000000.0 / mPeriod, mRefreshSkipCount);
+    StringAppendF(&result, "mPeriod: %" PRId64 " ns (%.3f fps)\n", mPeriod, 1000000000.0 / mPeriod);
     StringAppendF(&result, "mPhase: %" PRId64 " ns\n", mPhase);
     StringAppendF(&result, "mError: %" PRId64 " ns (sqrt=%.1f)\n", mError, sqrt(mError));
     StringAppendF(&result, "mNumResyncSamplesSincePresent: %d (limit %d)\n",
@@ -853,7 +857,7 @@
     StringAppendF(&result, "current monotonic time: %" PRId64 "\n", now);
 }
 
-nsecs_t DispSync::expectedPresentTime() {
+nsecs_t DispSync::expectedPresentTime(nsecs_t now) {
     // The HWC doesn't currently have a way to report additional latency.
     // Assume that whatever we submit now will appear right after the flip.
     // For a smart panel this might be 1.  This is expressed in frames,
@@ -862,9 +866,12 @@
     const uint32_t hwcLatency = 0;
 
     // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC).
-    return computeNextRefresh(hwcLatency);
+    return mThread->computeNextRefresh(hwcLatency, now);
 }
 
 } // namespace impl
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
index 0822266..6fb5654 100644
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -36,7 +36,7 @@
     public:
         Callback() = default;
         virtual ~Callback();
-        virtual void onDispSyncEvent(nsecs_t when) = 0;
+        virtual void onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) = 0;
 
     protected:
         Callback(Callback const&) = delete;
@@ -49,18 +49,18 @@
     virtual void reset() = 0;
     virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
     virtual void beginResync() = 0;
-    virtual bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) = 0;
+    virtual bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                                 bool* periodFlushed) = 0;
     virtual void endResync() = 0;
     virtual void setPeriod(nsecs_t period) = 0;
     virtual nsecs_t getPeriod() = 0;
-    virtual void setRefreshSkipCount(int count) = 0;
     virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback,
                                       nsecs_t lastCallbackTime) = 0;
     virtual status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) = 0;
     virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0;
-    virtual nsecs_t computeNextRefresh(int periodOffset) const = 0;
+    virtual nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const = 0;
     virtual void setIgnorePresentFences(bool ignore) = 0;
-    virtual nsecs_t expectedPresentTime() = 0;
+    virtual nsecs_t expectedPresentTime(nsecs_t now) = 0;
 
     virtual void dump(std::string& result) const = 0;
 
@@ -88,11 +88,10 @@
 // needed.
 class DispSync : public android::DispSync {
 public:
-    explicit DispSync(const char* name);
+    // hasSyncFramework specifies whether the platform supports present fences.
+    DispSync(const char* name, bool hasSyncFramework);
     ~DispSync() override;
 
-    void init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset);
-
     // reset clears the resync samples and error value.
     void reset() override;
 
@@ -127,7 +126,8 @@
     // down the DispSync model, and false otherwise.
     // periodFlushed will be set to true if mPendingPeriod is flushed to
     // mIntendedPeriod, and false otherwise.
-    bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) override;
+    bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                         bool* periodFlushed) override;
     void endResync() override;
 
     // The setPeriod method sets the vsync event model's period to a specific
@@ -138,12 +138,6 @@
     // The getPeriod method returns the current vsync period.
     nsecs_t getPeriod() override;
 
-    // setRefreshSkipCount specifies an additional number of refresh
-    // cycles to skip.  For example, on a 60Hz display, a skip count of 1
-    // will result in events happening at 30Hz.  Default is zero.  The idea
-    // is to sacrifice smoothness for battery life.
-    void setRefreshSkipCount(int count) override;
-
     // addEventListener registers a callback to be called repeatedly at the
     // given phase offset from the hardware vsync events.  The callback is
     // called from a separate thread and it should return reasonably quickly
@@ -173,7 +167,7 @@
     // The periodOffset value can be used to move forward or backward; an
     // offset of zero is the next refresh, -1 is the previous refresh, 1 is
     // the refresh after next. etc.
-    nsecs_t computeNextRefresh(int periodOffset) const override;
+    nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const override;
 
     // In certain situations the present fences aren't a good indicator of vsync
     // time, e.g. when vr flinger is active, or simply aren't available,
@@ -184,7 +178,7 @@
     void setIgnorePresentFences(bool ignore) override;
 
     // Determine the expected present time when a buffer acquired now will be displayed.
-    nsecs_t expectedPresentTime();
+    nsecs_t expectedPresentTime(nsecs_t now);
 
     // dump appends human-readable debug info to the result string.
     void dump(std::string& result) const override;
@@ -252,18 +246,12 @@
     std::shared_ptr<FenceTime> mPresentFences[NUM_PRESENT_SAMPLES]{FenceTime::NO_FENCE};
     size_t mPresentSampleOffset;
 
-    int mRefreshSkipCount;
-
     // mThread is the thread from which all the callbacks are called.
     sp<DispSyncThread> mThread;
 
     // mMutex is used to protect access to all member variables.
     mutable Mutex mMutex;
 
-    // This is the offset from the present fence timestamps to the corresponding
-    // vsync event.
-    int64_t mPresentTimeOffset;
-
     // Ignore present (retire) fences if the device doesn't have support for the
     // sync framework
     bool mIgnorePresentFences;
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 5faf46e..8752b66 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "DispSyncSource.h"
@@ -26,24 +30,20 @@
 #include "EventThread.h"
 
 namespace android {
+using base::StringAppendF;
 
-DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset,
-                               nsecs_t offsetThresholdForNextVsync, bool traceVsync,
+DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
                                const char* name)
       : mName(name),
+        mValue(base::StringPrintf("VSYNC-%s", name), 0),
         mTraceVsync(traceVsync),
         mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
-        mVsyncEventLabel(base::StringPrintf("VSYNC-%s", name)),
-        mVsyncOffsetLabel(base::StringPrintf("VsyncOffset-%s", name)),
-        mVsyncNegativeOffsetLabel(base::StringPrintf("VsyncNegativeOffset-%s", name)),
         mDispSync(dispSync),
-        mPhaseOffset(phaseOffset),
-        mOffsetThresholdForNextVsync(offsetThresholdForNextVsync) {}
+        mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset) {}
 
 void DispSyncSource::setVSyncEnabled(bool enable) {
     std::lock_guard lock(mVsyncMutex);
     if (enable) {
-        tracePhaseOffset();
         status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
                                                    static_cast<DispSync::Callback*>(this),
                                                    mLastCallbackTime);
@@ -70,10 +70,6 @@
 void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) {
     std::lock_guard lock(mVsyncMutex);
     const nsecs_t period = mDispSync->getPeriod();
-    // Check if offset should be handled as negative
-    if (phaseOffset >= mOffsetThresholdForNextVsync) {
-        phaseOffset -= period;
-    }
 
     // Normalize phaseOffset to [-period, period)
     const int numPeriods = phaseOffset / period;
@@ -83,7 +79,6 @@
     }
 
     mPhaseOffset = phaseOffset;
-    tracePhaseOffset();
 
     // If we're not enabled, we don't need to mess with the listeners
     if (!mEnabled) {
@@ -97,7 +92,7 @@
     }
 }
 
-void DispSyncSource::onDispSyncEvent(nsecs_t when) {
+void DispSyncSource::onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) {
     VSyncSource::Callback* callback;
     {
         std::lock_guard lock(mCallbackMutex);
@@ -106,22 +101,20 @@
 
     if (mTraceVsync) {
         mValue = (mValue + 1) % 2;
-        ATRACE_INT(mVsyncEventLabel.c_str(), mValue);
     }
 
     if (callback != nullptr) {
-        callback->onVSyncEvent(when);
+        callback->onVSyncEvent(when, expectedVSyncTimestamp);
     }
 }
 
-void DispSyncSource::tracePhaseOffset() {
-    if (mPhaseOffset > 0) {
-        ATRACE_INT(mVsyncOffsetLabel.c_str(), mPhaseOffset);
-        ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), 0);
-    } else {
-        ATRACE_INT(mVsyncOffsetLabel.c_str(), 0);
-        ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), -mPhaseOffset);
-    }
+void DispSyncSource::dump(std::string& result) const {
+    std::lock_guard lock(mVsyncMutex);
+    StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled");
+    mDispSync->dump(result);
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 50560a5..2aee3f6 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -20,35 +20,33 @@
 
 #include "DispSync.h"
 #include "EventThread.h"
+#include "TracedOrdinal.h"
 
 namespace android {
 
 class DispSyncSource final : public VSyncSource, private DispSync::Callback {
 public:
-    DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, nsecs_t offsetThresholdForNextVsync,
-                   bool traceVsync, const char* name);
+    DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name);
 
     ~DispSyncSource() override = default;
 
     // The following methods are implementation of VSyncSource.
+    const char* getName() const override { return mName; }
     void setVSyncEnabled(bool enable) override;
     void setCallback(VSyncSource::Callback* callback) override;
     void setPhaseOffset(nsecs_t phaseOffset) override;
 
+    void dump(std::string&) const override;
+
 private:
     // The following method is the implementation of the DispSync::Callback.
-    virtual void onDispSyncEvent(nsecs_t when);
-
-    void tracePhaseOffset() REQUIRES(mVsyncMutex);
+    void onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) override;
 
     const char* const mName;
-    int mValue = 0;
+    TracedOrdinal<int> mValue;
 
     const bool mTraceVsync;
     const std::string mVsyncOnLabel;
-    const std::string mVsyncEventLabel;
-    const std::string mVsyncOffsetLabel;
-    const std::string mVsyncNegativeOffsetLabel;
     nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0;
 
     DispSync* mDispSync;
@@ -56,9 +54,8 @@
     std::mutex mCallbackMutex;
     VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
 
-    std::mutex mVsyncMutex;
-    nsecs_t mPhaseOffset GUARDED_BY(mVsyncMutex);
-    const nsecs_t mOffsetThresholdForNextVsync;
+    mutable std::mutex mVsyncMutex;
+    TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
     bool mEnabled GUARDED_BY(mVsyncMutex) = false;
 };
 
diff --git a/services/surfaceflinger/Scheduler/EventControlThread.cpp b/services/surfaceflinger/Scheduler/EventControlThread.cpp
index fb6cff5..7f9db9c 100644
--- a/services/surfaceflinger/Scheduler/EventControlThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventControlThread.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <pthread.h>
 #include <sched.h>
 #include <sys/resource.h>
@@ -31,7 +35,7 @@
 namespace impl {
 
 EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function)
-      : mSetVSyncEnabled(function) {
+      : mSetVSyncEnabled(std::move(function)) {
     pthread_setname_np(mThread.native_handle(), "EventControlThread");
 
     pid_t tid = pthread_gettid_np(mThread.native_handle());
@@ -73,3 +77,6 @@
 
 } // namespace impl
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 9d1f777..cee36a1 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <pthread.h>
@@ -36,6 +40,7 @@
 #include <utils/Trace.h>
 
 #include "EventThread.h"
+#include "HwcStrongTypes.h"
 
 using namespace std::chrono_literals;
 
@@ -74,8 +79,9 @@
                                 event.hotplug.connected ? "connected" : "disconnected");
         case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
             return StringPrintf("VSync{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
-                                ", count=%u}",
-                                event.header.displayId, event.vsync.count);
+                                ", count=%u, expectedVSyncTimestamp=%" PRId64 "}",
+                                event.header.displayId, event.vsync.count,
+                                event.vsync.expectedVSyncTimestamp);
         case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
             return StringPrintf("ConfigChanged{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
                                 ", configId=%u}",
@@ -94,17 +100,20 @@
 }
 
 DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t timestamp,
-                                      uint32_t count) {
+                                      uint32_t count, nsecs_t expectedVSyncTimestamp) {
     DisplayEventReceiver::Event event;
     event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
     event.vsync.count = count;
+    event.vsync.expectedVSyncTimestamp = expectedVSyncTimestamp;
     return event;
 }
 
-DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId, int32_t configId) {
+DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId,
+                                              HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
     DisplayEventReceiver::Event event;
     event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()};
-    event.config.configId = configId;
+    event.config.configId = configId.value();
+    event.config.vsyncPeriod = vsyncPeriod;
     return event;
 }
 
@@ -114,7 +123,7 @@
                                              ResyncCallback resyncCallback,
                                              ISurfaceComposer::ConfigChanged configChanged)
       : resyncCallback(std::move(resyncCallback)),
-        configChanged(configChanged),
+        mConfigChanged(configChanged),
         mEventThread(eventThread),
         mChannel(gui::BitTube::DefaultSize) {}
 
@@ -143,6 +152,11 @@
     mEventThread->requestNextVsync(this);
 }
 
+void EventThreadConnection::requestLatestConfig() {
+    ATRACE_NAME("requestLatestConfig");
+    mEventThread->requestLatestConfig(this);
+}
+
 status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
     ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
     return size < 0 ? status_t(size) : status_t(NO_ERROR);
@@ -154,23 +168,11 @@
 
 namespace impl {
 
-EventThread::EventThread(std::unique_ptr<VSyncSource> src,
-                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
-      : EventThread(nullptr, std::move(src), std::move(interceptVSyncsCallback), threadName) {}
-
-EventThread::EventThread(VSyncSource* src, InterceptVSyncsCallback interceptVSyncsCallback,
-                         const char* threadName)
-      : EventThread(src, nullptr, std::move(interceptVSyncsCallback), threadName) {}
-
-EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
-                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
-      : mVSyncSource(src),
-        mVSyncSourceUnique(std::move(uniqueSrc)),
+EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
+                         InterceptVSyncsCallback interceptVSyncsCallback)
+      : mVSyncSource(std::move(vsyncSource)),
         mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
-        mThreadName(threadName) {
-    if (src == nullptr) {
-        mVSyncSource = mVSyncSourceUnique.get();
-    }
+        mThreadName(mVSyncSource->getName()) {
     mVSyncSource->setCallback(this);
 
     mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
@@ -178,7 +180,7 @@
         threadMain(lock);
     });
 
-    pthread_setname_np(mThread.native_handle(), threadName);
+    pthread_setname_np(mThread.native_handle(), mThreadName);
 
     pid_t tid = pthread_gettid_np(mThread.native_handle());
 
@@ -267,6 +269,28 @@
     }
 }
 
+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) {
+                             return event.header.type ==
+                                     DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED;
+                         });
+
+    // If we didn't find a pending config change event, then push out the
+    // latest one we've ever seen.
+    if (pendingConfigChange == std::end(mPendingEvents)) {
+        mPendingEvents.push_back(mLastConfigChangeEvent);
+    }
+
+    mCondition.notify_all();
+}
+
 void EventThread::onScreenReleased() {
     std::lock_guard<std::mutex> lock(mMutex);
     if (!mVSyncState || mVSyncState->synthetic) {
@@ -287,11 +311,12 @@
     mCondition.notify_all();
 }
 
-void EventThread::onVSyncEvent(nsecs_t timestamp) {
+void EventThread::onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     LOG_FATAL_IF(!mVSyncState);
-    mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count));
+    mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
+                                       expectedVSyncTimestamp));
     mCondition.notify_all();
 }
 
@@ -302,13 +327,19 @@
     mCondition.notify_all();
 }
 
-void EventThread::onConfigChanged(PhysicalDisplayId displayId, int32_t configId) {
+void EventThread::onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
+                                  nsecs_t vsyncPeriod) {
     std::lock_guard<std::mutex> lock(mMutex);
 
-    mPendingEvents.push_back(makeConfigChanged(displayId, configId));
+    mPendingEvents.push_back(makeConfigChanged(displayId, configId, vsyncPeriod));
     mCondition.notify_all();
 }
 
+size_t EventThread::getEventThreadConnectionCount() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mDisplayEventConnections.size();
+}
+
 void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
     DisplayEventConsumers consumers;
 
@@ -335,6 +366,9 @@
                         mInterceptVSyncsCallback(event->header.timestamp);
                     }
                     break;
+                case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+                    mLastConfigChangeEvent = *event;
+                    break;
             }
         }
 
@@ -347,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);
                 }
 
@@ -389,14 +427,27 @@
         } else {
             // Generate a fake VSYNC after a long timeout in case the driver stalls. When the
             // display is off, keep feeding clients at 60 Hz.
-            const auto timeout = mState == State::SyntheticVSync ? 16ms : 1000ms;
+            const std::chrono::nanoseconds timeout =
+                    mState == State::SyntheticVSync ? 16ms : 1000ms;
             if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {
-                ALOGW_IF(mState == State::VSync, "Faking VSYNC due to driver stall");
+                if (mState == State::VSync) {
+                    ALOGW("Faking VSYNC due to driver stall for thread %s", mThreadName);
+                    std::string debugInfo = "VsyncSource debug info:\n";
+                    mVSyncSource->dump(debugInfo);
+                    // Log the debug info line-by-line to avoid logcat overflow
+                    auto pos = debugInfo.find('\n');
+                    while (pos != std::string::npos) {
+                        ALOGW("%s", debugInfo.substr(0, pos).c_str());
+                        debugInfo = debugInfo.substr(pos + 1);
+                        pos = debugInfo.find('\n');
+                    }
+                }
 
                 LOG_FATAL_IF(!mVSyncState);
-                mPendingEvents.push_back(makeVSync(mVSyncState->displayId,
-                                                   systemTime(SYSTEM_TIME_MONOTONIC),
-                                                   ++mVSyncState->count));
+                const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
+                const auto expectedVSyncTime = now + timeout.count();
+                mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,
+                                                   ++mVSyncState->count, expectedVSyncTime));
             }
         }
     }
@@ -408,8 +459,11 @@
         case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
             return true;
 
-        case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
-            return connection->configChanged == ISurfaceComposer::eConfigChangedDispatch;
+        case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: {
+            const bool oneTimeDispatch = connection->mForcedConfigChangeDispatch;
+            return oneTimeDispatch ||
+                    connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch;
+        }
 
         case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
             switch (connection->vsyncRequest) {
@@ -489,3 +543,6 @@
 
 } // namespace impl
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index dd23b88..64acbd7 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -16,7 +16,12 @@
 
 #pragma once
 
+#include <android-base/thread_annotations.h>
+#include <gui/DisplayEventReceiver.h>
+#include <gui/IDisplayEventConnection.h>
+#include <private/gui/BitTube.h>
 #include <sys/types.h>
+#include <utils/Errors.h>
 
 #include <condition_variable>
 #include <cstdint>
@@ -26,13 +31,7 @@
 #include <thread>
 #include <vector>
 
-#include <android-base/thread_annotations.h>
-
-#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
-#include <private/gui/BitTube.h>
-
-#include <utils/Errors.h>
+#include "HwcStrongTypes.h"
 
 // ---------------------------------------------------------------------------
 namespace android {
@@ -58,13 +57,17 @@
     class Callback {
     public:
         virtual ~Callback() {}
-        virtual void onVSyncEvent(nsecs_t when) = 0;
+        virtual void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) = 0;
     };
 
     virtual ~VSyncSource() {}
+
+    virtual const char* getName() const = 0;
     virtual void setVSyncEnabled(bool enable) = 0;
     virtual void setCallback(Callback* callback) = 0;
     virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+
+    virtual void dump(std::string& result) const = 0;
 };
 
 class EventThreadConnection : public BnDisplayEventConnection {
@@ -78,12 +81,19 @@
     status_t stealReceiveChannel(gui::BitTube* outChannel) override;
     status_t setVsyncRate(uint32_t rate) override;
     void requestNextVsync() override; // asynchronous
+    void requestLatestConfig() override; // asynchronous
 
     // Called in response to requestNextVsync.
     const ResyncCallback resyncCallback;
 
     VSyncRequest vsyncRequest = VSyncRequest::None;
-    const ISurfaceComposer::ConfigChanged configChanged;
+    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.
+    bool mForcedConfigChangeDispatch = false;
 
 private:
     virtual void onFirstRef();
@@ -107,7 +117,8 @@
     virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
 
     // called when SF changes the active config and apps needs to be notified about the change
-    virtual void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) = 0;
+    virtual void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
+                                 nsecs_t vsyncPeriod) = 0;
 
     virtual void dump(std::string& result) const = 0;
 
@@ -118,6 +129,13 @@
     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(const sp<EventThreadConnection>& connection) = 0;
+
+    // Retrieves the number of event connections tracked by this EventThread.
+    virtual size_t getEventThreadConnectionCount() = 0;
 };
 
 namespace impl {
@@ -126,9 +144,7 @@
 public:
     using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
 
-    // TODO(b/128863962): Once the Scheduler is complete this constructor will become obsolete.
-    EventThread(VSyncSource*, InterceptVSyncsCallback, const char* threadName);
-    EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback, const char* threadName);
+    EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback);
     ~EventThread();
 
     sp<EventThreadConnection> createEventConnection(
@@ -137,6 +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(const sp<EventThreadConnection>& connection) override;
 
     // called before the screen is turned off from main thread
     void onScreenReleased() override;
@@ -146,21 +163,20 @@
 
     void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
 
-    void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) override;
+    void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
+                         nsecs_t vsyncPeriod) override;
 
     void dump(std::string& result) const override;
 
     void setPhaseOffset(nsecs_t phaseOffset) override;
 
+    size_t getEventThreadConnectionCount() override;
+
 private:
     friend EventThreadTest;
 
     using DisplayEventConsumers = std::vector<sp<EventThreadConnection>>;
 
-    // TODO(b/128863962): Once the Scheduler is complete this constructor will become obsolete.
-    EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
-                InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
-
     void threadMain(std::unique_lock<std::mutex>& lock) REQUIRES(mMutex);
 
     bool shouldConsumeEvent(const DisplayEventReceiver::Event& event,
@@ -172,11 +188,9 @@
             REQUIRES(mMutex);
 
     // Implements VSyncSource::Callback
-    void onVSyncEvent(nsecs_t timestamp) override;
+    void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp) override;
 
-    // TODO(b/128863962): Once the Scheduler is complete this pointer will become obsolete.
-    VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
-    std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr;
+    const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
 
     const InterceptVSyncsCallback mInterceptVSyncsCallback;
     const char* const mThreadName;
@@ -187,6 +201,7 @@
 
     std::vector<wp<EventThreadConnection>> mDisplayEventConnections GUARDED_BY(mMutex);
     std::deque<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
+    DisplayEventReceiver::Event mLastConfigChangeEvent GUARDED_BY(mMutex);
 
     // VSYNC state of connected display.
     struct VSyncState {
diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
similarity index 66%
copy from services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp
copy to services/surfaceflinger/Scheduler/HwcStrongTypes.h
index a7fd667..8ba4f20 100644
--- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp
+++ b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-#include "mock/gui/MockGraphicBufferProducer.h"
+#pragma once
+
+#include "StrongTyping.h"
 
 namespace android {
-namespace mock {
 
-// Explicit default instantiation is recommended.
-GraphicBufferProducer::GraphicBufferProducer() = default;
-GraphicBufferProducer::~GraphicBufferProducer() = default;
+// Strong types for the different indexes as they are referring to a different base.
+using HwcConfigIndexType = StrongTyping<int, struct HwcConfigIndexTypeTag, Compare, Add, Hash>;
 
-} // namespace mock
 } // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index 90609af..975c9db 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -35,20 +35,21 @@
         mCallback = callback;
     }
 
-    void onInjectSyncEvent(nsecs_t when) {
+    void onInjectSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) {
         std::lock_guard<std::mutex> lock(mCallbackMutex);
         if (mCallback) {
-            mCallback->onVSyncEvent(when);
+            mCallback->onVSyncEvent(when, expectedVSyncTimestamp);
         }
     }
 
+    const char* getName() const override { return "inject"; }
     void setVSyncEnabled(bool) override {}
     void setPhaseOffset(nsecs_t) override {}
-    void pauseVsyncCallback(bool) {}
+    void dump(std::string&) const override {}
 
 private:
     std::mutex mCallbackMutex;
     VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
 };
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index f80c233..ecf2597 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -14,179 +14,173 @@
  * limitations under the License.
  */
 
+#undef LOG_TAG
+#define LOG_TAG "LayerHistory"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "LayerHistory.h"
 
-#include <cinttypes>
-#include <cstdint>
-#include <limits>
-#include <numeric>
-#include <string>
-#include <unordered_map>
-
 #include <cutils/properties.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
 #include <utils/Trace.h>
 
+#include <algorithm>
+#include <cmath>
+#include <string>
+#include <utility>
+
+#include "../Layer.h"
+#include "LayerInfo.h"
 #include "SchedulerUtils.h"
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler::impl {
 
-std::atomic<int64_t> LayerHistory::sNextId = 0;
+namespace {
 
-LayerHistory::LayerHistory() {
+bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
+    if (layer.getFrameRateForLayerTree().rate > 0) {
+        return layer.isVisible();
+    }
+    return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
+}
+
+bool traceEnabled() {
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sf.layer_history_trace", value, "0");
-    mTraceEnabled = bool(atoi(value));
+    return atoi(value);
 }
 
+bool useFrameRatePriority() {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.sf.use_frame_rate_priority", value, "1");
+    return atoi(value);
+}
+
+void trace(const wp<Layer>& weak, int fps) {
+    const auto layer = weak.promote();
+    if (!layer) return;
+
+    const auto& name = layer->getName();
+    const auto tag = "LFPS " + name;
+    ATRACE_INT(tag.c_str(), fps);
+    ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
+}
+} // namespace
+
+LayerHistory::LayerHistory()
+      : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
 LayerHistory::~LayerHistory() = default;
 
-std::unique_ptr<LayerHistory::LayerHandle> LayerHistory::createLayer(const std::string name,
-                                                                     float minRefreshRate,
-                                                                     float maxRefreshRate) {
-    const int64_t id = sNextId++;
-
+void LayerHistory::registerLayer(Layer* layer, float lowRefreshRate, float highRefreshRate,
+                                 LayerVoteType /*type*/) {
+    auto info = std::make_unique<LayerInfo>(lowRefreshRate, highRefreshRate);
     std::lock_guard lock(mLock);
-    mInactiveLayerInfos.emplace(id,
-                                std::make_shared<LayerInfo>(name, minRefreshRate, maxRefreshRate));
-    return std::make_unique<LayerHistory::LayerHandle>(*this, id);
+    mLayerInfos.emplace_back(layer, std::move(info));
 }
 
-void LayerHistory::destroyLayer(const int64_t id) {
+void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
+                          LayerUpdateType /*updateType*/) {
     std::lock_guard lock(mLock);
-    auto it = mActiveLayerInfos.find(id);
-    if (it != mActiveLayerInfos.end()) {
-        mActiveLayerInfos.erase(it);
-    }
 
-    it = mInactiveLayerInfos.find(id);
-    if (it != mInactiveLayerInfos.end()) {
-        mInactiveLayerInfos.erase(it);
+    const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
+                                 [layer](const auto& pair) { return pair.first == layer; });
+    LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+
+    const auto& info = it->second;
+    info->setLastPresentTime(presentTime, now);
+
+    // Activate layer if inactive.
+    if (const auto end = activeLayers().end(); it >= end) {
+        std::iter_swap(it, end);
+        mActiveLayersEnd++;
     }
 }
 
-void LayerHistory::insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime,
-                          bool isHdr) {
-    std::shared_ptr<LayerInfo> layerInfo;
-    {
-        std::lock_guard lock(mLock);
-        auto layerInfoIterator = mInactiveLayerInfos.find(layerHandle->mId);
-        if (layerInfoIterator != mInactiveLayerInfos.end()) {
-            layerInfo = layerInfoIterator->second;
-            mInactiveLayerInfos.erase(layerInfoIterator);
-            mActiveLayerInfos.insert({layerHandle->mId, layerInfo});
+LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
+    ATRACE_CALL();
+    std::lock_guard lock(mLock);
+
+    partitionLayers(now);
+
+    LayerHistory::Summary summary;
+    for (const auto& [weakLayer, info] : activeLayers()) {
+        const bool recent = info->isRecentlyActive(now);
+        auto layer = weakLayer.promote();
+        // Only use the layer if the reference still exists.
+        if (layer || CC_UNLIKELY(mTraceEnabled)) {
+            const auto layerFocused =
+                    Layer::isLayerFocusedBasedOnPriority(layer->getFrameRateSelectionPriority());
+            // Check if frame rate was set on layer.
+            const auto frameRate = layer->getFrameRateForLayerTree();
+            if (frameRate.rate > 0.f) {
+                const auto voteType = [&]() {
+                    switch (frameRate.type) {
+                        case Layer::FrameRateCompatibility::Default:
+                            return LayerVoteType::ExplicitDefault;
+                        case Layer::FrameRateCompatibility::ExactOrMultiple:
+                            return LayerVoteType::ExplicitExactOrMultiple;
+                        case Layer::FrameRateCompatibility::NoVote:
+                            return LayerVoteType::NoVote;
+                    }
+                }();
+                summary.push_back({layer->getName(), voteType, frameRate.rate, /* weight */ 1.0f,
+                                   layerFocused});
+            } else if (recent) {
+                summary.push_back({layer->getName(), LayerVoteType::Heuristic,
+                                   info->getRefreshRate(now),
+                                   /* weight */ 1.0f, layerFocused});
+            }
+
+            if (CC_UNLIKELY(mTraceEnabled)) {
+                trace(weakLayer, round<int>(frameRate.rate));
+            }
+        }
+    }
+
+    return summary;
+}
+
+void LayerHistory::partitionLayers(nsecs_t now) {
+    const nsecs_t threshold = getActiveLayerThreshold(now);
+
+    // Collect expired and inactive layers after active layers.
+    size_t i = 0;
+    while (i < mActiveLayersEnd) {
+        auto& [weak, info] = mLayerInfos[i];
+        if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
+            i++;
+            continue;
+        }
+
+        if (CC_UNLIKELY(mTraceEnabled)) {
+            trace(weak, 0);
+        }
+
+        info->clearHistory();
+        std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
+    }
+
+    // Collect expired layers after inactive layers.
+    size_t end = mLayerInfos.size();
+    while (i < end) {
+        if (mLayerInfos[i].first.promote()) {
+            i++;
         } else {
-            layerInfoIterator = mActiveLayerInfos.find(layerHandle->mId);
-            if (layerInfoIterator != mActiveLayerInfos.end()) {
-                layerInfo = layerInfoIterator->second;
-            } else {
-                ALOGW("Inserting information about layer that is not registered: %" PRId64,
-                      layerHandle->mId);
-                return;
-            }
+            std::swap(mLayerInfos[i], mLayerInfos[--end]);
         }
     }
-    layerInfo->setLastPresentTime(presentTime);
-    layerInfo->setHDRContent(isHdr);
+
+    mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
 }
 
-void LayerHistory::setVisibility(const std::unique_ptr<LayerHandle>& layerHandle, bool visible) {
-    std::shared_ptr<LayerInfo> layerInfo;
-    {
-        std::lock_guard lock(mLock);
-        auto layerInfoIterator = mInactiveLayerInfos.find(layerHandle->mId);
-        if (layerInfoIterator != mInactiveLayerInfos.end()) {
-            layerInfo = layerInfoIterator->second;
-            if (visible) {
-                mInactiveLayerInfos.erase(layerInfoIterator);
-                mActiveLayerInfos.insert({layerHandle->mId, layerInfo});
-            }
-        } else {
-            layerInfoIterator = mActiveLayerInfos.find(layerHandle->mId);
-            if (layerInfoIterator != mActiveLayerInfos.end()) {
-                layerInfo = layerInfoIterator->second;
-            } else {
-                ALOGW("Inserting information about layer that is not registered: %" PRId64,
-                      layerHandle->mId);
-                return;
-            }
-        }
-    }
-    layerInfo->setVisibility(visible);
-}
-
-std::pair<float, bool> LayerHistory::getDesiredRefreshRateAndHDR() {
-    bool isHDR = false;
-    float newRefreshRate = 0.f;
+void LayerHistory::clear() {
     std::lock_guard lock(mLock);
 
-    removeIrrelevantLayers();
-
-    // Iterate through all layers that have been recently updated, and find the max refresh rate.
-    for (const auto& [layerId, layerInfo] : mActiveLayerInfos) {
-        const float layerRefreshRate = layerInfo->getDesiredRefreshRate();
-        if (mTraceEnabled) {
-            // Store the refresh rate in traces for easy debugging.
-            std::string layerName = "LFPS " + layerInfo->getName();
-            ATRACE_INT(layerName.c_str(), std::round(layerRefreshRate));
-            ALOGD("%s: %f", layerName.c_str(), std::round(layerRefreshRate));
-        }
-        if (layerInfo->isRecentlyActive() && layerRefreshRate > newRefreshRate) {
-            newRefreshRate = layerRefreshRate;
-        }
-        isHDR |= layerInfo->getHDRContent();
-    }
-    if (mTraceEnabled) {
-        ALOGD("LayerHistory DesiredRefreshRate: %.2f", newRefreshRate);
+    for (const auto& [layer, info] : activeLayers()) {
+        info->clearHistory();
     }
 
-    return {newRefreshRate, isHDR};
+    mActiveLayersEnd = 0;
 }
-
-void LayerHistory::removeIrrelevantLayers() {
-    const int64_t obsoleteEpsilon = systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count();
-    // Iterator pointing to first element in map
-    auto it = mActiveLayerInfos.begin();
-    while (it != mActiveLayerInfos.end()) {
-        // If last updated was before the obsolete time, remove it.
-        // Keep HDR layer around as long as they are visible.
-        if (!it->second->isVisible() ||
-            (!it->second->getHDRContent() && it->second->getLastUpdatedTime() < obsoleteEpsilon)) {
-            // erase() function returns the iterator of the next
-            // to last deleted element.
-            if (mTraceEnabled) {
-                ALOGD("Layer %s obsolete", it->second->getName().c_str());
-                // Make sure to update systrace to indicate that the layer was erased.
-                std::string layerName = "LFPS " + it->second->getName();
-                ATRACE_INT(layerName.c_str(), 0);
-            }
-            auto id = it->first;
-            auto layerInfo = it->second;
-            layerInfo->clearHistory();
-            mInactiveLayerInfos.insert({id, layerInfo});
-            it = mActiveLayerInfos.erase(it);
-        } else {
-            ++it;
-        }
-    }
-}
-
-void LayerHistory::clearHistory() {
-    std::lock_guard lock(mLock);
-
-    auto it = mActiveLayerInfos.begin();
-    while (it != mActiveLayerInfos.end()) {
-        auto id = it->first;
-        auto layerInfo = it->second;
-        layerInfo->clearHistory();
-        mInactiveLayerInfos.insert({id, layerInfo});
-        it = mActiveLayerInfos.erase(it);
-    }
-}
-
-} // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 5598cc1..228b8a0 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -16,76 +16,187 @@
 
 #pragma once
 
-#include <array>
-#include <cinttypes>
-#include <cstdint>
-#include <numeric>
-#include <string>
-#include <unordered_map>
-
+#include <android-base/thread_annotations.h>
+#include <utils/RefBase.h>
 #include <utils/Timers.h>
 
-#include "LayerInfo.h"
-#include "SchedulerUtils.h"
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+#include "RefreshRateConfigs.h"
 
 namespace android {
+
+class Layer;
+class TestableScheduler;
+
 namespace scheduler {
 
-/*
- * This class represents information about layers that are considered current. We keep an
- * unordered map between layer name and LayerInfo.
- */
+class LayerHistoryTest;
+class LayerHistoryTestV2;
+class LayerInfo;
+class LayerInfoV2;
+
 class LayerHistory {
 public:
-    // Handle for each layer we keep track of.
-    class LayerHandle {
-    public:
-        LayerHandle(LayerHistory& lh, int64_t id) : mId(id), mLayerHistory(lh) {}
-        ~LayerHandle() { mLayerHistory.destroyLayer(mId); }
+    using LayerVoteType = RefreshRateConfigs::LayerVoteType;
 
-        const int64_t mId;
+    virtual ~LayerHistory() = default;
 
-    private:
-        LayerHistory& mLayerHistory;
+    // Layers are unregistered when the weak reference expires.
+    virtual void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
+                               LayerVoteType type) = 0;
+
+    // Sets the display size. Client is responsible for synchronization.
+    virtual void setDisplayArea(uint32_t displayArea) = 0;
+
+    // Sets whether a config change is pending to be applied
+    virtual void setConfigChangePending(bool pending) = 0;
+
+    // Represents which layer activity is recorded
+    enum class LayerUpdateType {
+        Buffer,       // a new buffer queued
+        AnimationTX,  // a new transaction with eAnimation flag set
+        SetFrameRate, // setFrameRate API was called
     };
 
-    LayerHistory();
-    ~LayerHistory();
+    // Marks the layer as active, and records the given state to its history.
+    virtual void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) = 0;
 
-    // When the layer is first created, register it.
-    std::unique_ptr<LayerHandle> createLayer(const std::string name, float minRefreshRate,
-                                             float maxRefreshRate);
+    using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
 
-    // Method for inserting layers and their requested present time into the unordered map.
-    void insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime, bool isHdr);
-    // Method for setting layer visibility
-    void setVisibility(const std::unique_ptr<LayerHandle>& layerHandle, bool visible);
+    // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
+    virtual Summary summarize(nsecs_t now) = 0;
 
-    // Returns the desired refresh rate, which is a max refresh rate of all the current
-    // layers. See go/content-fps-detection-in-scheduler for more information.
-    std::pair<float, bool> getDesiredRefreshRateAndHDR();
-
-    // Clears all layer history.
-    void clearHistory();
-
-    // Removes the handle and the object from the map.
-    void destroyLayer(const int64_t id);
-
-private:
-    // Removes the layers that have been idle for a given amount of time from mLayerInfos.
-    void removeIrrelevantLayers() REQUIRES(mLock);
-
-    // Information about currently active layers.
-    std::mutex mLock;
-    std::unordered_map<int64_t, std::shared_ptr<LayerInfo>> mActiveLayerInfos GUARDED_BY(mLock);
-    std::unordered_map<int64_t, std::shared_ptr<LayerInfo>> mInactiveLayerInfos GUARDED_BY(mLock);
-
-    // Each layer has it's own ID. This variable keeps track of the count.
-    static std::atomic<int64_t> sNextId;
-
-    // Flag whether to log layer FPS in systrace
-    bool mTraceEnabled = false;
+    virtual void clear() = 0;
 };
 
+namespace impl {
+// Records per-layer history of scheduling-related information (primarily present time),
+// heuristically categorizes layers as active or inactive, and summarizes stats about
+// active layers (primarily maximum refresh rate). See go/content-fps-detection-in-scheduler.
+class LayerHistory : public android::scheduler::LayerHistory {
+public:
+    LayerHistory();
+    virtual ~LayerHistory();
+
+    // Layers are unregistered when the weak reference expires.
+    void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
+                       LayerVoteType type) override;
+
+    void setDisplayArea(uint32_t /*displayArea*/) override {}
+
+    void setConfigChangePending(bool /*pending*/) override {}
+
+    // Marks the layer as active, and records the given state to its history.
+    void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
+
+    // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
+    android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override;
+
+    void clear() override;
+
+private:
+    friend class android::scheduler::LayerHistoryTest;
+    friend TestableScheduler;
+
+    using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
+    using LayerInfos = std::vector<LayerPair>;
+
+    struct ActiveLayers {
+        LayerInfos& infos;
+        const size_t index;
+
+        auto begin() { return infos.begin(); }
+        auto end() { return begin() + static_cast<long>(index); }
+    };
+
+    ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
+
+    // Iterates over layers in a single pass, swapping pairs such that active layers precede
+    // inactive layers, and inactive layers precede expired layers. Removes expired layers by
+    // truncating after inactive layers.
+    void partitionLayers(nsecs_t now) REQUIRES(mLock);
+
+    mutable std::mutex mLock;
+
+    // Partitioned such that active layers precede inactive layers. For fast lookup, the few active
+    // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache.
+    LayerInfos mLayerInfos GUARDED_BY(mLock);
+    size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
+
+    // Whether to emit systrace output and debug logs.
+    const bool mTraceEnabled;
+
+    // Whether to use priority sent from WindowManager to determine the relevancy of the layer.
+    const bool mUseFrameRatePriority;
+};
+
+class LayerHistoryV2 : public android::scheduler::LayerHistory {
+public:
+    LayerHistoryV2(const scheduler::RefreshRateConfigs&);
+    virtual ~LayerHistoryV2();
+
+    // Layers are unregistered when the weak reference expires.
+    void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
+                       LayerVoteType type) override;
+
+    // Sets the display size. Client is responsible for synchronization.
+    void setDisplayArea(uint32_t displayArea) override { mDisplayArea = displayArea; }
+
+    void setConfigChangePending(bool pending) override { mConfigChangePending = pending; }
+
+    // Marks the layer as active, and records the given state to its history.
+    void record(Layer*, nsecs_t presentTime, nsecs_t now, LayerUpdateType updateType) override;
+
+    // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
+    android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override;
+
+    void clear() override;
+
+private:
+    friend android::scheduler::LayerHistoryTestV2;
+    friend TestableScheduler;
+
+    using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfoV2>>;
+    using LayerInfos = std::vector<LayerPair>;
+
+    struct ActiveLayers {
+        LayerInfos& infos;
+        const size_t index;
+
+        auto begin() { return infos.begin(); }
+        auto end() { return begin() + static_cast<long>(index); }
+    };
+
+    ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
+
+    // Iterates over layers in a single pass, swapping pairs such that active layers precede
+    // inactive layers, and inactive layers precede expired layers. Removes expired layers by
+    // truncating after inactive layers.
+    void partitionLayers(nsecs_t now) REQUIRES(mLock);
+
+    mutable std::mutex mLock;
+
+    // Partitioned such that active layers precede inactive layers. For fast lookup, the few active
+    // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache.
+    LayerInfos mLayerInfos GUARDED_BY(mLock);
+    size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
+
+    uint32_t mDisplayArea = 0;
+
+    // Whether to emit systrace output and debug logs.
+    const bool mTraceEnabled;
+
+    // Whether to use priority sent from WindowManager to determine the relevancy of the layer.
+    const bool mUseFrameRatePriority;
+
+    // Whether a config change is in progress or not
+    std::atomic<bool> mConfigChangePending = false;
+};
+
+} // namespace impl
 } // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
new file mode 100644
index 0000000..aa04bd7
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -0,0 +1,217 @@
+/*
+ * 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 "LayerHistoryV2"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "LayerHistory.h"
+
+#include <cutils/properties.h>
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <cmath>
+#include <string>
+#include <utility>
+
+#include "../Layer.h"
+#include "SchedulerUtils.h"
+
+#include "LayerInfoV2.h"
+
+namespace android::scheduler::impl {
+
+namespace {
+
+bool isLayerActive(const Layer& layer, const LayerInfoV2& info, nsecs_t threshold) {
+    // Layers with an explicit vote are always kept active
+    if (layer.getFrameRateForLayerTree().rate > 0) {
+        return true;
+    }
+
+    return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
+}
+
+bool traceEnabled() {
+    return property_get_bool("debug.sf.layer_history_trace", false);
+}
+
+bool useFrameRatePriority() {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.sf.use_frame_rate_priority", value, "1");
+    return atoi(value);
+}
+
+void trace(const wp<Layer>& weak, const LayerInfoV2& info, LayerHistory::LayerVoteType type,
+           int fps) {
+    const auto layer = weak.promote();
+    if (!layer) return;
+
+    const auto traceType = [&](LayerHistory::LayerVoteType checkedType, int value) {
+        ATRACE_INT(info.getTraceTag(checkedType), type == checkedType ? value : 0);
+    };
+
+    traceType(LayerHistory::LayerVoteType::NoVote, 1);
+    traceType(LayerHistory::LayerVoteType::Heuristic, fps);
+    traceType(LayerHistory::LayerVoteType::ExplicitDefault, fps);
+    traceType(LayerHistory::LayerVoteType::ExplicitExactOrMultiple, fps);
+    traceType(LayerHistory::LayerVoteType::Min, 1);
+    traceType(LayerHistory::LayerVoteType::Max, 1);
+
+    ALOGD("%s: %s @ %d Hz", __FUNCTION__, layer->getName().c_str(), fps);
+}
+} // namespace
+
+LayerHistoryV2::LayerHistoryV2(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+      : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {
+    LayerInfoV2::setTraceEnabled(mTraceEnabled);
+    LayerInfoV2::setRefreshRateConfigs(refreshRateConfigs);
+}
+
+LayerHistoryV2::~LayerHistoryV2() = default;
+
+void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate,
+                                   LayerVoteType type) {
+    const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate);
+    auto info = std::make_unique<LayerInfoV2>(layer->getName(), highRefreshRatePeriod, type);
+    std::lock_guard lock(mLock);
+    mLayerInfos.emplace_back(layer, std::move(info));
+}
+
+void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
+                            LayerUpdateType updateType) {
+    std::lock_guard lock(mLock);
+
+    const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
+                                 [layer](const auto& pair) { return pair.first == layer; });
+    LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+
+    const auto& info = it->second;
+    info->setLastPresentTime(presentTime, now, updateType, mConfigChangePending);
+
+    // Activate layer if inactive.
+    if (const auto end = activeLayers().end(); it >= end) {
+        std::iter_swap(it, end);
+        mActiveLayersEnd++;
+    }
+}
+
+LayerHistoryV2::Summary LayerHistoryV2::summarize(nsecs_t now) {
+    LayerHistory::Summary summary;
+
+    std::lock_guard lock(mLock);
+
+    partitionLayers(now);
+
+    for (const auto& [layer, info] : activeLayers()) {
+        const auto strong = layer.promote();
+        if (!strong) {
+            continue;
+        }
+
+        const auto frameRateSelectionPriority = strong->getFrameRateSelectionPriority();
+        const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
+        ALOGV("%s has priority: %d %s focused", strong->getName().c_str(),
+              frameRateSelectionPriority, layerFocused ? "" : "not");
+
+        const auto [type, refreshRate] = info->getRefreshRate(now);
+        // Skip NoVote layer as those don't have any requirements
+        if (type == LayerHistory::LayerVoteType::NoVote) {
+            continue;
+        }
+
+        // Compute the layer's position on the screen
+        const Rect bounds = Rect(strong->getBounds());
+        const ui::Transform transform = strong->getTransform();
+        constexpr bool roundOutwards = true;
+        Rect transformed = transform.transform(bounds, roundOutwards);
+
+        const float layerArea = transformed.getWidth() * transformed.getHeight();
+        float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
+        summary.push_back({strong->getName(), type, refreshRate, weight, layerFocused});
+
+        if (CC_UNLIKELY(mTraceEnabled)) {
+            trace(layer, *info, type, static_cast<int>(std::round(refreshRate)));
+        }
+    }
+
+    return summary;
+}
+
+void LayerHistoryV2::partitionLayers(nsecs_t now) {
+    const nsecs_t threshold = getActiveLayerThreshold(now);
+
+    // Collect expired and inactive layers after active layers.
+    size_t i = 0;
+    while (i < mActiveLayersEnd) {
+        auto& [weak, info] = mLayerInfos[i];
+        if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
+            i++;
+            // Set layer vote if set
+            const auto frameRate = layer->getFrameRateForLayerTree();
+            const auto voteType = [&]() {
+                switch (frameRate.type) {
+                    case Layer::FrameRateCompatibility::Default:
+                        return LayerVoteType::ExplicitDefault;
+                    case Layer::FrameRateCompatibility::ExactOrMultiple:
+                        return LayerVoteType::ExplicitExactOrMultiple;
+                    case Layer::FrameRateCompatibility::NoVote:
+                        return LayerVoteType::NoVote;
+                }
+            }();
+
+            if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
+                const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote;
+                info->setLayerVote(type, frameRate.rate);
+            } else {
+                info->resetLayerVote();
+            }
+            continue;
+        }
+
+        if (CC_UNLIKELY(mTraceEnabled)) {
+            trace(weak, *info, LayerHistory::LayerVoteType::NoVote, 0);
+        }
+
+        info->onLayerInactive(now);
+        std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
+    }
+
+    // Collect expired layers after inactive layers.
+    size_t end = mLayerInfos.size();
+    while (i < end) {
+        if (mLayerInfos[i].first.promote()) {
+            i++;
+        } else {
+            std::swap(mLayerInfos[i], mLayerInfos[--end]);
+        }
+    }
+
+    mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
+}
+
+void LayerHistoryV2::clear() {
+    std::lock_guard lock(mLock);
+
+    for (const auto& [layer, info] : activeLayers()) {
+        info->clearHistory(systemTime());
+    }
+}
+
+} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 723d71f..6d9dd43 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -16,27 +16,19 @@
 
 #include "LayerInfo.h"
 
-#include <cinttypes>
-#include <cstdint>
-#include <numeric>
-#include <string>
+#include <algorithm>
+#include <utility>
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
 
-LayerInfo::LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate)
-      : mName(name),
-        mMinRefreshDuration(1e9f / maxRefreshRate),
-        mLowActivityRefreshDuration(1e9f / minRefreshRate),
-        mRefreshRateHistory(mMinRefreshDuration) {}
+LayerInfo::LayerInfo(float lowRefreshRate, float highRefreshRate)
+      : mLowRefreshRate(lowRefreshRate), mHighRefreshRate(highRefreshRate) {}
 
-LayerInfo::~LayerInfo() = default;
-
-void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime) {
-    std::lock_guard lock(mLock);
+void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+    lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
 
     // Buffers can come with a present time far in the future. That keeps them relevant.
-    mLastUpdatedTime = std::max(lastPresentTime, systemTime());
+    mLastUpdatedTime = std::max(lastPresentTime, now);
     mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
 
     if (mLastPresentTime == 0) {
@@ -45,14 +37,13 @@
         return;
     }
 
-    const nsecs_t timeDiff = lastPresentTime - mLastPresentTime;
+    const nsecs_t period = lastPresentTime - mLastPresentTime;
     mLastPresentTime = lastPresentTime;
     // Ignore time diff that are too high - those are stale values
-    if (timeDiff > OBSOLETE_TIME_EPSILON_NS.count()) return;
-    const nsecs_t refreshDuration = std::max(timeDiff, mMinRefreshDuration);
-    const int fps = 1e9f / refreshDuration;
+    if (period > MAX_ACTIVE_LAYER_PERIOD_NS.count()) return;
+
+    const float fps = std::min(1e9f / period, mHighRefreshRate);
     mRefreshRateHistory.insertRefreshRate(fps);
 }
 
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 17afdda..820624b 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -16,27 +16,37 @@
 
 #pragma once
 
-#include <cinttypes>
-#include <cstdint>
-#include <deque>
-#include <mutex>
-#include <numeric>
-#include <string>
-
-#include <log/log.h>
-
-#include <utils/Mutex.h>
 #include <utils/Timers.h>
 
+#include <chrono>
+#include <deque>
+
 #include "SchedulerUtils.h"
 
 namespace android {
+
+class Layer;
+
 namespace scheduler {
 
-/*
- * This class represents information about individial layers.
- */
+using namespace std::chrono_literals;
+
+// Maximum period between presents for a layer to be considered active.
+constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms;
+
+// Earliest present time for a layer to be considered active.
+constexpr nsecs_t getActiveLayerThreshold(nsecs_t now) {
+    return now - MAX_ACTIVE_LAYER_PERIOD_NS.count();
+}
+
+// Stores history of present times and refresh rates for a layer.
 class LayerInfo {
+    // Layer is considered frequent if the earliest value in the window of most recent present times
+    // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
+    // favor of a low refresh rate.
+    static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3;
+    static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 250ms;
+
     /**
      * Struct that keeps the information about the refresh rate for last
      * HISTORY_SIZE frames. This is used to better determine the refresh rate
@@ -44,9 +54,9 @@
      */
     class RefreshRateHistory {
     public:
-        explicit RefreshRateHistory(nsecs_t minRefreshDuration)
-              : mMinRefreshDuration(minRefreshDuration) {}
-        void insertRefreshRate(int refreshRate) {
+        explicit RefreshRateHistory(float highRefreshRate) : mHighRefreshRate(highRefreshRate) {}
+
+        void insertRefreshRate(float refreshRate) {
             mElements.push_back(refreshRate);
             if (mElements.size() > HISTORY_SIZE) {
                 mElements.pop_front();
@@ -54,19 +64,16 @@
         }
 
         float getRefreshRateAvg() const {
-            if (mElements.empty()) {
-                return 1e9f / mMinRefreshDuration;
-            }
-
-            return scheduler::calculate_mean(mElements);
+            return mElements.empty() ? mHighRefreshRate : calculate_mean(mElements);
         }
 
         void clearHistory() { mElements.clear(); }
 
     private:
-        std::deque<nsecs_t> mElements;
+        const float mHighRefreshRate;
+
         static constexpr size_t HISTORY_SIZE = 30;
-        const nsecs_t mMinRefreshDuration;
+        std::deque<float> mElements;
     };
 
     /**
@@ -76,6 +83,8 @@
      */
     class PresentTimeHistory {
     public:
+        static constexpr size_t HISTORY_SIZE = 90;
+
         void insertPresentTime(nsecs_t presentTime) {
             mElements.push_back(presentTime);
             if (mElements.size() > HISTORY_SIZE) {
@@ -83,60 +92,45 @@
             }
         }
 
-        // Checks whether the present time that was inserted HISTORY_SIZE ago is within a
-        // certain threshold: TIME_EPSILON_NS.
-        bool isRelevant() const {
+        // Returns whether the earliest present time is within the active threshold.
+        bool isRecentlyActive(nsecs_t now) const {
             if (mElements.size() < 2) {
                 return false;
             }
 
-            // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
-            if (mElements.size() != HISTORY_SIZE &&
-                mElements.at(mElements.size() - 1) - mElements.at(0) < HISTORY_TIME.count()) {
+            // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
+            if (mElements.size() < HISTORY_SIZE &&
+                mElements.back() - mElements.front() < HISTORY_DURATION.count()) {
                 return false;
             }
 
-            // The last update should not be older than OBSOLETE_TIME_EPSILON_NS nanoseconds.
-            const int64_t obsoleteEpsilon =
-                    systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count();
-            if (mElements.at(mElements.size() - 1) < obsoleteEpsilon) {
-                return false;
-            }
-
-            return true;
+            return mElements.back() >= getActiveLayerThreshold(now);
         }
 
-        bool isLowActivityLayer() const {
-            // We want to make sure that we received more than two frames from the layer
-            // in order to check low activity.
-            if (mElements.size() < scheduler::LOW_ACTIVITY_BUFFERS + 1) {
+        bool isFrequent(nsecs_t now) const {
+            // Assume layer is infrequent if too few present times have been recorded.
+            if (mElements.size() < FREQUENT_LAYER_WINDOW_SIZE) {
                 return false;
             }
 
-            const int64_t obsoleteEpsilon =
-                    systemTime() - scheduler::LOW_ACTIVITY_EPSILON_NS.count();
-            // Check the frame before last to determine whether there is low activity.
-            // If that frame is older than LOW_ACTIVITY_EPSILON_NS, the layer is sending
-            // infrequent updates.
-            if (mElements.at(mElements.size() - (scheduler::LOW_ACTIVITY_BUFFERS + 1)) <
-                obsoleteEpsilon) {
-                return true;
-            }
-
-            return false;
+            // Layer is frequent if the earliest value in the window of most recent present times is
+            // within threshold.
+            const auto it = mElements.end() - FREQUENT_LAYER_WINDOW_SIZE;
+            const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count();
+            return *it >= threshold;
         }
 
         void clearHistory() { mElements.clear(); }
 
     private:
         std::deque<nsecs_t> mElements;
-        static constexpr size_t HISTORY_SIZE = 90;
-        static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
+        static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
     };
 
+    friend class LayerHistoryTest;
+
 public:
-    LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate);
-    ~LayerInfo();
+    LayerInfo(float lowRefreshRate, float highRefreshRate);
 
     LayerInfo(const LayerInfo&) = delete;
     LayerInfo& operator=(const LayerInfo&) = delete;
@@ -144,71 +138,33 @@
     // Records the last requested oresent time. It also stores information about when
     // the layer was last updated. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
-    void setLastPresentTime(nsecs_t lastPresentTime);
+    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
 
-    void setHDRContent(bool isHdr) {
-        std::lock_guard lock(mLock);
-        mIsHDR = isHdr;
-    }
+    bool isRecentlyActive(nsecs_t now) const { return mPresentTimeHistory.isRecentlyActive(now); }
+    bool isFrequent(nsecs_t now) const { return mPresentTimeHistory.isFrequent(now); }
 
-    void setVisibility(bool visible) {
-        std::lock_guard lock(mLock);
-        mIsVisible = visible;
-    }
-
-    // Checks the present time history to see whether the layer is relevant.
-    bool isRecentlyActive() const {
-        std::lock_guard lock(mLock);
-        return mPresentTimeHistory.isRelevant();
-    }
-
-    // Calculate the average refresh rate.
-    float getDesiredRefreshRate() const {
-        std::lock_guard lock(mLock);
-
-        if (mPresentTimeHistory.isLowActivityLayer()) {
-            return 1e9f / mLowActivityRefreshDuration;
-        }
-        return mRefreshRateHistory.getRefreshRateAvg();
-    }
-
-    bool getHDRContent() {
-        std::lock_guard lock(mLock);
-        return mIsHDR;
-    }
-
-    bool isVisible() {
-        std::lock_guard lock(mLock);
-        return mIsVisible;
+    float getRefreshRate(nsecs_t now) const {
+        return isFrequent(now) ? mRefreshRateHistory.getRefreshRateAvg() : mLowRefreshRate;
     }
 
     // Return the last updated time. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
-    nsecs_t getLastUpdatedTime() {
-        std::lock_guard lock(mLock);
-        return mLastUpdatedTime;
-    }
-
-    std::string getName() const { return mName; }
+    nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
 
     void clearHistory() {
-        std::lock_guard lock(mLock);
         mRefreshRateHistory.clearHistory();
         mPresentTimeHistory.clearHistory();
     }
 
 private:
-    const std::string mName;
-    const nsecs_t mMinRefreshDuration;
-    const nsecs_t mLowActivityRefreshDuration;
-    mutable std::mutex mLock;
-    nsecs_t mLastUpdatedTime GUARDED_BY(mLock) = 0;
-    nsecs_t mLastPresentTime GUARDED_BY(mLock) = 0;
-    RefreshRateHistory mRefreshRateHistory GUARDED_BY(mLock);
-    PresentTimeHistory mPresentTimeHistory GUARDED_BY(mLock);
-    bool mIsHDR GUARDED_BY(mLock) = false;
-    bool mIsVisible GUARDED_BY(mLock) = false;
+    const float mLowRefreshRate;
+    const float mHighRefreshRate;
+
+    nsecs_t mLastUpdatedTime = 0;
+    nsecs_t mLastPresentTime = 0;
+    RefreshRateHistory mRefreshRateHistory{mHighRefreshRate};
+    PresentTimeHistory mPresentTimeHistory;
 };
 
 } // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
new file mode 100644
index 0000000..44f20d0
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -0,0 +1,297 @@
+/*
+ * 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 LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "LayerInfoV2.h"
+
+#include <algorithm>
+#include <utility>
+
+#include <cutils/compiler.h>
+#include <cutils/trace.h>
+
+#undef LOG_TAG
+#define LOG_TAG "LayerInfoV2"
+
+namespace android::scheduler {
+
+const RefreshRateConfigs* LayerInfoV2::sRefreshRateConfigs = nullptr;
+bool LayerInfoV2::sTraceEnabled = false;
+
+LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
+                         LayerHistory::LayerVoteType defaultVote)
+      : mName(name),
+        mHighRefreshRatePeriod(highRefreshRatePeriod),
+        mDefaultVote(defaultVote),
+        mLayerVote({defaultVote, 0.0f}),
+        mRefreshRateHistory(name) {}
+
+void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now,
+                                     LayerUpdateType updateType, bool pendingConfigChange) {
+    lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
+
+    mLastUpdatedTime = std::max(lastPresentTime, now);
+    switch (updateType) {
+        case LayerUpdateType::AnimationTX:
+            mLastAnimationTime = std::max(lastPresentTime, now);
+            break;
+        case LayerUpdateType::SetFrameRate:
+        case LayerUpdateType::Buffer:
+            FrameTimeData frameTime = {.presetTime = lastPresentTime,
+                                       .queueTime = mLastUpdatedTime,
+                                       .pendingConfigChange = pendingConfigChange};
+            mFrameTimes.push_back(frameTime);
+            if (mFrameTimes.size() > HISTORY_SIZE) {
+                mFrameTimes.pop_front();
+            }
+            break;
+    }
+}
+
+bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
+    return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
+                                          mFrameTimeValidSince.time_since_epoch())
+                                          .count();
+}
+
+bool LayerInfoV2::isFrequent(nsecs_t now) const {
+    // If we know nothing about this layer we consider it as frequent as it might be the start
+    // of an animation.
+    if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
+        return true;
+    }
+
+    // Find the first active frame
+    auto it = mFrameTimes.begin();
+    for (; it != mFrameTimes.end(); ++it) {
+        if (it->queueTime >= getActiveLayerThreshold(now)) {
+            break;
+        }
+    }
+
+    const auto numFrames = std::distance(it, mFrameTimes.end());
+    if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
+        return false;
+    }
+
+    // Layer is considered frequent if the average frame rate is higher than the threshold
+    const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
+    return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
+}
+
+bool LayerInfoV2::isAnimating(nsecs_t now) const {
+    return mLastAnimationTime >= getActiveLayerThreshold(now);
+}
+
+bool LayerInfoV2::hasEnoughDataForHeuristic() const {
+    // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
+    if (mFrameTimes.size() < 2) {
+        ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
+        return false;
+    }
+
+    if (!isFrameTimeValid(mFrameTimes.front())) {
+        ALOGV("stale frames still captured");
+        return false;
+    }
+
+    const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
+    if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
+        ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
+              totalDuration / 1e9f);
+        return false;
+    }
+
+    return true;
+}
+
+std::optional<nsecs_t> LayerInfoV2::calculateAverageFrameTime() const {
+    nsecs_t totalPresentTimeDeltas = 0;
+    nsecs_t totalQueueTimeDeltas = 0;
+    bool missingPresentTime = false;
+    int numFrames = 0;
+    for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+        // Ignore frames captured during a config change
+        if (it->pendingConfigChange || (it + 1)->pendingConfigChange) {
+            return std::nullopt;
+        }
+
+        totalQueueTimeDeltas +=
+                std::max(((it + 1)->queueTime - it->queueTime), mHighRefreshRatePeriod);
+        numFrames++;
+
+        if (!missingPresentTime && (it->presetTime == 0 || (it + 1)->presetTime == 0)) {
+            missingPresentTime = true;
+            // If there are no presentation timestamps and we haven't calculated
+            // one in the past then we can't calculate the refresh rate
+            if (mLastRefreshRate.reported == 0) {
+                return std::nullopt;
+            }
+            continue;
+        }
+
+        totalPresentTimeDeltas +=
+                std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
+    }
+
+    // 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 auto averageFrameTime =
+            static_cast<float>(missingPresentTime ? totalQueueTimeDeltas : totalPresentTimeDeltas) /
+            numFrames;
+    return static_cast<nsecs_t>(averageFrameTime);
+}
+
+std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible(nsecs_t now) {
+    static constexpr float MARGIN = 1.0f; // 1Hz
+    if (!hasEnoughDataForHeuristic()) {
+        ALOGV("Not enough data");
+        return std::nullopt;
+    }
+
+    const auto averageFrameTime = calculateAverageFrameTime();
+    if (averageFrameTime.has_value()) {
+        const auto refreshRate = 1e9f / *averageFrameTime;
+        const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now);
+        if (refreshRateConsistent) {
+            const auto knownRefreshRate =
+                    sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate);
+
+            // To avoid oscillation, use the last calculated refresh rate if it is
+            // close enough
+            if (std::abs(mLastRefreshRate.calculated - refreshRate) > MARGIN &&
+                mLastRefreshRate.reported != knownRefreshRate) {
+                mLastRefreshRate.calculated = refreshRate;
+                mLastRefreshRate.reported = knownRefreshRate;
+            }
+
+            ALOGV("%s %.2fHz rounded to nearest known frame rate %.2fHz", mName.c_str(),
+                  refreshRate, mLastRefreshRate.reported);
+        } else {
+            ALOGV("%s Not stable (%.2fHz) returning last known frame rate %.2fHz", mName.c_str(),
+                  refreshRate, mLastRefreshRate.reported);
+        }
+    }
+
+    return mLastRefreshRate.reported == 0 ? std::nullopt
+                                          : std::make_optional(mLastRefreshRate.reported);
+}
+
+std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
+    if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
+        ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
+        return {mLayerVote.type, mLayerVote.fps};
+    }
+
+    if (isAnimating(now)) {
+        ALOGV("%s is animating", mName.c_str());
+        mLastRefreshRate.animatingOrInfrequent = true;
+        return {LayerHistory::LayerVoteType::Max, 0};
+    }
+
+    if (!isFrequent(now)) {
+        ALOGV("%s is infrequent", mName.c_str());
+        mLastRefreshRate.animatingOrInfrequent = true;
+        return {LayerHistory::LayerVoteType::Min, 0};
+    }
+
+    // If the layer was previously tagged as animating or infrequent, we clear
+    // the history as it is likely the layer just changed its behavior
+    // and we should not look at stale data
+    if (mLastRefreshRate.animatingOrInfrequent) {
+        clearHistory(now);
+    }
+
+    auto refreshRate = calculateRefreshRateIfPossible(now);
+    if (refreshRate.has_value()) {
+        ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
+        return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
+    }
+
+    ALOGV("%s Max (can't resolve refresh rate)", mName.c_str());
+    return {LayerHistory::LayerVoteType::Max, 0};
+}
+
+const char* LayerInfoV2::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
+    if (mTraceTags.count(type) == 0) {
+        const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
+        mTraceTags.emplace(type, tag);
+    }
+
+    return mTraceTags.at(type).c_str();
+}
+
+LayerInfoV2::RefreshRateHistory::HeuristicTraceTagData
+LayerInfoV2::RefreshRateHistory::makeHeuristicTraceTagData() const {
+    const std::string prefix = "LFPS ";
+    const std::string suffix = "Heuristic ";
+    return {.min = prefix + mName + suffix + "min",
+            .max = prefix + mName + suffix + "max",
+            .consistent = prefix + mName + suffix + "consistent",
+            .average = prefix + mName + suffix + "average"};
+}
+
+void LayerInfoV2::RefreshRateHistory::clear() {
+    mRefreshRates.clear();
+}
+
+bool LayerInfoV2::RefreshRateHistory::add(float refreshRate, nsecs_t now) {
+    mRefreshRates.push_back({refreshRate, now});
+    while (mRefreshRates.size() >= HISTORY_SIZE ||
+           now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) {
+        mRefreshRates.pop_front();
+    }
+
+    if (CC_UNLIKELY(sTraceEnabled)) {
+        if (!mHeuristicTraceTagData.has_value()) {
+            mHeuristicTraceTagData = makeHeuristicTraceTagData();
+        }
+
+        ATRACE_INT(mHeuristicTraceTagData->average.c_str(), static_cast<int>(refreshRate));
+    }
+
+    return isConsistent();
+}
+
+bool LayerInfoV2::RefreshRateHistory::isConsistent() const {
+    if (mRefreshRates.empty()) return true;
+
+    const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end());
+    const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end());
+    const auto consistent = max->refreshRate - min->refreshRate <= MARGIN_FPS;
+
+    if (CC_UNLIKELY(sTraceEnabled)) {
+        if (!mHeuristicTraceTagData.has_value()) {
+            mHeuristicTraceTagData = makeHeuristicTraceTagData();
+        }
+
+        ATRACE_INT(mHeuristicTraceTagData->max.c_str(), static_cast<int>(max->refreshRate));
+        ATRACE_INT(mHeuristicTraceTagData->min.c_str(), static_cast<int>(min->refreshRate));
+        ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent);
+    }
+
+    return consistent;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
new file mode 100644
index 0000000..33dc66f
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -0,0 +1,221 @@
+/*
+ * 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 <utils/Timers.h>
+
+#include <chrono>
+#include <deque>
+
+#include "LayerHistory.h"
+#include "RefreshRateConfigs.h"
+#include "SchedulerUtils.h"
+
+namespace android {
+
+class Layer;
+
+namespace scheduler {
+
+using namespace std::chrono_literals;
+
+// Maximum period between presents for a layer to be considered active.
+constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms;
+
+// Earliest present time for a layer to be considered active.
+constexpr nsecs_t getActiveLayerThreshold(nsecs_t now) {
+    return now - MAX_ACTIVE_LAYER_PERIOD_NS.count();
+}
+
+// Stores history of present times and refresh rates for a layer.
+class LayerInfoV2 {
+    using LayerUpdateType = LayerHistory::LayerUpdateType;
+
+    // Layer is considered frequent if the earliest value in the window of most recent present times
+    // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
+    // favor of a low refresh rate.
+    static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3;
+    static constexpr float MIN_FPS_FOR_FREQUENT_LAYER = 10.0f;
+    static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS =
+            std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / MIN_FPS_FOR_FREQUENT_LAYER)) + 1ms;
+
+    friend class LayerHistoryTestV2;
+
+public:
+    static void setTraceEnabled(bool enabled) { sTraceEnabled = enabled; }
+
+    static void setRefreshRateConfigs(const RefreshRateConfigs& refreshRateConfigs) {
+        sRefreshRateConfigs = &refreshRateConfigs;
+    }
+
+    LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
+                LayerHistory::LayerVoteType defaultVote);
+
+    LayerInfoV2(const LayerInfo&) = delete;
+    LayerInfoV2& operator=(const LayerInfoV2&) = delete;
+
+    // Records the last requested present time. It also stores information about when
+    // the layer was last updated. If the present time is farther in the future than the
+    // updated time, the updated time is the present time.
+    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType,
+                            bool pendingConfigChange);
+
+    // Sets an explicit layer vote. This usually comes directly from the application via
+    // ANativeWindow_setFrameRate API
+    void setLayerVote(LayerHistory::LayerVoteType type, float fps) { mLayerVote = {type, fps}; }
+
+    // Sets the default layer vote. This will be the layer vote after calling to resetLayerVote().
+    // This is used for layers that called to setLayerVote() and then removed the vote, so that the
+    // layer can go back to whatever vote it had before the app voted for it.
+    void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
+
+    // Resets the layer vote to its default.
+    void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f}; }
+
+    std::pair<LayerHistory::LayerVoteType, float> getRefreshRate(nsecs_t now);
+
+    // Return the last updated time. If the present time is farther in the future than the
+    // updated time, the updated time is the present time.
+    nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
+
+    // Returns a C string for tracing a vote
+    const char* getTraceTag(LayerHistory::LayerVoteType type) const;
+
+    void onLayerInactive(nsecs_t now) {
+        // Mark mFrameTimeValidSince to now to ignore all previous frame times.
+        // We are not deleting the old frame to keep track of whether we should treat the first
+        // buffer as Max as we don't know anything about this layer or Min as this layer is
+        // posting infrequent updates.
+        const auto timePoint = std::chrono::nanoseconds(now);
+        mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
+        mLastRefreshRate = {};
+        mRefreshRateHistory.clear();
+    }
+
+    void clearHistory(nsecs_t now) {
+        onLayerInactive(now);
+        mFrameTimes.clear();
+    }
+
+private:
+    // Used to store the layer timestamps
+    struct FrameTimeData {
+        nsecs_t presetTime; // desiredPresentTime, if provided
+        nsecs_t queueTime;  // buffer queue time
+        bool pendingConfigChange;
+    };
+
+    // Holds information about the calculated and reported refresh rate
+    struct RefreshRateHeuristicData {
+        // Rate calculated on the layer
+        float calculated = 0.0f;
+        // Last reported rate for LayerInfoV2::getRefreshRate()
+        float reported = 0.0f;
+        // Whether the last reported rate for LayerInfoV2::getRefreshRate()
+        // was due to animation or infrequent updates
+        bool animatingOrInfrequent = false;
+    };
+
+    // Holds information about the layer vote
+    struct LayerVote {
+        LayerHistory::LayerVoteType type = LayerHistory::LayerVoteType::Heuristic;
+        float fps = 0.0f;
+    };
+
+    // Class to store past calculated refresh rate and determine whether
+    // the refresh rate calculated is consistent with past values
+    class RefreshRateHistory {
+    public:
+        static constexpr auto HISTORY_SIZE = 90;
+        static constexpr std::chrono::nanoseconds HISTORY_DURATION = 2s;
+
+        RefreshRateHistory(const std::string& name) : mName(name) {}
+
+        // Clears History
+        void clear();
+
+        // Adds a new refresh rate and returns true if it is consistent
+        bool add(float refreshRate, nsecs_t now);
+
+    private:
+        friend class LayerHistoryTestV2;
+
+        // Holds the refresh rate when it was calculated
+        struct RefreshRateData {
+            float refreshRate = 0.0f;
+            nsecs_t timestamp = 0;
+
+            bool operator<(const RefreshRateData& other) const {
+                return refreshRate < other.refreshRate;
+            }
+        };
+
+        // Holds tracing strings
+        struct HeuristicTraceTagData {
+            std::string min;
+            std::string max;
+            std::string consistent;
+            std::string average;
+        };
+
+        bool isConsistent() const;
+        HeuristicTraceTagData makeHeuristicTraceTagData() const;
+
+        const std::string mName;
+        mutable std::optional<HeuristicTraceTagData> mHeuristicTraceTagData;
+        std::deque<RefreshRateData> mRefreshRates;
+        static constexpr float MARGIN_FPS = 1.0;
+    };
+
+    bool isFrequent(nsecs_t now) const;
+    bool isAnimating(nsecs_t now) const;
+    bool hasEnoughDataForHeuristic() const;
+    std::optional<float> calculateRefreshRateIfPossible(nsecs_t now);
+    std::optional<nsecs_t> calculateAverageFrameTime() const;
+    bool isFrameTimeValid(const FrameTimeData&) const;
+
+    const std::string mName;
+
+    // Used for sanitizing the heuristic data
+    const nsecs_t mHighRefreshRatePeriod;
+    LayerHistory::LayerVoteType mDefaultVote;
+
+    LayerVote mLayerVote;
+
+    nsecs_t mLastUpdatedTime = 0;
+
+    nsecs_t mLastAnimationTime = 0;
+
+    RefreshRateHeuristicData mLastRefreshRate;
+
+    std::deque<FrameTimeData> mFrameTimes;
+    std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
+            std::chrono::steady_clock::now();
+    static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE;
+    static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s;
+
+    RefreshRateHistory mRefreshRateHistory;
+
+    mutable std::unordered_map<LayerHistory::LayerVoteType, std::string> mTraceTags;
+
+    // Shared for all LayerInfo instances
+    static const RefreshRateConfigs* sRefreshRateConfigs;
+    static bool sTraceEnabled;
+};
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index fcb307f..6067e69 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#include <errno.h>
-#include <stdint.h>
-#include <sys/types.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 
 #include <binder/IPCThreadState.h>
 
@@ -31,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) {
@@ -58,8 +39,9 @@
     }
 }
 
-void MessageQueue::Handler::dispatchInvalidate() {
+void MessageQueue::Handler::dispatchInvalidate(nsecs_t expectedVSyncTimestamp) {
     if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+        mExpectedVSyncTime = expectedVSyncTimestamp;
         mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
     }
 }
@@ -68,11 +50,11 @@
     switch (message.what) {
         case INVALIDATE:
             android_atomic_and(~eventMaskInvalidate, &mEventMask);
-            mQueue.mFlinger->onMessageReceived(message.what);
+            mQueue.mFlinger->onMessageReceived(message.what, mExpectedVSyncTime);
             break;
         case REFRESH:
             android_atomic_and(~eventMaskRefresh, &mEventMask);
-            mQueue.mFlinger->onMessageReceived(message.what);
+            mQueue.mFlinger->onMessageReceived(message.what, mExpectedVSyncTime);
             break;
     }
 }
@@ -85,24 +67,6 @@
     mHandler = new Handler(*this);
 }
 
-void MessageQueue::setEventThread(android::EventThread* eventThread,
-                                  ResyncCallback resyncCallback) {
-    if (mEventThread == eventThread) {
-        return;
-    }
-
-    if (mEventTube.getFd() >= 0) {
-        mLooper->removeFd(mEventTube.getFd());
-    }
-
-    mEventThread = eventThread;
-    mEvents = eventThread->createEventConnection(std::move(resyncCallback),
-                                                 ISurfaceComposer::eConfigChangedSuppress);
-    mEvents->stealReceiveChannel(&mEventTube);
-    mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
-                   this);
-}
-
 void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) {
     if (mEventTube.getFd() >= 0) {
         mLooper->removeFd(mEventTube.getFd());
@@ -136,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() {
@@ -165,7 +123,7 @@
     while ((n = DisplayEventReceiver::getEvents(&mEventTube, buffer, 8)) > 0) {
         for (int i = 0; i < n; i++) {
             if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-                mHandler->dispatchInvalidate();
+                mHandler->dispatchInvalidate(buffer[i].vsync.expectedVSyncTimestamp);
                 break;
             }
         }
@@ -173,7 +131,7 @@
     return 1;
 }
 
-// ---------------------------------------------------------------------------
+} // namespace android::impl
 
-} // namespace impl
-} // namespace android
+// 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 0b2206d..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,14 +60,12 @@
         REFRESH = 1,
     };
 
-    virtual ~MessageQueue();
+    virtual ~MessageQueue() = default;
 
     virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
-    // TODO(b/128863962): Remove this function once everything is migrated to Scheduler.
-    virtual void setEventThread(EventThread* events, ResyncCallback resyncCallback) = 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;
 };
@@ -103,19 +79,19 @@
         enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
         MessageQueue& mQueue;
         int32_t mEventMask;
+        std::atomic<nsecs_t> mExpectedVSyncTime;
 
     public:
         explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) {}
         virtual void handleMessage(const Message& message);
         void dispatchRefresh();
-        void dispatchInvalidate();
+        void dispatchInvalidate(nsecs_t expectedVSyncTimestamp);
     };
 
     friend class Handler;
 
     sp<SurfaceFlinger> mFlinger;
     sp<Looper> mLooper;
-    android::EventThread* mEventThread;
     sp<EventThreadConnection> mEvents;
     gui::BitTube mEventTube;
     sp<Handler> mHandler;
@@ -126,11 +102,10 @@
 public:
     ~MessageQueue() override = default;
     void init(const sp<SurfaceFlinger>& flinger) override;
-    void setEventThread(android::EventThread* events, ResyncCallback resyncCallback) override;
     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;
@@ -139,9 +114,5 @@
     void refresh() override;
 };
 
-// ---------------------------------------------------------------------------
-
 } // namespace impl
 } // namespace android
-
-#endif /* ANDROID_MESSAGE_QUEUE_H */
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
similarity index 84%
rename from services/surfaceflinger/Scheduler/IdleTimer.cpp
rename to services/surfaceflinger/Scheduler/OneShotTimer.cpp
index 37fdfc7..a90d05e 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -14,31 +14,32 @@
  * limitations under the License.
  */
 
-#include "IdleTimer.h"
+#include "OneShotTimer.h"
 
 #include <chrono>
+#include <sstream>
 #include <thread>
 
 namespace android {
 namespace scheduler {
 
-IdleTimer::IdleTimer(const Interval& interval, const ResetCallback& resetCallback,
-                     const TimeoutCallback& timeoutCallback)
+OneShotTimer::OneShotTimer(const Interval& interval, const ResetCallback& resetCallback,
+                           const TimeoutCallback& timeoutCallback)
       : mInterval(interval), mResetCallback(resetCallback), mTimeoutCallback(timeoutCallback) {}
 
-IdleTimer::~IdleTimer() {
+OneShotTimer::~OneShotTimer() {
     stop();
 }
 
-void IdleTimer::start() {
+void OneShotTimer::start() {
     {
         std::lock_guard<std::mutex> lock(mMutex);
         mState = TimerState::RESET;
     }
-    mThread = std::thread(&IdleTimer::loop, this);
+    mThread = std::thread(&OneShotTimer::loop, this);
 }
 
-void IdleTimer::stop() {
+void OneShotTimer::stop() {
     {
         std::lock_guard<std::mutex> lock(mMutex);
         mState = TimerState::STOPPED;
@@ -49,7 +50,7 @@
     }
 }
 
-void IdleTimer::loop() {
+void OneShotTimer::loop() {
     while (true) {
         bool triggerReset = false;
         bool triggerTimeout = false;
@@ -98,9 +99,9 @@
             mTimeoutCallback();
         }
     }
-} // namespace scheduler
+}
 
-void IdleTimer::reset() {
+void OneShotTimer::reset() {
     {
         std::lock_guard<std::mutex> lock(mMutex);
         mState = TimerState::RESET;
@@ -108,5 +109,11 @@
     mCondition.notify_all();
 }
 
+std::string OneShotTimer::dump() const {
+    std::ostringstream stream;
+    stream << mInterval.count() << " ms";
+    return stream.str();
+}
+
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
similarity index 92%
rename from services/surfaceflinger/Scheduler/IdleTimer.h
rename to services/surfaceflinger/Scheduler/OneShotTimer.h
index 2646688..b005754 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -29,15 +29,15 @@
  * Class that sets off a timer for a given interval, and fires a callback when the
  * interval expires.
  */
-class IdleTimer {
+class OneShotTimer {
 public:
     using Interval = std::chrono::milliseconds;
     using ResetCallback = std::function<void()>;
     using TimeoutCallback = std::function<void()>;
 
-    IdleTimer(const Interval& interval, const ResetCallback& resetCallback,
-              const TimeoutCallback& timeoutCallback);
-    ~IdleTimer();
+    OneShotTimer(const Interval& interval, const ResetCallback& resetCallback,
+                 const TimeoutCallback& timeoutCallback);
+    ~OneShotTimer();
 
     // Initializes and turns on the idle timer.
     void start();
@@ -46,6 +46,8 @@
     // Resets the wakeup time and fires the reset callback.
     void reset();
 
+    std::string dump() const;
+
 private:
     // Enum to track in what state is the timer.
     enum class TimerState {
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 9fa2bbc..fe2e406 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -18,114 +18,341 @@
 
 #include <cutils/properties.h>
 
+#include <optional>
+
 #include "SurfaceFlingerProperties.h"
 
-namespace android {
-using namespace android::sysprop;
+namespace {
 
-namespace scheduler {
+std::optional<nsecs_t> getProperty(const char* name) {
+    char value[PROPERTY_VALUE_MAX];
+    property_get(name, value, "-1");
+    if (const int i = atoi(value); i != -1) return i;
+    return std::nullopt;
+}
 
-using RefreshRateType = RefreshRateConfigs::RefreshRateType;
-PhaseOffsets::~PhaseOffsets() = default;
+bool fpsEqualsWithMargin(float fpsA, float fpsB) {
+    static constexpr float MARGIN = 0.01f;
+    return std::abs(fpsA - fpsB) <= MARGIN;
+}
+
+std::vector<float> getRefreshRatesFromConfigs(
+        const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+    const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+    std::vector<float> refreshRates;
+    refreshRates.reserve(allRefreshRates.size());
+
+    for (const auto& [ignored, refreshRate] : allRefreshRates) {
+        refreshRates.emplace_back(refreshRate->getFps());
+    }
+
+    return refreshRates;
+}
+
+} // namespace
+
+namespace android::scheduler {
+
+PhaseConfiguration::~PhaseConfiguration() = default;
 
 namespace impl {
-PhaseOffsets::PhaseOffsets() {
-    int64_t vsyncPhaseOffsetNs = vsync_event_phase_offset_ns(1000000);
 
-    int64_t sfVsyncPhaseOffsetNs = vsync_sf_event_phase_offset_ns(1000000);
+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
+                     // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
+                     // vsync.
+                     getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+                             .value_or(std::numeric_limits<nsecs_t>::max())) {}
 
-    char value[PROPERTY_VALUE_MAX];
-    property_get("debug.sf.early_phase_offset_ns", value, "-1");
-    const int earlySfOffsetNs = atoi(value);
-
-    property_get("debug.sf.early_gl_phase_offset_ns", value, "-1");
-    const int earlyGlSfOffsetNs = atoi(value);
-
-    property_get("debug.sf.early_app_phase_offset_ns", value, "-1");
-    const int earlyAppOffsetNs = atoi(value);
-
-    property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1");
-    const int earlyGlAppOffsetNs = atoi(value);
-
-    property_get("debug.sf.high_fps_early_phase_offset_ns", value, "-1");
-    const int highFpsEarlySfOffsetNs = atoi(value);
-
-    property_get("debug.sf.high_fps_early_gl_phase_offset_ns", value, "-1");
-    const int highFpsEarlyGlSfOffsetNs = atoi(value);
-
-    property_get("debug.sf.high_fps_early_app_phase_offset_ns", value, "-1");
-    const int highFpsEarlyAppOffsetNs = atoi(value);
-
-    property_get("debug.sf.high_fps_early_gl_app_phase_offset_ns", value, "-1");
-    const int highFpsEarlyGlAppOffsetNs = atoi(value);
-
-    // TODO(b/122905996): Define these in device.mk.
-    property_get("debug.sf.high_fps_late_app_phase_offset_ns", value, "2000000");
-    const int highFpsLateAppOffsetNs = atoi(value);
-
-    property_get("debug.sf.high_fps_late_sf_phase_offset_ns", value, "1000000");
-    const int highFpsLateSfOffsetNs = atoi(value);
-
-    // 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 offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
-    property_get("debug.sf.phase_offset_threshold_for_next_vsync_ns", value, "-1");
-    const int phaseOffsetThresholdForNextVsyncNs = atoi(value);
-
-    Offsets defaultOffsets;
-    Offsets highFpsOffsets;
-    defaultOffsets.early = {RefreshRateType::DEFAULT,
-                            earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs,
-                            earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs};
-    defaultOffsets.earlyGl = {RefreshRateType::DEFAULT,
-                              earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs : sfVsyncPhaseOffsetNs,
-                              earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs : vsyncPhaseOffsetNs};
-    defaultOffsets.late = {RefreshRateType::DEFAULT, sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs};
-
-    highFpsOffsets.early = {RefreshRateType::PERFORMANCE,
-                            highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs
-                                                         : highFpsLateSfOffsetNs,
-                            highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs
-                                                          : highFpsLateAppOffsetNs};
-    highFpsOffsets.earlyGl = {RefreshRateType::PERFORMANCE,
-                              highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs
-                                                             : highFpsLateSfOffsetNs,
-                              highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
-                                                              : highFpsLateAppOffsetNs};
-    highFpsOffsets.late = {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs,
-                           highFpsLateAppOffsetNs};
-
-    mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets});
-    mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets});
-
-    mOffsetThresholdForNextVsync = phaseOffsetThresholdForNextVsyncNs != -1
-            ? phaseOffsetThresholdForNextVsyncNs
-            : std::numeric_limits<nsecs_t>::max();
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(
-        android::scheduler::RefreshRateConfigs::RefreshRateType refreshRateType) const {
-    return mOffsets.at(refreshRateType);
-}
+PhaseOffsets::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)
+      : mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs),
+        mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs),
+        mEarlySfOffsetNs(earlySfOffsetNs),
+        mEarlyGlSfOffsetNs(earlyGlSfOffsetNs),
+        mEarlyAppOffsetNs(earlyAppOffsetNs),
+        mEarlyGlAppOffsetNs(earlyGlAppOffsetNs),
+        mThresholdForNextVsync(thresholdForNextVsync),
+        mOffsets(initializeOffsets(refreshRates)),
+        mRefreshRateFps(currentFps) {}
 
 void PhaseOffsets::dump(std::string& result) const {
     const auto [early, earlyGl, late] = getCurrentOffsets();
-    base::StringAppendF(&result,
-                        "         app phase: %9" PRId64 " ns\t         SF phase: %9" PRId64 " ns\n"
-                        "   early app phase: %9" PRId64 " ns\t   early SF phase: %9" PRId64 " ns\n"
-                        "GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n",
-                        late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf);
+    using base::StringAppendF;
+    StringAppendF(&result,
+                  "           app phase: %9" PRId64 " ns\t         SF phase: %9" PRId64 " ns\n"
+                  "     early app phase: %9" PRId64 " ns\t   early SF phase: %9" PRId64 " ns\n"
+                  "  GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n"
+                  "next VSYNC threshold: %9" PRId64 " ns\n",
+                  late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf,
+                  mThresholdForNextVsync);
 }
 
-nsecs_t PhaseOffsets::getCurrentAppOffset() {
-    return getCurrentOffsets().late.app;
+std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
+        const std::vector<float>& refreshRates) const {
+    std::unordered_map<float, Offsets> offsets;
+
+    for (const auto& refreshRate : refreshRates) {
+        offsets.emplace(refreshRate,
+                        getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
+    }
+    return offsets;
 }
 
-nsecs_t PhaseOffsets::getCurrentSfOffset() {
-    return getCurrentOffsets().late.sf;
+PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
+    if (fps > 65.0f) {
+        return getHighFpsOffsets(vsyncPeriod);
+    } else {
+        return getDefaultOffsets(vsyncPeriod);
+    }
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
+    return {
+            {
+                    mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+                            ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+                            : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
+
+                    mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
+            },
+            {
+                    mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync
+                            ? mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs)
+                            : mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration,
+
+                    mEarlyGlAppOffsetNs.value_or(mVSyncPhaseOffsetNs),
+            },
+            {
+                    mSfVSyncPhaseOffsetNs < mThresholdForNextVsync
+                            ? mSfVSyncPhaseOffsetNs
+                            : mSfVSyncPhaseOffsetNs - vsyncDuration,
+
+                    mVSyncPhaseOffsetNs,
+            },
+    };
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
+    const auto highFpsLateAppOffsetNs =
+            getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
+    const auto highFpsLateSfOffsetNs =
+            getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000);
+
+    const auto highFpsEarlySfOffsetNs = getProperty("debug.sf.high_fps_early_phase_offset_ns");
+    const auto highFpsEarlyGlSfOffsetNs = getProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
+    const auto highFpsEarlyAppOffsetNs = getProperty("debug.sf.high_fps_early_app_phase_offset_ns");
+    const auto highFpsEarlyGlAppOffsetNs =
+            getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
+
+    return {
+            {
+                    highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync
+                            ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs)
+                            : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) -
+                                    vsyncDuration,
+
+                    highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs),
+            },
+            {
+                    highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) <
+                                    mThresholdForNextVsync
+                            ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs)
+                            : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) -
+                                    vsyncDuration,
+
+                    highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs),
+            },
+            {
+                    highFpsLateSfOffsetNs < mThresholdForNextVsync
+                            ? highFpsLateSfOffsetNs
+                            : highFpsLateSfOffsetNs - vsyncDuration,
+
+                    highFpsLateAppOffsetNs,
+            },
+    };
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
+    const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
+                                   [&fps](const std::pair<float, Offsets>& candidateFps) {
+                                       return fpsEqualsWithMargin(fps, candidateFps.first);
+                                   });
+
+    if (iter != mOffsets.end()) {
+        return iter->second;
+    }
+
+    // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
+    // In this case just construct the offset.
+    ALOGW("Can't find offset for %.2f fps", fps);
+    return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps));
+}
+
+static void validateSysprops() {
+    const auto validatePropertyBool = [](const char* prop) {
+        LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
+    };
+
+    validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
+
+    LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
+                        "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
+                        "duration");
+
+    LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
+                        "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
+                        "duration");
+
+    const auto validateProperty = [](const char* prop) {
+        LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
+                            "%s is set to %" PRId64 " but expecting duration", prop,
+                            getProperty(prop).value_or(-1));
+    };
+
+    validateProperty("debug.sf.early_phase_offset_ns");
+    validateProperty("debug.sf.early_gl_phase_offset_ns");
+    validateProperty("debug.sf.early_app_phase_offset_ns");
+    validateProperty("debug.sf.early_gl_app_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_early_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
+}
+
+static nsecs_t sfDurationToOffset(nsecs_t sfDuration, nsecs_t vsyncDuration) {
+    return sfDuration == -1 ? 1'000'000 : vsyncDuration - sfDuration % vsyncDuration;
+}
+
+static nsecs_t appDurationToOffset(nsecs_t appDuration, nsecs_t sfDuration, nsecs_t vsyncDuration) {
+    return sfDuration == -1 ? 1'000'000
+                            : vsyncDuration - (appDuration + sfDuration) % vsyncDuration;
+}
+
+PhaseDurations::Offsets PhaseDurations::constructOffsets(nsecs_t vsyncDuration) const {
+    return Offsets{
+            {
+                    mSfEarlyDuration < vsyncDuration
+                            ? sfDurationToOffset(mSfEarlyDuration, vsyncDuration)
+                            : sfDurationToOffset(mSfEarlyDuration, vsyncDuration) - vsyncDuration,
+
+                    appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration, vsyncDuration),
+            },
+            {
+                    mSfEarlyGlDuration < vsyncDuration
+                            ? sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration)
+                            : sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) - vsyncDuration,
+
+                    appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration, vsyncDuration),
+            },
+            {
+                    mSfDuration < vsyncDuration
+                            ? sfDurationToOffset(mSfDuration, vsyncDuration)
+                            : sfDurationToOffset(mSfDuration, vsyncDuration) - vsyncDuration,
+
+                    appDurationToOffset(mAppDuration, mSfDuration, vsyncDuration),
+            },
+    };
+}
+
+std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
+        const std::vector<float>& refreshRates) const {
+    std::unordered_map<float, Offsets> offsets;
+
+    for (const auto fps : refreshRates) {
+        offsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
+    }
+    return offsets;
+}
+
+PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+      : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs),
+                       refreshRateConfigs.getCurrentRefreshRate().getFps(),
+                       getProperty("debug.sf.late.sf.duration").value_or(-1),
+                       getProperty("debug.sf.late.app.duration").value_or(-1),
+                       getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
+                       getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
+                       getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
+                       getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
+    validateSysprops();
+}
+
+PhaseDurations::PhaseDurations(const std::vector<float>& refreshRates, float currentFps,
+                               nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
+                               nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration,
+                               nsecs_t appEarlyGlDuration)
+      : mSfDuration(sfDuration),
+        mAppDuration(appDuration),
+        mSfEarlyDuration(sfEarlyDuration),
+        mAppEarlyDuration(appEarlyDuration),
+        mSfEarlyGlDuration(sfEarlyGlDuration),
+        mAppEarlyGlDuration(appEarlyGlDuration),
+        mOffsets(initializeOffsets(refreshRates)),
+        mRefreshRateFps(currentFps) {}
+
+PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const {
+    const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(), [=](const auto& candidateFps) {
+        return fpsEqualsWithMargin(fps, candidateFps.first);
+    });
+
+    if (iter != mOffsets.end()) {
+        return iter->second;
+    }
+
+    // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
+    // In this case just construct the offset.
+    ALOGW("Can't find offset for %.2f fps", fps);
+    return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
+}
+
+void PhaseDurations::dump(std::string& result) const {
+    const auto [early, earlyGl, late] = getCurrentOffsets();
+    using base::StringAppendF;
+    StringAppendF(&result,
+                  "           app phase:    %9" PRId64 " ns\t         SF phase:    %9" PRId64
+                  " ns\n"
+                  "           app duration: %9" PRId64 " ns\t         SF duration: %9" PRId64
+                  " ns\n"
+                  "     early app phase:    %9" PRId64 " ns\t   early SF phase:    %9" PRId64
+                  " ns\n"
+                  "     early app duration: %9" PRId64 " ns\t   early SF duration: %9" PRId64
+                  " ns\n"
+                  "  GL early app phase:    %9" PRId64 " ns\tGL early SF phase:    %9" PRId64
+                  " ns\n"
+                  "  GL early app duration: %9" PRId64 " ns\tGL early SF duration: %9" PRId64
+                  " ns\n",
+                  late.app,
+
+                  late.sf,
+
+                  mAppDuration, mSfDuration,
+
+                  early.app, early.sf,
+
+                  mAppEarlyDuration, mSfEarlyDuration,
+
+                  earlyGl.app,
+
+                  earlyGl.sf,
+
+                  mAppEarlyGlDuration, mSfEarlyGlDuration);
 }
 
 } // namespace impl
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index 2b5c2f1..9ec6d56 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -16,14 +16,12 @@
 
 #pragma once
 
-#include <cinttypes>
 #include <unordered_map>
 
 #include "RefreshRateConfigs.h"
 #include "VSyncModulator.h"
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
 
 /*
  * This class encapsulates offsets for different refresh rates. Depending
@@ -31,62 +29,113 @@
  * different offsets will help us with latency. This class keeps track of
  * which mode the device is on, and returns approprate offsets when needed.
  */
-class PhaseOffsets {
+class PhaseConfiguration {
 public:
-    struct Offsets {
-        VSyncModulator::Offsets early;
-        VSyncModulator::Offsets earlyGl;
-        VSyncModulator::Offsets late;
-    };
+    using Offsets = VSyncModulator::OffsetsConfig;
 
-    virtual ~PhaseOffsets();
+    virtual ~PhaseConfiguration();
 
-    virtual nsecs_t getCurrentAppOffset() = 0;
-    virtual nsecs_t getCurrentSfOffset() = 0;
-    virtual Offsets getOffsetsForRefreshRate(
-            RefreshRateConfigs::RefreshRateType refreshRateType) const = 0;
     virtual Offsets getCurrentOffsets() const = 0;
-    virtual void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) = 0;
-    virtual nsecs_t getOffsetThresholdForNextVsync() const = 0;
+    virtual Offsets getOffsetsForRefreshRate(float fps) const = 0;
+
+    virtual void setRefreshRateFps(float fps) = 0;
+
     virtual void dump(std::string& result) const = 0;
 };
 
 namespace impl {
-class PhaseOffsets : public scheduler::PhaseOffsets {
-public:
-    PhaseOffsets();
 
-    nsecs_t getCurrentAppOffset() override;
-    nsecs_t getCurrentSfOffset() override;
+/*
+ * This is the old implementation of phase offsets and considered as deprecated.
+ * PhaseDurations is the new implementation.
+ */
+class PhaseOffsets : public scheduler::PhaseConfiguration {
+public:
+    PhaseOffsets(const scheduler::RefreshRateConfigs&);
 
     // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
-    Offsets getOffsetsForRefreshRate(
-            RefreshRateConfigs::RefreshRateType refreshRateType) const override;
+    Offsets getOffsetsForRefreshRate(float fps) const override;
 
     // Returns early, early GL, and late offsets for Apps and SF.
-    Offsets getCurrentOffsets() const override {
-        return getOffsetsForRefreshRate(mRefreshRateType);
-    }
+    Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
 
     // This function should be called when the device is switching between different
     // refresh rates, to properly update the offsets.
-    void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) override {
-        mRefreshRateType = refreshRateType;
-    }
-
-    nsecs_t getOffsetThresholdForNextVsync() const override { return mOffsetThresholdForNextVsync; }
+    void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
 
     // Returns current offsets in human friendly format.
     void dump(std::string& result) const override;
 
-private:
-    std::atomic<RefreshRateConfigs::RefreshRateType> mRefreshRateType =
-            RefreshRateConfigs::RefreshRateType::DEFAULT;
+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;
+    Offsets getDefaultOffsets(nsecs_t vsyncPeriod) const;
+    Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const;
+    Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const;
 
-    std::unordered_map<RefreshRateConfigs::RefreshRateType, Offsets> mOffsets;
-    nsecs_t mOffsetThresholdForNextVsync;
+    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;
+
+    std::atomic<float> mRefreshRateFps;
 };
-} // namespace impl
 
-} // namespace scheduler
-} // namespace android
+/*
+ * Class that encapsulates the phase offsets for SurfaceFlinger and App.
+ * The offsets are calculated from durations for each one of the (late, early, earlyGL)
+ * offset types.
+ */
+class PhaseDurations : public scheduler::PhaseConfiguration {
+public:
+    PhaseDurations(const scheduler::RefreshRateConfigs&);
+
+    // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
+    Offsets getOffsetsForRefreshRate(float fps) const override;
+
+    // Returns early, early GL, and late offsets for Apps and SF.
+    Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
+
+    // This function should be called when the device is switching between different
+    // refresh rates, to properly update the offsets.
+    void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
+
+    // Returns current offsets in human friendly format.
+    void dump(std::string& result) const override;
+
+protected:
+    // Used for unit tests
+    PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration,
+                   nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+                   nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration);
+
+private:
+    std::unordered_map<float, Offsets> initializeOffsets(const std::vector<float>&) const;
+    PhaseDurations::Offsets constructOffsets(nsecs_t vsyncDuration) const;
+
+    const nsecs_t mSfDuration;
+    const nsecs_t mAppDuration;
+
+    const nsecs_t mSfEarlyDuration;
+    const nsecs_t mAppEarlyDuration;
+
+    const nsecs_t mSfEarlyGlDuration;
+    const nsecs_t mAppEarlyGlDuration;
+
+    const std::unordered_map<float, Offsets> mOffsets;
+
+    std::atomic<float> mRefreshRateFps;
+};
+
+} // namespace impl
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
new file mode 100644
index 0000000..8661b6e
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -0,0 +1,621 @@
+/*
+ * 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.
+ */
+
+// #define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "RefreshRateConfigs.h"
+#include <android-base/stringprintf.h>
+#include <utils/Trace.h>
+#include <chrono>
+#include <cmath>
+
+#undef LOG_TAG
+#define LOG_TAG "RefreshRateConfigs"
+
+namespace android::scheduler {
+
+using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
+using RefreshRate = RefreshRateConfigs::RefreshRate;
+
+std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) {
+    switch (vote) {
+        case LayerVoteType::NoVote:
+            return "NoVote";
+        case LayerVoteType::Min:
+            return "Min";
+        case LayerVoteType::Max:
+            return "Max";
+        case LayerVoteType::Heuristic:
+            return "Heuristic";
+        case LayerVoteType::ExplicitDefault:
+            return "ExplicitDefault";
+        case LayerVoteType::ExplicitExactOrMultiple:
+            return "ExplicitExactOrMultiple";
+    }
+}
+
+const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(
+        const std::vector<LayerRequirement>& layers) const {
+    std::lock_guard lock(mLock);
+    int contentFramerate = 0;
+    int explicitContentFramerate = 0;
+    for (const auto& layer : layers) {
+        const auto desiredRefreshRateRound = round<int>(layer.desiredRefreshRate);
+        if (layer.vote == LayerVoteType::ExplicitDefault ||
+            layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
+            if (desiredRefreshRateRound > explicitContentFramerate) {
+                explicitContentFramerate = desiredRefreshRateRound;
+            }
+        } else {
+            if (desiredRefreshRateRound > contentFramerate) {
+                contentFramerate = desiredRefreshRateRound;
+            }
+        }
+    }
+
+    if (explicitContentFramerate != 0) {
+        contentFramerate = explicitContentFramerate;
+    } else if (contentFramerate == 0) {
+        contentFramerate = round<int>(mMaxSupportedRefreshRate->getFps());
+    }
+    ATRACE_INT("ContentFPS", contentFramerate);
+
+    // Find the appropriate refresh rate with minimal error
+    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);
+                            });
+
+    // Some content aligns better on higher refresh rate. For example for 45fps we should choose
+    // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
+    // align well with both
+    const RefreshRate* bestSoFar = *iter;
+    constexpr float MARGIN = 0.05f;
+    float ratio = (*iter)->fps / contentFramerate;
+    if (std::abs(std::round(ratio) - ratio) > MARGIN) {
+        while (iter != mPrimaryRefreshRates.cend()) {
+            ratio = (*iter)->fps / contentFramerate;
+
+            if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
+                bestSoFar = *iter;
+                break;
+            }
+            ++iter;
+        }
+    }
+
+    return *bestSoFar;
+}
+
+std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPeriod,
+                                                                 nsecs_t displayPeriod) const {
+    auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
+    if (displayFramesRem <= MARGIN_FOR_PERIOD_CALCULATION ||
+        std::abs(displayFramesRem - displayPeriod) <= MARGIN_FOR_PERIOD_CALCULATION) {
+        displayFramesQuot++;
+        displayFramesRem = 0;
+    }
+
+    return {displayFramesQuot, displayFramesRem};
+}
+
+const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
+        const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+        GlobalSignals* outSignalsConsidered) const {
+    ATRACE_CALL();
+    ALOGV("getRefreshRateForContent %zu layers", layers.size());
+
+    if (outSignalsConsidered) *outSignalsConsidered = {};
+    const auto setTouchConsidered = [&] {
+        if (outSignalsConsidered) {
+            outSignalsConsidered->touch = true;
+        }
+    };
+
+    const auto setIdleConsidered = [&] {
+        if (outSignalsConsidered) {
+            outSignalsConsidered->idle = true;
+        }
+    };
+
+    std::lock_guard lock(mLock);
+
+    int noVoteLayers = 0;
+    int minVoteLayers = 0;
+    int maxVoteLayers = 0;
+    int explicitDefaultVoteLayers = 0;
+    int explicitExactOrMultipleVoteLayers = 0;
+    float maxExplicitWeight = 0;
+    for (const auto& layer : layers) {
+        if (layer.vote == LayerVoteType::NoVote) {
+            noVoteLayers++;
+        } else if (layer.vote == LayerVoteType::Min) {
+            minVoteLayers++;
+        } else if (layer.vote == LayerVoteType::Max) {
+            maxVoteLayers++;
+        } else if (layer.vote == LayerVoteType::ExplicitDefault) {
+            explicitDefaultVoteLayers++;
+            maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
+        } else if (layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
+            explicitExactOrMultipleVoteLayers++;
+            maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
+        }
+    }
+
+    const bool hasExplicitVoteLayers =
+            explicitDefaultVoteLayers > 0 || explicitExactOrMultipleVoteLayers > 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 (globalSignals.touch && !hasExplicitVoteLayers) {
+        ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
+        setTouchConsidered();
+        return getMaxRefreshRateByPolicyLocked();
+    }
+
+    // If the primary range consists of a single refresh rate then we can only
+    // move out the of range if layers explicitly request a different refresh
+    // rate.
+    const Policy* policy = getCurrentPolicyLocked();
+    const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max;
+
+    if (!globalSignals.touch && globalSignals.idle &&
+        !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) {
+        ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
+        setIdleConsidered();
+        return getMinRefreshRateByPolicyLocked();
+    }
+
+    if (layers.empty() || noVoteLayers == layers.size()) {
+        return getMaxRefreshRateByPolicyLocked();
+    }
+
+    // Only if all layers want Min we should return Min
+    if (noVoteLayers + minVoteLayers == layers.size()) {
+        ALOGV("all layers Min - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
+        return getMinRefreshRateByPolicyLocked();
+    }
+
+    // Find the best refresh rate based on score
+    std::vector<std::pair<const RefreshRate*, float>> scores;
+    scores.reserve(mAppRequestRefreshRates.size());
+
+    for (const auto refreshRate : mAppRequestRefreshRates) {
+        scores.emplace_back(refreshRate, 0.0f);
+    }
+
+    for (const auto& layer : layers) {
+        ALOGV("Calculating score for %s (%s, weight %.2f)", layer.name.c_str(),
+              layerVoteTypeString(layer.vote).c_str(), layer.weight);
+        if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
+            continue;
+        }
+
+        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 ((primaryRangeIsSingleRate || !inPrimaryRange) &&
+                !(layer.focused && layer.vote == LayerVoteType::ExplicitDefault)) {
+                // Only focused layers with ExplicitDefault 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;
+                // use ratio^2 to get a lower score the more we get further from peak
+                const auto layerScore = ratio * ratio;
+                ALOGV("%s (Max, weight %.2f) gives %s score of %.2f", layer.name.c_str(), weight,
+                      scores[i].first->name.c_str(), layerScore);
+                scores[i].second += weight * layerScore;
+                continue;
+            }
+
+            const auto displayPeriod = scores[i].first->hwcConfig->getVsyncPeriod();
+            const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate);
+            if (layer.vote == LayerVoteType::ExplicitDefault) {
+                const auto layerScore = [&]() {
+                    // Find the actual rate the layer will render, assuming
+                    // that layerPeriod is the minimal time to render a frame
+                    auto actualLayerPeriod = displayPeriod;
+                    int multiplier = 1;
+                    while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
+                        multiplier++;
+                        actualLayerPeriod = displayPeriod * multiplier;
+                    }
+                    return std::min(1.0f,
+                                    static_cast<float>(layerPeriod) /
+                                            static_cast<float>(actualLayerPeriod));
+                }();
+
+                ALOGV("%s (ExplicitDefault, weight %.2f) %.2fHz gives %s score of %.2f",
+                      layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(),
+                      layerScore);
+                scores[i].second += weight * layerScore;
+                continue;
+            }
+
+            if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
+                layer.vote == LayerVoteType::Heuristic) {
+                const auto layerScore = [&] {
+                    // Calculate how many display vsyncs we need to present a single frame for this
+                    // layer
+                    const auto [displayFramesQuot, displayFramesRem] =
+                            getDisplayFrames(layerPeriod, displayPeriod);
+                    static constexpr size_t MAX_FRAMES_TO_FIT =
+                            10; // Stop calculating when score < 0.1
+                    if (displayFramesRem == 0) {
+                        // Layer desired refresh rate matches the display rate.
+                        return 1.0f;
+                    }
+
+                    if (displayFramesQuot == 0) {
+                        // Layer desired refresh rate is higher the display rate.
+                        return (static_cast<float>(layerPeriod) /
+                                static_cast<float>(displayPeriod)) *
+                                (1.0f / (MAX_FRAMES_TO_FIT + 1));
+                    }
+
+                    // Layer desired refresh rate is lower the display rate. Check how well it fits
+                    // the cadence
+                    auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
+                    int iter = 2;
+                    while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
+                        diff = diff - (displayPeriod - diff);
+                        iter++;
+                    }
+
+                    return 1.0f / iter;
+                }();
+                ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
+                      layerVoteTypeString(layer.vote).c_str(), weight, 1e9f / layerPeriod,
+                      scores[i].first->name.c_str(), layerScore);
+                scores[i].second += weight * layerScore;
+                continue;
+            }
+        }
+    }
+
+    // Now that we scored all the refresh rates we need to pick the one that got the highest score.
+    // In case of a tie we will pick the higher refresh rate if any of the layers wanted Max,
+    // or the lower otherwise.
+    const RefreshRate* bestRefreshRate = maxVoteLayers > 0
+            ? getBestRefreshRate(scores.rbegin(), scores.rend())
+            : getBestRefreshRate(scores.begin(), scores.end());
+
+    if (primaryRangeIsSingleRate) {
+        // If we never scored any layers, then choose the rate from the primary
+        // range instead of picking a random score from the app range.
+        if (std::all_of(scores.begin(), scores.end(),
+                        [](std::pair<const RefreshRate*, float> p) { return p.second == 0; })) {
+            ALOGV("layers not scored - choose %s",
+                  getMaxRefreshRateByPolicyLocked().getName().c_str());
+            return getMaxRefreshRateByPolicyLocked();
+        } else {
+            return *bestRefreshRate;
+        }
+    }
+
+    // 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 (globalSignals.touch && explicitDefaultVoteLayers == 0 &&
+        bestRefreshRate->fps < touchRefreshRate.fps) {
+        setTouchConsidered();
+        ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
+        return touchRefreshRate;
+    }
+
+    return *bestRefreshRate;
+}
+
+template <typename Iter>
+const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
+    constexpr auto EPSILON = 0.001f;
+    const RefreshRate* bestRefreshRate = begin->first;
+    float max = begin->second;
+    for (auto i = begin; i != end; ++i) {
+        const auto [refreshRate, score] = *i;
+        ALOGV("%s scores %.2f", refreshRate->name.c_str(), score);
+
+        ATRACE_INT(refreshRate->name.c_str(), round<int>(score * 100));
+
+        if (score > max * (1 + EPSILON)) {
+            max = score;
+            bestRefreshRate = refreshRate;
+        }
+    }
+
+    return bestRefreshRate;
+}
+
+const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const {
+    return mRefreshRates;
+}
+
+const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
+    std::lock_guard lock(mLock);
+    return getMinRefreshRateByPolicyLocked();
+}
+
+const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const {
+    return *mPrimaryRefreshRates.front();
+}
+
+const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
+    std::lock_guard lock(mLock);
+    return getMaxRefreshRateByPolicyLocked();
+}
+
+const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const {
+    return *mPrimaryRefreshRates.back();
+}
+
+const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const {
+    std::lock_guard lock(mLock);
+    return *mCurrentRefreshRate;
+}
+
+const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
+    std::lock_guard lock(mLock);
+    return getCurrentRefreshRateByPolicyLocked();
+}
+
+const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicyLocked() const {
+    if (std::find(mAppRequestRefreshRates.begin(), mAppRequestRefreshRates.end(),
+                  mCurrentRefreshRate) != mAppRequestRefreshRates.end()) {
+        return *mCurrentRefreshRate;
+    }
+    return *mRefreshRates.at(getCurrentPolicyLocked()->defaultConfig);
+}
+
+void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) {
+    std::lock_guard lock(mLock);
+    mCurrentRefreshRate = mRefreshRates.at(configId).get();
+}
+
+RefreshRateConfigs::RefreshRateConfigs(
+        const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+        HwcConfigIndexType currentConfigId)
+      : mKnownFrameRates(constructKnownFrameRates(configs)) {
+    LOG_ALWAYS_FATAL_IF(configs.empty());
+    LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size());
+
+    for (auto configId = HwcConfigIndexType(0); configId.value() < configs.size(); configId++) {
+        const auto& config = configs.at(static_cast<size_t>(configId.value()));
+        const float fps = 1e9f / config->getVsyncPeriod();
+        mRefreshRates.emplace(configId,
+                              std::make_unique<RefreshRate>(configId, config,
+                                                            base::StringPrintf("%.0ffps", fps), fps,
+                                                            RefreshRate::ConstructorTag(0)));
+        if (configId == currentConfigId) {
+            mCurrentRefreshRate = mRefreshRates.at(configId).get();
+        }
+    }
+
+    std::vector<const RefreshRate*> sortedConfigs;
+    getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
+    mDisplayManagerPolicy.defaultConfig = currentConfigId;
+    mMinSupportedRefreshRate = sortedConfigs.front();
+    mMaxSupportedRefreshRate = sortedConfigs.back();
+    constructAvailableRefreshRates();
+}
+
+bool RefreshRateConfigs::isPolicyValid(const Policy& policy) {
+    // defaultConfig must be a valid config, and within the given refresh rate range.
+    auto iter = mRefreshRates.find(policy.defaultConfig);
+    if (iter == mRefreshRates.end()) {
+        return false;
+    }
+    const RefreshRate& refreshRate = *iter->second;
+    if (!refreshRate.inPolicy(policy.primaryRange.min, policy.primaryRange.max)) {
+        return false;
+    }
+    return policy.appRequestRange.min <= policy.primaryRange.min &&
+            policy.appRequestRange.max >= policy.primaryRange.max;
+}
+
+status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) {
+    std::lock_guard lock(mLock);
+    if (!isPolicyValid(policy)) {
+        return BAD_VALUE;
+    }
+    Policy previousPolicy = *getCurrentPolicyLocked();
+    mDisplayManagerPolicy = policy;
+    if (*getCurrentPolicyLocked() == previousPolicy) {
+        return CURRENT_POLICY_UNCHANGED;
+    }
+    constructAvailableRefreshRates();
+    return NO_ERROR;
+}
+
+status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& policy) {
+    std::lock_guard lock(mLock);
+    if (policy && !isPolicyValid(*policy)) {
+        return BAD_VALUE;
+    }
+    Policy previousPolicy = *getCurrentPolicyLocked();
+    mOverridePolicy = policy;
+    if (*getCurrentPolicyLocked() == previousPolicy) {
+        return CURRENT_POLICY_UNCHANGED;
+    }
+    constructAvailableRefreshRates();
+    return NO_ERROR;
+}
+
+const RefreshRateConfigs::Policy* RefreshRateConfigs::getCurrentPolicyLocked() const {
+    return mOverridePolicy ? &mOverridePolicy.value() : &mDisplayManagerPolicy;
+}
+
+RefreshRateConfigs::Policy RefreshRateConfigs::getCurrentPolicy() const {
+    std::lock_guard lock(mLock);
+    return *getCurrentPolicyLocked();
+}
+
+RefreshRateConfigs::Policy RefreshRateConfigs::getDisplayManagerPolicy() const {
+    std::lock_guard lock(mLock);
+    return mDisplayManagerPolicy;
+}
+
+bool RefreshRateConfigs::isConfigAllowed(HwcConfigIndexType config) const {
+    std::lock_guard lock(mLock);
+    for (const RefreshRate* refreshRate : mAppRequestRefreshRates) {
+        if (refreshRate->configId == config) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void RefreshRateConfigs::getSortedRefreshRateList(
+        const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
+        std::vector<const RefreshRate*>* outRefreshRates) {
+    outRefreshRates->clear();
+    outRefreshRates->reserve(mRefreshRates.size());
+    for (const auto& [type, refreshRate] : mRefreshRates) {
+        if (shouldAddRefreshRate(*refreshRate)) {
+            ALOGV("getSortedRefreshRateList: config %d added to list policy",
+                  refreshRate->configId.value());
+            outRefreshRates->push_back(refreshRate.get());
+        }
+    }
+
+    std::sort(outRefreshRates->begin(), outRefreshRates->end(),
+              [](const auto refreshRate1, const auto refreshRate2) {
+                  if (refreshRate1->hwcConfig->getVsyncPeriod() !=
+                      refreshRate2->hwcConfig->getVsyncPeriod()) {
+                      return refreshRate1->hwcConfig->getVsyncPeriod() >
+                              refreshRate2->hwcConfig->getVsyncPeriod();
+                  } else {
+                      return refreshRate1->hwcConfig->getConfigGroup() >
+                              refreshRate2->hwcConfig->getConfigGroup();
+                  }
+              });
+}
+
+void RefreshRateConfigs::constructAvailableRefreshRates() {
+    // 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 primaryRange=[%.2f %.2f]"
+          " appRequestRange=[%.2f %.2f]",
+          policy->defaultConfig.value(), defaultConfig->getConfigGroup(), policy->primaryRange.min,
+          policy->primaryRange.max, policy->appRequestRange.min, policy->appRequestRange.max);
+
+    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;
+
+                    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);
+
+        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);
+}
+
+std::vector<float> RefreshRateConfigs::constructKnownFrameRates(
+        const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+    std::vector<float> knownFrameRates = {24.0f, 30.0f, 45.0f, 60.0f, 72.0f};
+    knownFrameRates.reserve(knownFrameRates.size() + configs.size());
+
+    // Add all supported refresh rates to the set
+    for (const auto& config : configs) {
+        const auto refreshRate = 1e9f / config->getVsyncPeriod();
+        knownFrameRates.emplace_back(refreshRate);
+    }
+
+    // Sort and remove duplicates
+    const auto frameRatesEqual = [](float a, float b) { return std::abs(a - b) <= 0.01f; };
+    std::sort(knownFrameRates.begin(), knownFrameRates.end());
+    knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(),
+                                      frameRatesEqual),
+                          knownFrameRates.end());
+    return knownFrameRates;
+}
+
+float RefreshRateConfigs::findClosestKnownFrameRate(float frameRate) const {
+    if (frameRate <= *mKnownFrameRates.begin()) {
+        return *mKnownFrameRates.begin();
+    }
+
+    if (frameRate >= *std::prev(mKnownFrameRates.end())) {
+        return *std::prev(mKnownFrameRates.end());
+    }
+
+    auto lowerBound = std::lower_bound(mKnownFrameRates.begin(), mKnownFrameRates.end(), frameRate);
+
+    const auto distance1 = std::abs(frameRate - *lowerBound);
+    const auto distance2 = std::abs(frameRate - *std::prev(lowerBound));
+    return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
+}
+
+RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
+    std::lock_guard lock(mLock);
+    const auto& deviceMin = getMinRefreshRate();
+    const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
+    const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();
+
+    // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
+    // the min allowed refresh rate is higher than the device min, we do not want to enable the
+    // timer.
+    if (deviceMin < minByPolicy) {
+        return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
+    }
+    if (minByPolicy == maxByPolicy) {
+        // Do not sent the call to toggle off kernel idle timer if the device min and policy min and
+        // max are all the same. This saves us extra unnecessary calls to sysprop.
+        if (deviceMin == minByPolicy) {
+            return RefreshRateConfigs::KernelIdleTimerAction::NoChange;
+        }
+        return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
+    }
+    // Turn on the timer in all other cases.
+    return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index d813708..27bf0ec 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -16,16 +16,28 @@
 
 #pragma once
 
+#include <android-base/stringprintf.h>
+
 #include <algorithm>
 #include <numeric>
-
-#include "android-base/stringprintf.h"
+#include <optional>
+#include <type_traits>
 
 #include "DisplayHardware/HWComposer.h"
+#include "HwcStrongTypes.h"
 #include "Scheduler/SchedulerUtils.h"
+#include "Scheduler/StrongTyping.h"
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
+
+using namespace std::chrono_literals;
+
+enum class RefreshRateConfigEvent : unsigned { None = 0b0, Changed = 0b1 };
+
+inline RefreshRateConfigEvent operator|(RefreshRateConfigEvent lhs, RefreshRateConfigEvent rhs) {
+    using T = std::underlying_type_t<RefreshRateConfigEvent>;
+    return static_cast<RefreshRateConfigEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
+}
 
 /**
  * This class is used to encapsulate configuration for refresh rates. It holds information
@@ -34,173 +46,318 @@
  */
 class RefreshRateConfigs {
 public:
-    // Enum to indicate which vsync rate to run at. Default is the old 60Hz, and performance
-    // is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs.
-    enum class RefreshRateType { DEFAULT, PERFORMANCE };
+    // Margin used when matching refresh rates to the content desired ones.
+    static constexpr nsecs_t MARGIN_FOR_PERIOD_CALCULATION =
+        std::chrono::nanoseconds(800us).count();
 
-    struct RefreshRate {
+    class RefreshRate {
+    private:
+        // Effectively making the constructor private while allowing
+        // std::make_unique to create the object
+        struct ConstructorTag {
+            explicit ConstructorTag(int) {}
+        };
+
+    public:
+        RefreshRate(HwcConfigIndexType configId,
+                    std::shared_ptr<const HWC2::Display::Config> config, std::string name,
+                    float fps, ConstructorTag)
+              : configId(configId), hwcConfig(config), name(std::move(name)), fps(fps) {}
+
+        RefreshRate(const RefreshRate&) = delete;
+
+        HwcConfigIndexType getConfigId() const { return configId; }
+        nsecs_t getVsyncPeriod() const { return hwcConfig->getVsyncPeriod(); }
+        int32_t getConfigGroup() const { return hwcConfig->getConfigGroup(); }
+        const std::string& getName() const { return name; }
+        float getFps() const { return fps; }
+
+        // Checks whether the fps of this RefreshRate struct is within a given min and max refresh
+        // rate passed in. FPS_EPSILON is applied to the boundaries for approximation.
+        bool inPolicy(float minRefreshRate, float maxRefreshRate) const {
+            return (fps >= (minRefreshRate - FPS_EPSILON) && fps <= (maxRefreshRate + FPS_EPSILON));
+        }
+
+        bool operator!=(const RefreshRate& other) const {
+            return configId != other.configId || hwcConfig != other.hwcConfig;
+        }
+
+        bool operator<(const RefreshRate& other) const { return getFps() < other.getFps(); }
+
+        bool operator==(const RefreshRate& other) const { return !(*this != other); }
+
+    private:
+        friend RefreshRateConfigs;
+        friend class RefreshRateConfigsTest;
+
+        // The tolerance within which we consider FPS approximately equals.
+        static constexpr float FPS_EPSILON = 0.001f;
+
         // This config ID corresponds to the position of the config in the vector that is stored
         // on the device.
-        int configId;
+        const HwcConfigIndexType configId;
+        // The config itself
+        std::shared_ptr<const HWC2::Display::Config> hwcConfig;
         // Human readable name of the refresh rate.
+        const std::string name;
+        // Refresh rate in frames per second
+        const float fps = 0;
+    };
+
+    using AllRefreshRatesMapType =
+            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 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 && primaryRange == other.primaryRange &&
+                    appRequestRange == other.appRequestRange &&
+                    allowGroupSwitching == other.allowGroupSwitching;
+        }
+
+        bool operator!=(const Policy& other) const { return !(*this == other); }
+    };
+
+    // Return code set*Policy() to indicate the current policy is unchanged.
+    static constexpr int CURRENT_POLICY_UNCHANGED = 1;
+
+    // We maintain the display manager policy and the override policy separately. The override
+    // policy is used by CTS tests to get a consistent device state for testing. While the override
+    // policy is set, it takes precedence over the display manager policy. Once the override policy
+    // is cleared, we revert to using the display manager policy.
+
+    // Sets the display manager policy to choose refresh rates. The return value will be:
+    //   - A negative value if the policy is invalid or another error occurred.
+    //   - NO_ERROR if the policy was successfully updated, and the current policy is different from
+    //     what it was before the call.
+    //   - CURRENT_POLICY_UNCHANGED if the policy was successfully updated, but the current policy
+    //     is the same as it was before the call.
+    status_t setDisplayManagerPolicy(const Policy& policy) EXCLUDES(mLock);
+    // Sets the override policy. See setDisplayManagerPolicy() for the meaning of the return value.
+    status_t setOverridePolicy(const std::optional<Policy>& policy) EXCLUDES(mLock);
+    // Gets the current policy, which will be the override policy if active, and the display manager
+    // policy otherwise.
+    Policy getCurrentPolicy() const EXCLUDES(mLock);
+    // Gets the display manager policy, regardless of whether an override policy is active.
+    Policy getDisplayManagerPolicy() const EXCLUDES(mLock);
+
+    // Returns true if config is allowed by the current policy.
+    bool isConfigAllowed(HwcConfigIndexType config) const EXCLUDES(mLock);
+
+    // Describes the different options the layer voted for refresh rate
+    enum class LayerVoteType {
+        NoVote,          // Doesn't care about the refresh rate
+        Min,             // Minimal refresh rate available
+        Max,             // Maximal refresh rate available
+        Heuristic,       // Specific refresh rate that was calculated by platform using a heuristic
+        ExplicitDefault, // Specific refresh rate that was provided by the app with Default
+                         // compatibility
+        ExplicitExactOrMultiple // Specific refresh rate that was provided by the app with
+                                // ExactOrMultiple compatibility
+    };
+
+    // Captures the layer requirements for a refresh rate. This will be used to determine the
+    // display refresh rate.
+    struct LayerRequirement {
+        // Layer's name. Used for debugging purposes.
         std::string name;
-        // Refresh rate in frames per second, rounded to the nearest integer.
-        uint32_t fps = 0;
-        // Vsync period in nanoseconds.
-        nsecs_t vsyncPeriod;
-        // Hwc config Id (returned from HWC2::Display::Config::getId())
-        hwc2_config_t hwcId;
+        // Layer vote type.
+        LayerVoteType vote = LayerVoteType::NoVote;
+        // Layer's desired refresh rate, if applicable.
+        float desiredRefreshRate = 0.0f;
+        // Layer's weight in the range of [0, 1]. The higher the weight the more impact this layer
+        // would have on choosing the refresh rate.
+        float weight = 0.0f;
+        // Whether layer is in focus or not based on WindowManager's state
+        bool focused = false;
+
+        bool operator==(const LayerRequirement& other) const {
+            return name == other.name && vote == other.vote &&
+                    desiredRefreshRate == other.desiredRefreshRate && weight == other.weight &&
+                    focused == other.focused;
+        }
+
+        bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
     };
 
-    // Returns true if this device is doing refresh rate switching. This won't change at runtime.
-    bool refreshRateSwitchingSupported() const { return mRefreshRateSwitchingSupported; }
+    // Returns the refresh rate that fits best to the given layers.
+    const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const
+            EXCLUDES(mLock);
 
-    // Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
-    // from multiple threads. This can only be called if refreshRateSwitching() returns true.
-    // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
-    // baking them in.
-    const std::map<RefreshRateType, RefreshRate>& getRefreshRateMap() const {
-        LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported);
-        return mRefreshRateMap;
-    }
-
-    const RefreshRate& getRefreshRateFromType(RefreshRateType type) const {
-        if (!mRefreshRateSwitchingSupported) {
-            return getCurrentRefreshRate().second;
-        } else {
-            auto refreshRate = mRefreshRateMap.find(type);
-            LOG_ALWAYS_FATAL_IF(refreshRate == mRefreshRateMap.end());
-            return refreshRate->second;
-        }
-    }
-
-    std::pair<RefreshRateType, const RefreshRate&> getCurrentRefreshRate() const {
-        int currentConfig = mCurrentConfig;
-        if (mRefreshRateSwitchingSupported) {
-            for (const auto& [type, refresh] : mRefreshRateMap) {
-                if (refresh.configId == currentConfig) {
-                    return {type, refresh};
-                }
-            }
-            LOG_ALWAYS_FATAL();
-        }
-        return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]};
-    }
-
-    const RefreshRate& getRefreshRateFromConfigId(int configId) const {
-        LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size());
-        return mRefreshRates[configId];
-    }
-
-    RefreshRateType getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const {
-        if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT;
-
-        for (const auto& [type, refreshRate] : mRefreshRateMap) {
-            if (refreshRate.hwcId == hwcId) {
-                return type;
-            }
-        }
-
-        return RefreshRateType::DEFAULT;
-    }
-
-    void setCurrentConfig(int config) {
-        LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size());
-        mCurrentConfig = config;
-    }
-
-    struct InputConfig {
-        hwc2_config_t hwcId = 0;
-        nsecs_t vsyncPeriod = 0;
+    // Global state describing signals that affect refresh rate choice.
+    struct GlobalSignals {
+        // Whether the user touched the screen recently. Used to apply touch boost.
+        bool touch = false;
+        // True if the system hasn't seen any buffers posted to layers recently.
+        bool idle = false;
     };
 
-    RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
-                       int currentConfig) {
-        init(refreshRateSwitching, configs, currentConfig);
-    }
+    // Returns the refresh rate that fits best to the given layers.
+    //   layers - The layer requirements to consider.
+    //   globalSignals - global state of touch and idle
+    //   outSignalsConsidered - An output param that tells the caller whether the refresh rate was
+    //                          chosen based on touch boost and/or idle timer.
+    const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers,
+                                          const GlobalSignals& globalSignals,
+                                          GlobalSignals* outSignalsConsidered = nullptr) const
+            EXCLUDES(mLock);
 
-    RefreshRateConfigs(bool refreshRateSwitching,
-                       const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
-                       int currentConfig) {
-        std::vector<InputConfig> inputConfigs;
-        for (const auto& config : configs) {
-            inputConfigs.push_back({config->getId(), config->getVsyncPeriod()});
-        }
-        init(refreshRateSwitching, inputConfigs, currentConfig);
-    }
+    // Returns all the refresh rates supported by the device. This won't change at runtime.
+    const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
+
+    // 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 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 at runtime. Only
+    // uses the primary range, not the app request range.
+    const RefreshRate& getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
+
+    // Returns the current refresh rate
+    const RefreshRate& getCurrentRefreshRate() const EXCLUDES(mLock);
+
+    // Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
+    // the policy.
+    const RefreshRate& getCurrentRefreshRateByPolicy() const;
+
+    // Returns the refresh rate that corresponds to a HwcConfigIndexType. This won't change at
+    // runtime.
+    const RefreshRate& getRefreshRateFromConfigId(HwcConfigIndexType configId) const {
+        return *mRefreshRates.at(configId);
+    };
+
+    // Stores the current configId the device operates at
+    void setCurrentConfigId(HwcConfigIndexType configId) EXCLUDES(mLock);
+
+    // Returns a string that represents the layer vote type
+    static std::string layerVoteTypeString(LayerVoteType vote);
+
+    // Returns a known frame rate that is the closest to frameRate
+    float findClosestKnownFrameRate(float frameRate) const;
+
+    RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+                       HwcConfigIndexType currentConfigId);
+
+    // Class to enumerate options around toggling the kernel timer on and off. We have an option
+    // for no change to avoid extra calls to kernel.
+    enum class KernelIdleTimerAction {
+        NoChange, // Do not change the idle timer.
+        TurnOff,  // Turn off the idle timer.
+        TurnOn    // Turn on the idle timer.
+    };
+    // Checks whether kernel idle timer should be active depending the policy decisions around
+    // refresh rates.
+    KernelIdleTimerAction getIdleTimerAction() const;
 
 private:
-    void init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
-              int currentConfig) {
-        mRefreshRateSwitchingSupported = refreshRateSwitching;
-        LOG_ALWAYS_FATAL_IF(configs.empty());
-        LOG_ALWAYS_FATAL_IF(currentConfig >= configs.size());
-        mCurrentConfig = currentConfig;
+    friend class RefreshRateConfigsTest;
 
-        auto buildRefreshRate = [&](int configId) -> RefreshRate {
-            const nsecs_t vsyncPeriod = configs[configId].vsyncPeriod;
-            const float fps = 1e9 / vsyncPeriod;
-            return {configId, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps),
-                    vsyncPeriod, configs[configId].hwcId};
-        };
+    void constructAvailableRefreshRates() REQUIRES(mLock);
+    static std::vector<float> constructKnownFrameRates(
+            const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs);
 
-        for (int i = 0; i < configs.size(); ++i) {
-            mRefreshRates.push_back(buildRefreshRate(i));
-        }
+    void getSortedRefreshRateList(
+            const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
+            std::vector<const RefreshRate*>* outRefreshRates);
 
-        if (!mRefreshRateSwitchingSupported) return;
+    // Returns the refresh rate with the highest score in the collection specified from begin
+    // to end. If there are more than one with the same highest refresh rate, the first one is
+    // returned.
+    template <typename Iter>
+    const RefreshRate* getBestRefreshRate(Iter begin, Iter end) const;
 
-        auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> {
-            if (configs.size() < 2) {
-                return {};
-            }
+    // Returns number of display frames and remainder when dividing the layer refresh period by
+    // display refresh period.
+    std::pair<nsecs_t, nsecs_t> getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const;
 
-            std::vector<const RefreshRate*> sortedRefreshRates;
-            for (const auto& refreshRate : mRefreshRates) {
-                sortedRefreshRates.push_back(&refreshRate);
-            }
-            std::sort(sortedRefreshRates.begin(), sortedRefreshRates.end(),
-                      [](const RefreshRate* refreshRate1, const RefreshRate* refreshRate2) {
-                          return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
-                      });
+    // 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);
 
-            // When the configs are ordered by the resync rate, we assume that
-            // the first one is DEFAULT and the second one is PERFORMANCE,
-            // i.e. the higher rate.
-            if (sortedRefreshRates[0]->vsyncPeriod == 0 ||
-                sortedRefreshRates[1]->vsyncPeriod == 0) {
-                return {};
-            }
+    // 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);
 
-            return std::pair<int, int>(sortedRefreshRates[0]->configId,
-                                       sortedRefreshRates[1]->configId);
-        };
+    // Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
+    // the policy.
+    const RefreshRate& getCurrentRefreshRateByPolicyLocked() const REQUIRES(mLock);
 
-        auto defaultAndPerfConfigs = findDefaultAndPerfConfigs();
-        if (!defaultAndPerfConfigs) {
-            mRefreshRateSwitchingSupported = false;
-            return;
-        }
+    const Policy* getCurrentPolicyLocked() const REQUIRES(mLock);
+    bool isPolicyValid(const Policy& policy);
 
-        mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first];
-        mRefreshRateMap[RefreshRateType::PERFORMANCE] =
-                mRefreshRates[defaultAndPerfConfigs->second];
-    }
-
-    // Whether this device is doing refresh rate switching or not. This must not change after this
-    // object is initialized.
-    bool mRefreshRateSwitchingSupported;
     // The list of refresh rates, indexed by display config ID. This must not change after this
     // object is initialized.
-    std::vector<RefreshRate> mRefreshRates;
-    // The mapping of refresh rate type to RefreshRate. This must not change after this object is
-    // initialized.
-    std::map<RefreshRateType, RefreshRate> mRefreshRateMap;
-    // The ID of 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, so it's
-    // atomic.
-    std::atomic<int> mCurrentConfig;
+    AllRefreshRatesMapType mRefreshRates;
+
+    // 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.
+    const RefreshRate* mCurrentRefreshRate GUARDED_BY(mLock);
+
+    // The policy values will change at runtime. They're set by SurfaceFlinger on the main thread,
+    // and read by the Scheduler (and other objects) on other threads.
+    Policy mDisplayManagerPolicy GUARDED_BY(mLock);
+    std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
+
+    // The min and max refresh rates supported by the device.
+    // This will not change at runtime.
+    const RefreshRate* mMinSupportedRefreshRate;
+    const RefreshRate* mMaxSupportedRefreshRate;
+
+    mutable std::mutex mLock;
+
+    // A sorted list of known frame rates that a Heuristic layer will choose
+    // from based on the closest value.
+    const std::vector<float> mKnownFrameRates;
 };
 
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 947eb08..d9e7b37 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -25,8 +25,7 @@
 #include "android-base/stringprintf.h"
 #include "utils/Timers.h"
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
 
 /**
  * Class to encapsulate statistics about refresh rates that the display is using. When the power
@@ -42,14 +41,15 @@
 
 public:
     RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats,
-                     int currentConfigMode, int currentPowerMode)
+                     HwcConfigIndexType currentConfigId,
+                     android::hardware::graphics::composer::hal::PowerMode currentPowerMode)
           : mRefreshRateConfigs(refreshRateConfigs),
             mTimeStats(timeStats),
-            mCurrentConfigMode(currentConfigMode),
+            mCurrentConfigMode(currentConfigId),
             mCurrentPowerMode(currentPowerMode) {}
 
     // Sets power mode.
-    void setPowerMode(int mode) {
+    void setPowerMode(android::hardware::graphics::composer::hal::PowerMode mode) {
         if (mCurrentPowerMode == mode) {
             return;
         }
@@ -59,12 +59,12 @@
 
     // Sets config mode. If the mode has changed, it records how much time was spent in the previous
     // mode.
-    void setConfigMode(int mode) {
-        if (mCurrentConfigMode == mode) {
+    void setConfigMode(HwcConfigIndexType configId) {
+        if (mCurrentConfigMode == configId) {
             return;
         }
         flushTime();
-        mCurrentConfigMode = mode;
+        mCurrentConfigMode = configId;
     }
 
     // Returns a map between human readable refresh rate and number of seconds the device spent in
@@ -78,11 +78,11 @@
         std::unordered_map<std::string, int64_t> totalTime;
         // Multiple configs may map to the same name, e.g. "60fps". Add the
         // times for such configs together.
-        for (const auto& [config, time] : mConfigModesTotalTime) {
-            totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] = 0;
+        for (const auto& [configId, time] : mConfigModesTotalTime) {
+            totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getName()] = 0;
         }
-        for (const auto& [config, time] : mConfigModesTotalTime) {
-            totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] += time;
+        for (const auto& [configId, time] : mConfigModesTotalTime) {
+            totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getName()] += time;
         }
         totalTime["ScreenOff"] = mScreenOffTime;
         return totalTime;
@@ -90,13 +90,13 @@
 
     // Traverses through the map of config modes and returns how long they've been running in easy
     // to read format.
-    std::string doDump() const {
-        std::ostringstream stream;
-        stream << "+  Refresh rate: running time in seconds\n";
+    void dump(std::string& result) const {
+        std::ostringstream stream("+  Refresh rate: running time in seconds\n");
+
         for (const auto& [name, time] : const_cast<RefreshRateStats*>(this)->getTotalTimes()) {
             stream << name << ": " << getDateFormatFromMs(time) << '\n';
         }
-        return stream.str();
+        result.append(stream.str());
     }
 
 private:
@@ -109,13 +109,14 @@
         mPreviousRecordedTime = currentTime;
 
         uint32_t fps = 0;
-        if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
+        if (mCurrentPowerMode == android::hardware::graphics::composer::hal::PowerMode::ON) {
             // Normal power mode is counted under different config modes.
             if (mConfigModesTotalTime.find(mCurrentConfigMode) == mConfigModesTotalTime.end()) {
                 mConfigModesTotalTime[mCurrentConfigMode] = 0;
             }
             mConfigModesTotalTime[mCurrentConfigMode] += timeElapsedMs;
-            fps = mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps;
+            fps = static_cast<uint32_t>(std::round(
+                    mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).getFps()));
         } else {
             mScreenOffTime += timeElapsedMs;
         }
@@ -139,14 +140,14 @@
     // Aggregate refresh rate statistics for telemetry.
     TimeStats& mTimeStats;
 
-    int mCurrentConfigMode;
-    int32_t mCurrentPowerMode;
+    HwcConfigIndexType mCurrentConfigMode;
+    android::hardware::graphics::composer::hal::PowerMode mCurrentPowerMode;
 
-    std::unordered_map<int /* config */, int64_t /* duration in ms */> mConfigModesTotalTime;
+    std::unordered_map<HwcConfigIndexType /* configId */, int64_t /* duration in ms */>
+            mConfigModesTotalTime;
     int64_t mScreenOffTime = 0;
 
     nsecs_t mPreviousRecordedTime = systemTime();
 };
 
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index b2c069e..5c0ba01 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -14,16 +14,13 @@
  * limitations under the License.
  */
 
+#undef LOG_TAG
+#define LOG_TAG "Scheduler"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "Scheduler.h"
 
-#include <algorithm>
-#include <cinttypes>
-#include <cstdint>
-#include <memory>
-#include <numeric>
-
+#include <android-base/stringprintf.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
@@ -34,131 +31,169 @@
 #include <utils/Timers.h>
 #include <utils/Trace.h>
 
+#include <algorithm>
+#include <cinttypes>
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <numeric>
+
+#include "../Layer.h"
 #include "DispSync.h"
 #include "DispSyncSource.h"
 #include "EventControlThread.h"
 #include "EventThread.h"
-#include "IdleTimer.h"
 #include "InjectVSyncSource.h"
-#include "LayerInfo.h"
+#include "OneShotTimer.h"
 #include "SchedulerUtils.h"
 #include "SurfaceFlingerProperties.h"
+#include "Timer.h"
+#include "VSyncDispatchTimerQueue.h"
+#include "VSyncPredictor.h"
+#include "VSyncReactor.h"
+
+#define RETURN_IF_INVALID_HANDLE(handle, ...)                        \
+    do {                                                             \
+        if (mConnections.count(handle) == 0) {                       \
+            ALOGE("Invalid connection handle %" PRIuPTR, handle.id); \
+            return __VA_ARGS__;                                      \
+        }                                                            \
+    } while (false)
 
 namespace android {
 
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-using namespace android::sysprop;
+std::unique_ptr<DispSync> createDispSync(bool supportKernelTimer) {
+    // TODO (140302863) remove this and use the vsync_reactor system.
+    if (property_get_bool("debug.sf.vsync_reactor", true)) {
+        // TODO (144707443) tune Predictor tunables.
+        static constexpr int defaultRate = 60;
+        static constexpr auto initialPeriod =
+                std::chrono::duration<nsecs_t, std::ratio<1, defaultRate>>(1);
+        static constexpr size_t vsyncTimestampHistorySize = 20;
+        static constexpr size_t minimumSamplesForPrediction = 6;
+        static constexpr uint32_t discardOutlierPercent = 20;
+        auto tracker = std::make_unique<
+                scheduler::VSyncPredictor>(std::chrono::duration_cast<std::chrono::nanoseconds>(
+                                                   initialPeriod)
+                                                   .count(),
+                                           vsyncTimestampHistorySize, minimumSamplesForPrediction,
+                                           discardOutlierPercent);
 
-#define RETURN_VALUE_IF_INVALID(value) \
-    if (handle == nullptr || mConnections.count(handle->id) == 0) return value
-#define RETURN_IF_INVALID() \
-    if (handle == nullptr || mConnections.count(handle->id) == 0) return
+        static constexpr auto vsyncMoveThreshold =
+                std::chrono::duration_cast<std::chrono::nanoseconds>(3ms);
+        static constexpr auto timerSlack =
+                std::chrono::duration_cast<std::chrono::nanoseconds>(500us);
+        auto dispatch = std::make_unique<
+                scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), *tracker,
+                                                    timerSlack.count(), vsyncMoveThreshold.count());
 
-std::atomic<int64_t> Scheduler::sNextId = 0;
+        static constexpr size_t pendingFenceLimit = 20;
+        return std::make_unique<scheduler::VSyncReactor>(std::make_unique<scheduler::SystemClock>(),
+                                                         std::move(dispatch), std::move(tracker),
+                                                         pendingFenceLimit, supportKernelTimer);
+    } else {
+        return std::make_unique<impl::DispSync>("SchedulerDispSync",
+                                                sysprop::running_without_sync_framework(true));
+    }
+}
 
 Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
-                     const scheduler::RefreshRateConfigs& refreshRateConfig)
-      : mHasSyncFramework(running_without_sync_framework(true)),
-        mDispSyncPresentTimeOffset(present_time_offset_from_vsync_ns(0)),
-        mPrimaryHWVsyncEnabled(false),
-        mHWVsyncAvailable(false),
-        mRefreshRateConfigs(refreshRateConfig) {
-    // Note: We create a local temporary with the real DispSync implementation
-    // type temporarily so we can initialize it with the configured values,
-    // before storing it for more generic use using the interface type.
-    auto primaryDispSync = std::make_unique<impl::DispSync>("SchedulerDispSync");
-    primaryDispSync->init(mHasSyncFramework, mDispSyncPresentTimeOffset);
-    mPrimaryDispSync = std::move(primaryDispSync);
-    mEventControlThread = std::make_unique<impl::EventControlThread>(function);
+                     const scheduler::RefreshRateConfigs& refreshRateConfig,
+                     ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
+                     bool useContentDetection)
+      : mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
+        mPrimaryDispSync(createDispSync(mSupportKernelTimer)),
+        mEventControlThread(new impl::EventControlThread(std::move(function))),
+        mSchedulerCallback(schedulerCallback),
+        mRefreshRateConfigs(refreshRateConfig),
+        mUseContentDetection(useContentDetection),
+        mUseContentDetectionV2(useContentDetectionV2) {
+    using namespace sysprop;
 
-    mSetIdleTimerMs = set_idle_timer_ms(0);
-    mSupportKernelTimer = support_kernel_idle_timer(false);
-
-    mSetTouchTimerMs = set_touch_timer_ms(0);
-    mSetDisplayPowerTimerMs = set_display_power_timer_ms(0);
-
-    char value[PROPERTY_VALUE_MAX];
-    property_get("debug.sf.set_idle_timer_ms", value, "0");
-    int int_value = atoi(value);
-    if (int_value) {
-        mSetIdleTimerMs = atoi(value);
+    if (mUseContentDetectionV2) {
+        mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(refreshRateConfig);
+    } else {
+        mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
     }
 
-    if (mSetIdleTimerMs > 0) {
-        if (mSupportKernelTimer) {
-            mIdleTimer =
-                    std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
-                                                                   mSetIdleTimerMs),
-                                                           [this] { resetKernelTimerCallback(); },
-                                                           [this] {
-                                                               expiredKernelTimerCallback();
-                                                           });
-        } else {
-            mIdleTimer = std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
-                                                                        mSetIdleTimerMs),
-                                                                [this] { resetTimerCallback(); },
-                                                                [this] { expiredTimerCallback(); });
-        }
+    const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
+
+    if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
+        const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback
+                                                  : &Scheduler::idleTimerCallback;
+        mIdleTimer.emplace(
+                std::chrono::milliseconds(millis),
+                [this, callback] { std::invoke(callback, this, TimerState::Reset); },
+                [this, callback] { std::invoke(callback, this, TimerState::Expired); });
         mIdleTimer->start();
     }
 
-    if (mSetTouchTimerMs > 0) {
+    if (const int64_t millis = set_touch_timer_ms(0); millis > 0) {
         // Touch events are coming to SF every 100ms, so the timer needs to be higher than that
-        mTouchTimer =
-                std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetTouchTimerMs),
-                                                       [this] { resetTouchTimerCallback(); },
-                                                       [this] { expiredTouchTimerCallback(); });
+        mTouchTimer.emplace(
+                std::chrono::milliseconds(millis),
+                [this] { touchTimerCallback(TimerState::Reset); },
+                [this] { touchTimerCallback(TimerState::Expired); });
         mTouchTimer->start();
     }
 
-    if (mSetDisplayPowerTimerMs > 0) {
-        mDisplayPowerTimer =
-                std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
-                                                               mSetDisplayPowerTimerMs),
-                                                       [this] { resetDisplayPowerTimerCallback(); },
-                                                       [this] {
-                                                           expiredDisplayPowerTimerCallback();
-                                                       });
+    if (const int64_t millis = set_display_power_timer_ms(0); millis > 0) {
+        mDisplayPowerTimer.emplace(
+                std::chrono::milliseconds(millis),
+                [this] { displayPowerTimerCallback(TimerState::Reset); },
+                [this] { displayPowerTimerCallback(TimerState::Expired); });
         mDisplayPowerTimer->start();
     }
 }
 
+Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
+                     std::unique_ptr<EventControlThread> eventControlThread,
+                     const scheduler::RefreshRateConfigs& configs,
+                     ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
+                     bool useContentDetection)
+      : mSupportKernelTimer(false),
+        mPrimaryDispSync(std::move(primaryDispSync)),
+        mEventControlThread(std::move(eventControlThread)),
+        mSchedulerCallback(schedulerCallback),
+        mRefreshRateConfigs(configs),
+        mUseContentDetection(useContentDetection),
+        mUseContentDetectionV2(useContentDetectionV2) {}
+
 Scheduler::~Scheduler() {
-    // Ensure the IdleTimer thread is joined before we start destroying state.
+    // Ensure the OneShotTimer threads are joined before we start destroying state.
     mDisplayPowerTimer.reset();
     mTouchTimer.reset();
     mIdleTimer.reset();
 }
 
-sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
-        const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
-        impl::EventThread::InterceptVSyncsCallback interceptCallback) {
-    const int64_t id = sNextId++;
-    ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id);
-
-    std::unique_ptr<EventThread> eventThread =
-            makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs,
-                            offsetThresholdForNextVsync, std::move(interceptCallback));
-
-    auto eventThreadConnection =
-            createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
-    mConnections.emplace(id,
-                         std::make_unique<Connection>(new ConnectionHandle(id),
-                                                      eventThreadConnection,
-                                                      std::move(eventThread)));
-    return mConnections[id]->handle;
+DispSync& Scheduler::getPrimaryDispSync() {
+    return *mPrimaryDispSync;
 }
 
-std::unique_ptr<EventThread> Scheduler::makeEventThread(
-        const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs,
-        nsecs_t offsetThresholdForNextVsync,
+std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(const char* name,
+                                                                  nsecs_t phaseOffsetNs) {
+    return std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs,
+                                            true /* traceVsync */, name);
+}
+
+Scheduler::ConnectionHandle Scheduler::createConnection(
+        const char* connectionName, nsecs_t phaseOffsetNs,
         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
-    std::unique_ptr<VSyncSource> eventThreadSource =
-            std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, offsetThresholdForNextVsync,
-                                             true, connectionName);
-    return std::make_unique<impl::EventThread>(std::move(eventThreadSource),
-                                               std::move(interceptCallback), connectionName);
+    auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs);
+    auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
+                                                           std::move(interceptCallback));
+    return createConnection(std::move(eventThread));
+}
+
+Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
+    const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
+    ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
+
+    auto connection =
+            createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
+
+    mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
+    return handle;
 }
 
 sp<EventThreadConnection> Scheduler::createConnectionInternal(
@@ -167,59 +202,117 @@
 }
 
 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
-        const sp<Scheduler::ConnectionHandle>& handle,
-        ISurfaceComposer::ConfigChanged configChanged) {
-    RETURN_VALUE_IF_INVALID(nullptr);
-    return createConnectionInternal(mConnections[handle->id]->thread.get(), configChanged);
+        ConnectionHandle handle, ISurfaceComposer::ConfigChanged configChanged) {
+    RETURN_IF_INVALID_HANDLE(handle, nullptr);
+    return createConnectionInternal(mConnections[handle].thread.get(), configChanged);
 }
 
-EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
-    RETURN_VALUE_IF_INVALID(nullptr);
-    return mConnections[handle->id]->thread.get();
+sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
+    RETURN_IF_INVALID_HANDLE(handle, nullptr);
+    return mConnections[handle].connection;
 }
 
-sp<EventThreadConnection> Scheduler::getEventConnection(const sp<ConnectionHandle>& handle) {
-    RETURN_VALUE_IF_INVALID(nullptr);
-    return mConnections[handle->id]->eventConnection;
+void Scheduler::onHotplugReceived(ConnectionHandle handle, PhysicalDisplayId displayId,
+                                  bool connected) {
+    RETURN_IF_INVALID_HANDLE(handle);
+    mConnections[handle].thread->onHotplugReceived(displayId, connected);
 }
 
-void Scheduler::hotplugReceived(const sp<Scheduler::ConnectionHandle>& handle,
-                                PhysicalDisplayId displayId, bool connected) {
-    RETURN_IF_INVALID();
-    mConnections[handle->id]->thread->onHotplugReceived(displayId, connected);
+void Scheduler::onScreenAcquired(ConnectionHandle handle) {
+    RETURN_IF_INVALID_HANDLE(handle);
+    mConnections[handle].thread->onScreenAcquired();
 }
 
-void Scheduler::onScreenAcquired(const sp<Scheduler::ConnectionHandle>& handle) {
-    RETURN_IF_INVALID();
-    mConnections[handle->id]->thread->onScreenAcquired();
+void Scheduler::onScreenReleased(ConnectionHandle handle) {
+    RETURN_IF_INVALID_HANDLE(handle);
+    mConnections[handle].thread->onScreenReleased();
 }
 
-void Scheduler::onScreenReleased(const sp<Scheduler::ConnectionHandle>& handle) {
-    RETURN_IF_INVALID();
-    mConnections[handle->id]->thread->onScreenReleased();
+void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+                                              HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+    std::lock_guard<std::mutex> lock(mFeatureStateLock);
+    // Cache the last reported config for primary display.
+    mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod};
+    onNonPrimaryDisplayConfigChanged(handle, displayId, configId, vsyncPeriod);
 }
 
-void Scheduler::onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
-                                int32_t configId) {
-    RETURN_IF_INVALID();
-    mConnections[handle->id]->thread->onConfigChanged(displayId, configId);
+void Scheduler::dispatchCachedReportedConfig() {
+    const auto configId = *mFeatures.configId;
+    const auto vsyncPeriod =
+            mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getVsyncPeriod();
+
+    // If there is no change from cached config, there is no need to dispatch an event
+    if (configId == mFeatures.cachedConfigChangedParams->configId &&
+        vsyncPeriod == mFeatures.cachedConfigChangedParams->vsyncPeriod) {
+        return;
+    }
+
+    mFeatures.cachedConfigChangedParams->configId = configId;
+    mFeatures.cachedConfigChangedParams->vsyncPeriod = vsyncPeriod;
+    onNonPrimaryDisplayConfigChanged(mFeatures.cachedConfigChangedParams->handle,
+                                     mFeatures.cachedConfigChangedParams->displayId,
+                                     mFeatures.cachedConfigChangedParams->configId,
+                                     mFeatures.cachedConfigChangedParams->vsyncPeriod);
 }
 
-void Scheduler::dump(const sp<Scheduler::ConnectionHandle>& handle, std::string& result) const {
-    RETURN_IF_INVALID();
-    mConnections.at(handle->id)->thread->dump(result);
+void Scheduler::onNonPrimaryDisplayConfigChanged(ConnectionHandle handle,
+                                                 PhysicalDisplayId displayId,
+                                                 HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+    RETURN_IF_INVALID_HANDLE(handle);
+    mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod);
 }
 
-void Scheduler::setPhaseOffset(const sp<Scheduler::ConnectionHandle>& handle, nsecs_t phaseOffset) {
-    RETURN_IF_INVALID();
-    mConnections[handle->id]->thread->setPhaseOffset(phaseOffset);
+size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
+    RETURN_IF_INVALID_HANDLE(handle, 0);
+    return mConnections[handle].thread->getEventThreadConnectionCount();
+}
+
+void Scheduler::dump(ConnectionHandle handle, std::string& result) const {
+    RETURN_IF_INVALID_HANDLE(handle);
+    mConnections.at(handle).thread->dump(result);
+}
+
+void Scheduler::setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
+    RETURN_IF_INVALID_HANDLE(handle);
+    mConnections[handle].thread->setPhaseOffset(phaseOffset);
 }
 
 void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) {
-    stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0);
+    stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0, systemTime());
     stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
 }
 
+Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
+    if (mInjectVSyncs == enable) {
+        return {};
+    }
+
+    ALOGV("%s VSYNC injection", enable ? "Enabling" : "Disabling");
+
+    if (!mInjectorConnectionHandle) {
+        auto vsyncSource = std::make_unique<InjectVSyncSource>();
+        mVSyncInjector = vsyncSource.get();
+
+        auto eventThread =
+                std::make_unique<impl::EventThread>(std::move(vsyncSource),
+                                                    impl::EventThread::InterceptVSyncsCallback());
+
+        mInjectorConnectionHandle = createConnection(std::move(eventThread));
+    }
+
+    mInjectVSyncs = enable;
+    return mInjectorConnectionHandle;
+}
+
+bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime) {
+    if (!mInjectVSyncs || !mVSyncInjector) {
+        return false;
+    }
+
+    mVSyncInjector->onInjectSyncEvent(when, expectedVSyncTime);
+    return true;
+}
+
 void Scheduler::enableHardwareVsync() {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
     if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
@@ -267,16 +360,11 @@
     const nsecs_t last = mLastResyncTime.exchange(now);
 
     if (now - last > kIgnoreDelay) {
-        resyncToHardwareVsync(false,
-                              mRefreshRateConfigs.getCurrentRefreshRate().second.vsyncPeriod);
+        resyncToHardwareVsync(false, mRefreshRateConfigs.getCurrentRefreshRate().getVsyncPeriod());
     }
 }
 
-void Scheduler::setRefreshSkipCount(int count) {
-    mPrimaryDispSync->setRefreshSkipCount(count);
-}
-
-void Scheduler::setVsyncPeriod(const nsecs_t period) {
+void Scheduler::setVsyncPeriod(nsecs_t period) {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
     mPrimaryDispSync->setPeriod(period);
 
@@ -287,13 +375,15 @@
     }
 }
 
-void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodFlushed) {
+void Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                                bool* periodFlushed) {
     bool needsHwVsync = false;
     *periodFlushed = false;
     { // Scope for the lock
         std::lock_guard<std::mutex> lock(mHWVsyncLock);
         if (mPrimaryHWVsyncEnabled) {
-            needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodFlushed);
+            needsHwVsync =
+                    mPrimaryDispSync->addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
         }
     }
 
@@ -316,86 +406,89 @@
     mPrimaryDispSync->setIgnorePresentFences(ignore);
 }
 
-nsecs_t Scheduler::getDispSyncExpectedPresentTime() {
-    return mPrimaryDispSync->expectedPresentTime();
+nsecs_t Scheduler::getDispSyncExpectedPresentTime(nsecs_t now) {
+    return mPrimaryDispSync->expectedPresentTime(now);
 }
 
-void Scheduler::dumpPrimaryDispSync(std::string& result) const {
-    mPrimaryDispSync->dump(result);
-}
+void Scheduler::registerLayer(Layer* layer) {
+    if (!mLayerHistory) return;
 
-std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer(
-        std::string const& name, int windowType) {
-    uint32_t defaultFps, performanceFps;
-    if (mRefreshRateConfigs.refreshRateSwitchingSupported()) {
-        defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
-        performanceFps =
-                mRefreshRateConfigs
-                        .getRefreshRateFromType((windowType == InputWindowInfo::TYPE_WALLPAPER)
-                                                        ? RefreshRateType::DEFAULT
-                                                        : RefreshRateType::PERFORMANCE)
-                        .fps;
+    const auto minFps = mRefreshRateConfigs.getMinRefreshRate().getFps();
+    const auto maxFps = mRefreshRateConfigs.getMaxRefreshRate().getFps();
+
+    if (layer->getWindowType() == InputWindowInfo::TYPE_STATUS_BAR) {
+        mLayerHistory->registerLayer(layer, minFps, maxFps,
+                                     scheduler::LayerHistory::LayerVoteType::NoVote);
+    } else if (!mUseContentDetection) {
+        // If the content detection feature is off, all layers are registered at Max. We still keep
+        // the layer history, since we use it for other features (like Frame Rate API), so layers
+        // still need to be registered.
+        mLayerHistory->registerLayer(layer, minFps, maxFps,
+                                     scheduler::LayerHistory::LayerVoteType::Max);
+    } else if (!mUseContentDetectionV2) {
+        // In V1 of content detection, all layers are registered as Heuristic (unless it's
+        // wallpaper).
+        const auto highFps =
+                layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER ? minFps : maxFps;
+
+        mLayerHistory->registerLayer(layer, minFps, highFps,
+                                     scheduler::LayerHistory::LayerVoteType::Heuristic);
     } else {
-        defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps;
-        performanceFps = defaultFps;
+        if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) {
+            // Running Wallpaper at Min is considered as part of content detection.
+            mLayerHistory->registerLayer(layer, minFps, maxFps,
+                                         scheduler::LayerHistory::LayerVoteType::Min);
+        } else {
+            mLayerHistory->registerLayer(layer, minFps, maxFps,
+                                         scheduler::LayerHistory::LayerVoteType::Heuristic);
+        }
     }
-    return mLayerHistory.createLayer(name, defaultFps, performanceFps);
 }
 
-void Scheduler::addLayerPresentTimeAndHDR(
-        const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
-        nsecs_t presentTime, bool isHDR) {
-    mLayerHistory.insert(layerHandle, presentTime, isHDR);
+void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
+                                   LayerHistory::LayerUpdateType updateType) {
+    if (mLayerHistory) {
+        mLayerHistory->record(layer, presentTime, systemTime(), updateType);
+    }
 }
 
-void Scheduler::setLayerVisibility(
-        const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible) {
-    mLayerHistory.setVisibility(layerHandle, visible);
+void Scheduler::setConfigChangePending(bool pending) {
+    if (mLayerHistory) {
+        mLayerHistory->setConfigChangePending(pending);
+    }
 }
 
-void Scheduler::withPrimaryDispSync(std::function<void(DispSync&)> const& fn) {
-    fn(*mPrimaryDispSync);
-}
+void Scheduler::chooseRefreshRateForContent() {
+    if (!mLayerHistory) return;
 
-void Scheduler::updateFpsBasedOnContent() {
-    auto [refreshRate, isHDR] = mLayerHistory.getDesiredRefreshRateAndHDR();
-    const uint32_t refreshRateRound = std::round(refreshRate);
-    RefreshRateType newRefreshRateType;
+    ATRACE_CALL();
+
+    scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
+    HwcConfigIndexType newConfigId;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        if (mContentRefreshRate == refreshRateRound && mIsHDRContent == isHDR) {
+        if (mFeatures.contentRequirements == summary) {
             return;
         }
-        mContentRefreshRate = refreshRateRound;
-        ATRACE_INT("ContentFPS", mContentRefreshRate);
+        mFeatures.contentRequirements = summary;
+        mFeatures.contentDetectionV1 =
+                !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
 
-        mIsHDRContent = isHDR;
-        ATRACE_INT("ContentHDR", mIsHDRContent);
-
-        mCurrentContentFeatureState = refreshRateRound > 0
-                ? ContentFeatureState::CONTENT_DETECTION_ON
-                : ContentFeatureState::CONTENT_DETECTION_OFF;
-        newRefreshRateType = calculateRefreshRateType();
-        if (mRefreshRateType == newRefreshRateType) {
+        scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
+        newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
+        if (mFeatures.configId == newConfigId) {
+            // We don't need to change the config, but we might need to send an event
+            // about a config change, since it was suppressed due to a previous idleConsidered
+            if (!consideredSignals.idle) {
+                dispatchCachedReportedConfig();
+            }
             return;
         }
-        mRefreshRateType = newRefreshRateType;
-    }
-    changeRefreshRate(newRefreshRateType, ConfigEvent::Changed);
-}
-
-void Scheduler::setChangeRefreshRateCallback(
-        const ChangeRefreshRateCallback&& changeRefreshRateCallback) {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    mChangeRefreshRateCallback = changeRefreshRateCallback;
-}
-
-void Scheduler::updateFrameSkipping(const int64_t skipCount) {
-    ATRACE_INT("FrameSkipCount", skipCount);
-    if (mSkipCount != skipCount) {
-        // Only update DispSync if it hasn't been updated yet.
-        mPrimaryDispSync->setRefreshSkipCount(skipCount);
-        mSkipCount = skipCount;
+        mFeatures.configId = newConfigId;
+        auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        mSchedulerCallback.changeRefreshRate(newRefreshRate,
+                                             consideredSignals.idle ? ConfigEvent::None
+                                                                    : ConfigEvent::Changed);
     }
 }
 
@@ -406,23 +499,28 @@
 }
 
 void Scheduler::notifyTouchEvent() {
-    if (mTouchTimer) {
-        mTouchTimer->reset();
-    }
-
-    if (mSupportKernelTimer) {
-        resetIdleTimer();
-    }
+    if (!mTouchTimer) return;
 
     // Touch event will boost the refresh rate to performance.
-    // Clear Layer History to get fresh FPS detection
-    mLayerHistory.clearHistory();
+    // Clear Layer History to get fresh FPS detection.
+    // NOTE: Instead of checking all the layers, we should be checking the layer
+    // 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::getBestRefreshRate
+
+        mTouchTimer->reset();
+
+        if (mSupportKernelTimer && mIdleTimer) {
+            mIdleTimer->reset();
+        }
+    }
 }
 
 void Scheduler::setDisplayPowerState(bool normal) {
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        mIsDisplayPowerStateNormal = normal;
+        mFeatures.isDisplayPowerStateNormal = normal;
     }
 
     if (mDisplayPowerTimer) {
@@ -431,157 +529,182 @@
 
     // Display Power event will boost the refresh rate to performance.
     // Clear Layer History to get fresh FPS detection
-    mLayerHistory.clearHistory();
+    if (mLayerHistory) {
+        mLayerHistory->clear();
+    }
 }
 
-void Scheduler::resetTimerCallback() {
-    handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::RESET, false);
-    ATRACE_INT("ExpiredIdleTimer", 0);
-}
+void Scheduler::kernelIdleTimerCallback(TimerState state) {
+    ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
 
-void Scheduler::resetKernelTimerCallback() {
-    ATRACE_INT("ExpiredKernelIdleTimer", 0);
-    const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
-    if (refreshRate.first == RefreshRateType::PERFORMANCE) {
+    // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
+    // magic number
+    const auto& refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
+    constexpr float FPS_THRESHOLD_FOR_KERNEL_TIMER = 65.0f;
+    if (state == TimerState::Reset && refreshRate.getFps() > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
         // If we're not in performance mode then the kernel timer shouldn't do
         // anything, as the refresh rate during DPU power collapse will be the
         // same.
-        resyncToHardwareVsync(true, refreshRate.second.vsyncPeriod);
+        resyncToHardwareVsync(true /* makeAvailable */, refreshRate.getVsyncPeriod());
+    } else if (state == TimerState::Expired &&
+               refreshRate.getFps() <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
+        // Disable HW VSYNC if the timer expired, as we don't need it enabled if
+        // we're not pushing frames, and if we're in PERFORMANCE mode then we'll
+        // need to update the DispSync model anyway.
+        disableHardwareVsync(false /* makeUnavailable */);
     }
+
+    mSchedulerCallback.kernelTimerChanged(state == TimerState::Expired);
 }
 
-void Scheduler::expiredTimerCallback() {
-    handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::EXPIRED, false);
-    ATRACE_INT("ExpiredIdleTimer", 1);
+void Scheduler::idleTimerCallback(TimerState state) {
+    handleTimerStateChanged(&mFeatures.idleTimer, state);
+    ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
 }
 
-void Scheduler::resetTouchTimerCallback() {
-    handleTimerStateChanged(&mCurrentTouchState, TouchState::ACTIVE, true);
-    ATRACE_INT("TouchState", 1);
-}
-
-void Scheduler::expiredTouchTimerCallback() {
-    handleTimerStateChanged(&mCurrentTouchState, TouchState::INACTIVE, true);
-    ATRACE_INT("TouchState", 0);
-}
-
-void Scheduler::resetDisplayPowerTimerCallback() {
-    handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::RESET, true);
-    ATRACE_INT("ExpiredDisplayPowerTimer", 0);
-}
-
-void Scheduler::expiredDisplayPowerTimerCallback() {
-    handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::EXPIRED, true);
-    ATRACE_INT("ExpiredDisplayPowerTimer", 1);
-}
-
-void Scheduler::expiredKernelTimerCallback() {
-    ATRACE_INT("ExpiredKernelIdleTimer", 1);
-    const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
-    if (refreshRate.first != RefreshRateType::PERFORMANCE) {
-        // Disable HW Vsync if the timer expired, as we don't need it
-        // enabled if we're not pushing frames, and if we're in PERFORMANCE
-        // mode then we'll need to re-update the DispSync model anyways.
-        disableHardwareVsync(false);
+void Scheduler::touchTimerCallback(TimerState state) {
+    const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive;
+    if (handleTimerStateChanged(&mFeatures.touch, touch)) {
+        mLayerHistory->clear();
     }
+    ATRACE_INT("TouchState", static_cast<int>(touch));
 }
 
-std::string Scheduler::doDump() {
-    std::ostringstream stream;
-    stream << "+  Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl;
-    stream << "+  Touch timer interval: " << mSetTouchTimerMs << " ms" << std::endl;
-    return stream.str();
+void Scheduler::displayPowerTimerCallback(TimerState state) {
+    handleTimerStateChanged(&mFeatures.displayPowerTimer, state);
+    ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
+}
+
+void Scheduler::dump(std::string& result) const {
+    using base::StringAppendF;
+    const char* const states[] = {"off", "on"};
+
+    StringAppendF(&result, "+  Idle timer: %s\n",
+                  mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
+    StringAppendF(&result, "+  Touch timer: %s\n",
+                  mTouchTimer ? mTouchTimer->dump().c_str() : states[0]);
+    StringAppendF(&result, "+  Use content detection: %s\n\n",
+                  sysprop::use_content_detection_for_refresh_rate(false) ? "on" : "off");
 }
 
 template <class T>
-void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
-    ConfigEvent event = ConfigEvent::None;
-    RefreshRateType newRefreshRateType;
+bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
+    HwcConfigIndexType newConfigId;
+    scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
         if (*currentState == newState) {
-            return;
+            return false;
         }
         *currentState = newState;
-        newRefreshRateType = calculateRefreshRateType();
-        if (mRefreshRateType == newRefreshRateType) {
-            return;
-        }
-        mRefreshRateType = newRefreshRateType;
-        if (eventOnContentDetection &&
-            mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) {
-            event = ConfigEvent::Changed;
-        }
-    }
-    changeRefreshRate(newRefreshRateType, event);
-}
-
-Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
-    if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
-        return RefreshRateType::DEFAULT;
-    }
-
-    // HDR content is not supported on PERFORMANCE mode
-    if (mForceHDRContentToDefaultRefreshRate && mIsHDRContent) {
-        return RefreshRateType::DEFAULT;
-    }
-
-    // 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 (!mIsDisplayPowerStateNormal || mDisplayPowerTimerState == DisplayPowerTimerState::RESET) {
-        return RefreshRateType::PERFORMANCE;
-    }
-
-    // As long as touch is active we want to be in performance mode
-    if (mCurrentTouchState == TouchState::ACTIVE) {
-        return RefreshRateType::PERFORMANCE;
-    }
-
-    // If timer has expired as it means there is no new content on the screen
-    if (mCurrentIdleTimerState == IdleTimerState::EXPIRED) {
-        return RefreshRateType::DEFAULT;
-    }
-
-    // If content detection is off we choose performance as we don't know the content fps
-    if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_OFF) {
-        return RefreshRateType::PERFORMANCE;
-    }
-
-    // Content detection is on, find the appropriate refresh rate with minimal error
-    auto begin = mRefreshRateConfigs.getRefreshRateMap().cbegin();
-
-    auto iter = min_element(begin, mRefreshRateConfigs.getRefreshRateMap().cend(),
-                            [rate = mContentRefreshRate](const auto& l, const auto& r) -> bool {
-                                return std::abs(l.second.fps - static_cast<float>(rate)) <
-                                        std::abs(r.second.fps - static_cast<float>(rate));
-                            });
-    RefreshRateType currRefreshRateType = iter->first;
-
-    // Some content aligns better on higher refresh rate. For example for 45fps we should choose
-    // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
-    // align well with both
-    constexpr float MARGIN = 0.05f;
-    float ratio = mRefreshRateConfigs.getRefreshRateFromType(currRefreshRateType).fps /
-            float(mContentRefreshRate);
-    if (std::abs(std::round(ratio) - ratio) > MARGIN) {
-        while (iter != mRefreshRateConfigs.getRefreshRateMap().cend()) {
-            ratio = iter->second.fps / float(mContentRefreshRate);
-
-            if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
-                currRefreshRateType = iter->first;
-                break;
+        newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
+        if (mFeatures.configId == newConfigId) {
+            // We don't need to change the config, but we might need to send an event
+            // about a config change, since it was suppressed due to a previous idleConsidered
+            if (!consideredSignals.idle) {
+                dispatchCachedReportedConfig();
             }
-            ++iter;
+            return consideredSignals.touch;
+        }
+        mFeatures.configId = newConfigId;
+    }
+    const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+    mSchedulerCallback.changeRefreshRate(newRefreshRate,
+                                         consideredSignals.idle ? ConfigEvent::None
+                                                                : ConfigEvent::Changed);
+    return consideredSignals.touch;
+}
+
+HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(
+        scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) {
+    ATRACE_CALL();
+    if (consideredSignals) *consideredSignals = {};
+
+    // 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 (touchActive) {
+            return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
+        }
+
+        // If timer has expired as it means there is no new content on the screen.
+        if (idle) {
+            if (consideredSignals) consideredSignals->idle = true;
+            return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId();
+        }
+
+        // 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.
+            return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId();
+        }
+
+        // Content detection is on, find the appropriate refresh rate with minimal error
+        return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements)
+                .getConfigId();
+    }
+
+    return mRefreshRateConfigs
+            .getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle},
+                                consideredSignals)
+            .getConfigId();
+}
+
+std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
+    std::lock_guard<std::mutex> lock(mFeatureStateLock);
+    // Make sure that the default config ID is first updated, before returned.
+    if (mFeatures.configId.has_value()) {
+        mFeatures.configId = calculateRefreshRateConfigIndexType();
+    }
+    return mFeatures.configId;
+}
+
+void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
+    if (timeline.refreshRequired) {
+        mSchedulerCallback.repaintEverythingForHWC();
+    }
+
+    std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
+    mLastVsyncPeriodChangeTimeline = std::make_optional(timeline);
+
+    const auto maxAppliedTime = systemTime() + MAX_VSYNC_APPLIED_TIME.count();
+    if (timeline.newVsyncAppliedTimeNanos > maxAppliedTime) {
+        mLastVsyncPeriodChangeTimeline->newVsyncAppliedTimeNanos = maxAppliedTime;
+    }
+}
+
+void Scheduler::onDisplayRefreshed(nsecs_t timestamp) {
+    bool callRepaint = false;
+    {
+        std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
+        if (mLastVsyncPeriodChangeTimeline && mLastVsyncPeriodChangeTimeline->refreshRequired) {
+            if (mLastVsyncPeriodChangeTimeline->refreshTimeNanos < timestamp) {
+                mLastVsyncPeriodChangeTimeline->refreshRequired = false;
+            } else {
+                // We need to send another refresh as refreshTimeNanos is still in the future
+                callRepaint = true;
+            }
         }
     }
 
-    return currRefreshRateType;
+    if (callRepaint) {
+        mSchedulerCallback.repaintEverythingForHWC();
+    }
 }
 
-void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    if (mChangeRefreshRateCallback) {
-        mChangeRefreshRateCallback(refreshRateType, configEvent);
+void Scheduler::onPrimaryDisplayAreaChanged(uint32_t displayArea) {
+    if (mLayerHistory) {
+        mLayerHistory->setDisplayArea(displayArea);
     }
 }
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index da0a015..730ea8f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -16,114 +16,104 @@
 
 #pragma once
 
-#include <cstdint>
+#include <atomic>
 #include <functional>
 #include <memory>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
 
-#include <ui/DisplayStatInfo.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 #include <ui/GraphicTypes.h>
+#pragma clang diagnostic pop
 
-#include "DispSync.h"
 #include "EventControlThread.h"
 #include "EventThread.h"
-#include "IdleTimer.h"
-#include "InjectVSyncSource.h"
 #include "LayerHistory.h"
+#include "OneShotTimer.h"
 #include "RefreshRateConfigs.h"
 #include "SchedulerUtils.h"
 
 namespace android {
 
-class EventControlThread;
+using namespace std::chrono_literals;
+using scheduler::LayerHistory;
 
-class Scheduler {
+class DispSync;
+class FenceTime;
+class InjectVSyncSource;
+struct DisplayStateInfo;
+
+class ISchedulerCallback {
 public:
-    // Enum to keep track of whether we trigger event to notify choreographer of config changes.
-    enum class ConfigEvent { None, Changed };
+    virtual ~ISchedulerCallback() = default;
+    virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
+                                   scheduler::RefreshRateConfigEvent) = 0;
+    virtual void repaintEverythingForHWC() = 0;
+    virtual void kernelTimerChanged(bool expired) = 0;
+};
 
-    // logical or operator with the semantics of at least one of the events is Changed
-    friend ConfigEvent operator|(const ConfigEvent& first, const ConfigEvent& second) {
-        if (first == ConfigEvent::Changed) return ConfigEvent::Changed;
-        if (second == ConfigEvent::Changed) return ConfigEvent::Changed;
-        return ConfigEvent::None;
-    }
+class IPhaseOffsetControl {
+public:
+    virtual ~IPhaseOffsetControl() = default;
+    virtual void setPhaseOffset(scheduler::ConnectionHandle, nsecs_t phaseOffset) = 0;
+};
 
-    using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
-    using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>;
+class Scheduler : public IPhaseOffsetControl {
+public:
+    using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
+    using ConfigEvent = scheduler::RefreshRateConfigEvent;
 
-    // Enum to indicate whether to start the transaction early, or at vsync time.
-    enum class TransactionStart { EARLY, NORMAL };
-
-    /* The scheduler handle is a BBinder object passed to the client from which we can extract
-     * an ID for subsequent operations.
-     */
-    class ConnectionHandle : public BBinder {
-    public:
-        ConnectionHandle(int64_t id) : id(id) {}
-
-        ~ConnectionHandle() = default;
-
-        const int64_t id;
+    // Indicates whether to start the transaction early, or at vsync time.
+    enum class TransactionStart {
+        Early,      // DEPRECATED. Start the transaction early. Times out on its own
+        EarlyStart, // Start the transaction early and keep this config until EarlyEnd
+        EarlyEnd,   // End the early config started at EarlyStart
+        Normal      // Start the transaction at the normal time
     };
 
-    class Connection {
-    public:
-        Connection(sp<ConnectionHandle> handle, sp<EventThreadConnection> eventConnection,
-                   std::unique_ptr<EventThread> eventThread)
-              : handle(handle), eventConnection(eventConnection), thread(std::move(eventThread)) {}
-
-        ~Connection() = default;
-
-        sp<ConnectionHandle> handle;
-        sp<EventThreadConnection> eventConnection;
-        const std::unique_ptr<EventThread> thread;
-    };
-
-    explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
-                       const scheduler::RefreshRateConfigs& refreshRateConfig);
+    Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
+              const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
+              bool useContentDetectionV2, bool useContentDetection);
 
     virtual ~Scheduler();
 
-    /** Creates an EventThread connection. */
-    sp<ConnectionHandle> createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
-                                          nsecs_t offsetThresholdForNextVsync,
-                                          impl::EventThread::InterceptVSyncsCallback);
+    DispSync& getPrimaryDispSync();
 
-    sp<IDisplayEventConnection> createDisplayEventConnection(
-            const sp<ConnectionHandle>& handle, ISurfaceComposer::ConfigChanged configChanged);
+    using ConnectionHandle = scheduler::ConnectionHandle;
+    ConnectionHandle createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
+                                      impl::EventThread::InterceptVSyncsCallback);
 
-    // Getter methods.
-    EventThread* getEventThread(const sp<ConnectionHandle>& handle);
+    sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
+                                                             ISurfaceComposer::ConfigChanged);
 
-    // Provides access to the DispSync object for the primary display.
-    void withPrimaryDispSync(std::function<void(DispSync&)> const& fn);
+    sp<EventThreadConnection> getEventConnection(ConnectionHandle);
 
-    sp<EventThreadConnection> getEventConnection(const sp<ConnectionHandle>& handle);
+    void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
+    void onPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
+                                       HwcConfigIndexType configId, nsecs_t vsyncPeriod)
+            EXCLUDES(mFeatureStateLock);
+    void onNonPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
+                                          HwcConfigIndexType configId, nsecs_t vsyncPeriod);
+    void onScreenAcquired(ConnectionHandle);
+    void onScreenReleased(ConnectionHandle);
 
-    // Should be called when receiving a hotplug event.
-    void hotplugReceived(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
-                         bool connected);
-
-    // Should be called after the screen is turned on.
-    void onScreenAcquired(const sp<ConnectionHandle>& handle);
-
-    // Should be called before the screen is turned off.
-    void onScreenReleased(const sp<ConnectionHandle>& handle);
-
-    // Should be called when display config changed
-    void onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
-                         int32_t configId);
-
-    // Should be called when dumpsys command is received.
-    void dump(const sp<ConnectionHandle>& handle, std::string& result) const;
-
-    // Offers ability to modify phase offset in the event thread.
-    void setPhaseOffset(const sp<ConnectionHandle>& handle, nsecs_t phaseOffset);
+    // Modifies phase offset in the event thread.
+    void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset) override;
 
     void getDisplayStatInfo(DisplayStatInfo* stats);
 
+    // Returns injector handle if injection has toggled, or an invalid handle otherwise.
+    ConnectionHandle enableVSyncInjection(bool enable);
+
+    // Returns false if injection is disabled.
+    bool injectVSync(nsecs_t when, nsecs_t expectedVSyncTime);
+
     void enableHardwareVsync();
     void disableHardwareVsync(bool makeUnavailable);
+
     // Resyncs the scheduler to hardware vsync.
     // If makeAvailable is true, then hardware vsync will be turned on.
     // Otherwise, if hardware vsync is not already enabled then this method will
@@ -131,176 +121,164 @@
     // The period is the vsync period from the current display configuration.
     void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
     void resync();
-    void setRefreshSkipCount(int count);
+
     // Passes a vsync sample to DispSync. periodFlushed will be true if
     // DispSync detected that the vsync period changed, and false otherwise.
-    void addResyncSample(const nsecs_t timestamp, bool* periodFlushed);
-    void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
+    void addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                         bool* periodFlushed);
+    void addPresentFence(const std::shared_ptr<FenceTime>&);
     void setIgnorePresentFences(bool ignore);
-    nsecs_t getDispSyncExpectedPresentTime();
-    // Registers the layer in the scheduler, and returns the handle for future references.
-    std::unique_ptr<scheduler::LayerHistory::LayerHandle> registerLayer(std::string const& name,
-                                                                        int windowType);
+    nsecs_t getDispSyncExpectedPresentTime(nsecs_t now);
 
-    // Stores present time for a layer.
-    void addLayerPresentTimeAndHDR(
-            const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
-            nsecs_t presentTime, bool isHDR);
-    // Stores visibility for a layer.
-    void setLayerVisibility(
-            const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible);
-    // Updates FPS based on the most content presented.
-    void updateFpsBasedOnContent();
-    // Callback that gets invoked when Scheduler wants to change the refresh rate.
-    void setChangeRefreshRateCallback(const ChangeRefreshRateCallback&& changeRefreshRateCallback);
+    // Layers are registered on creation, and unregistered when the weak reference expires.
+    void registerLayer(Layer*);
+    void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType);
+    void setConfigChangePending(bool pending);
 
-    // Returns whether idle timer is enabled or not
-    bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; }
+    // Detects content using layer history, and selects a matching refresh rate.
+    void chooseRefreshRateForContent();
 
-    // Function that resets the idle timer.
+    bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); }
     void resetIdleTimer();
 
     // Function that resets the touch timer.
     void notifyTouchEvent();
 
-    // Function that sets whether display power mode is normal or not.
     void setDisplayPowerState(bool normal);
 
-    // Returns relevant information about Scheduler for dumpsys purposes.
-    std::string doDump();
+    void dump(std::string&) const;
+    void dump(ConnectionHandle, std::string&) const;
 
-    // calls DispSync::dump() on primary disp sync
-    void dumpPrimaryDispSync(std::string& result) const;
+    // Get the appropriate refresh for current conditions.
+    std::optional<HwcConfigIndexType> getPreferredConfigId();
 
-protected:
-    virtual std::unique_ptr<EventThread> makeEventThread(
-            const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs,
-            nsecs_t offsetThresholdForNextVsync,
-            impl::EventThread::InterceptVSyncsCallback interceptCallback);
+    // Notifies the scheduler about a refresh rate timeline change.
+    void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
+
+    // Notifies the scheduler when the display was refreshed
+    void onDisplayRefreshed(nsecs_t timestamp);
+
+    // Notifies the scheduler when the display size has changed. Called from SF's main thread
+    void onPrimaryDisplayAreaChanged(uint32_t displayArea);
+
+    size_t getEventThreadConnectionCount(ConnectionHandle handle);
 
 private:
     friend class TestableScheduler;
 
     // In order to make sure that the features don't override themselves, we need a state machine
     // to keep track which feature requested the config change.
-    enum class ContentFeatureState { CONTENT_DETECTION_ON, CONTENT_DETECTION_OFF };
-    enum class IdleTimerState { EXPIRED, RESET };
-    enum class TouchState { INACTIVE, ACTIVE };
-    enum class DisplayPowerTimerState { EXPIRED, RESET };
+    enum class ContentDetectionState { Off, On };
+    enum class TimerState { Reset, Expired };
+    enum class TouchState { Inactive, Active };
 
-    // Creates a connection on the given EventThread and forwards the given callbacks.
+    // Used by tests to inject mocks.
+    Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
+              const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
+              bool useContentDetectionV2, bool useContentDetection);
+
+    std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
+
+    // Create a connection on the given EventThread.
+    ConnectionHandle createConnection(std::unique_ptr<EventThread>);
     sp<EventThreadConnection> createConnectionInternal(EventThread*,
                                                        ISurfaceComposer::ConfigChanged);
 
-    nsecs_t calculateAverage() const;
-    void updateFrameSkipping(const int64_t skipCount);
+    // Update feature state machine to given state when corresponding timer resets or expires.
+    void kernelIdleTimerCallback(TimerState);
+    void idleTimerCallback(TimerState);
+    void touchTimerCallback(TimerState);
+    void displayPowerTimerCallback(TimerState);
 
-    // Function that is called when the timer resets.
-    void resetTimerCallback();
-    // Function that is called when the timer expires.
-    void expiredTimerCallback();
-    // Function that is called when the timer resets when paired with a display
-    // driver timeout in the kernel. This enables hardware vsync when we move
-    // out from idle.
-    void resetKernelTimerCallback();
-    // Function that is called when the timer expires when paired with a display
-    // driver timeout in the kernel. This disables hardware vsync when we move
-    // into idle.
-    void expiredKernelTimerCallback();
-    // Function that is called when the touch timer resets.
-    void resetTouchTimerCallback();
-    // Function that is called when the touch timer expires.
-    void expiredTouchTimerCallback();
-    // Function that is called when the display power timer resets.
-    void resetDisplayPowerTimerCallback();
-    // Function that is called when the display power timer expires.
-    void expiredDisplayPowerTimerCallback();
-    // Sets vsync period.
-    void setVsyncPeriod(const nsecs_t period);
     // handles various timer features to change the refresh rate.
     template <class T>
-    void handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection);
-    // Calculate the new refresh rate type
-    RefreshRateType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
-    // Acquires a lock and calls the ChangeRefreshRateCallback() with given parameters.
-    void changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent);
+    bool handleTimerStateChanged(T* currentState, T newState);
 
-    // Helper function to calculate error frames
-    float getErrorFrames(float contentFps, float configFps);
+    void setVsyncPeriod(nsecs_t period);
 
-    // If fences from sync Framework are supported.
-    const bool mHasSyncFramework;
+    // This function checks whether individual features that are affecting the refresh rate
+    // selection were initialized, prioritizes them, and calculates the HwcConfigIndexType
+    // for the suggested refresh rate.
+    HwcConfigIndexType calculateRefreshRateConfigIndexType(
+            scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr)
+            REQUIRES(mFeatureStateLock);
 
-    // The offset in nanoseconds to use, when DispSync timestamps present fence
-    // signaling time.
-    nsecs_t mDispSyncPresentTimeOffset;
+    void dispatchCachedReportedConfig() REQUIRES(mFeatureStateLock);
 
-    // Each connection has it's own ID. This variable keeps track of the count.
-    static std::atomic<int64_t> sNextId;
+    // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
+    struct Connection {
+        sp<EventThreadConnection> connection;
+        std::unique_ptr<EventThread> thread;
+    };
 
-    // Connections are stored in a map <connection ID, connection> for easy retrieval.
-    std::unordered_map<int64_t, std::unique_ptr<Connection>> mConnections;
+    ConnectionHandle::Id mNextConnectionHandleId = 0;
+    std::unordered_map<ConnectionHandle, Connection> mConnections;
+
+    bool mInjectVSyncs = false;
+    InjectVSyncSource* mVSyncInjector = nullptr;
+    ConnectionHandle mInjectorConnectionHandle;
 
     std::mutex mHWVsyncLock;
-    bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock);
-    bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock);
+    bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false;
+    bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false;
 
     std::atomic<nsecs_t> mLastResyncTime = 0;
 
+    // Whether to use idle timer callbacks that support the kernel timer.
+    const bool mSupportKernelTimer;
+
     std::unique_ptr<DispSync> mPrimaryDispSync;
     std::unique_ptr<EventControlThread> mEventControlThread;
 
-    // TODO(b/113612090): The following set of variables needs to be revised. For now, this is
-    // a proof of concept. We turn on frame skipping if the difference between the timestamps
-    // is between 32 and 34ms. We expect this currently for 30fps videos, so we render them at 30Hz.
-    nsecs_t mPreviousFrameTimestamp = 0;
-    // Keeping track of whether we are skipping the refresh count. If we want to
-    // simulate 30Hz rendering, we skip every other frame, and this variable is set
-    // to 1.
-    int64_t mSkipCount = 0;
-    std::array<int64_t, scheduler::ARRAY_SIZE> mTimeDifferences{};
-    size_t mCounter = 0;
+    // Used to choose refresh rate if content detection is enabled.
+    std::unique_ptr<LayerHistory> mLayerHistory;
 
-    // Historical information about individual layers. Used for predicting the refresh rate.
-    scheduler::LayerHistory mLayerHistory;
-
-    // Timer that records time between requests for next vsync. If the time is higher than a given
-    // interval, a callback is fired. Set this variable to >0 to use this feature.
-    int64_t mSetIdleTimerMs = 0;
-    std::unique_ptr<scheduler::IdleTimer> mIdleTimer;
-    // Enables whether to use idle timer callbacks that support the kernel
-    // timer.
-    bool mSupportKernelTimer;
-
+    // Timer that records time between requests for next vsync.
+    std::optional<scheduler::OneShotTimer> mIdleTimer;
     // Timer used to monitor touch events.
-    int64_t mSetTouchTimerMs = 0;
-    std::unique_ptr<scheduler::IdleTimer> mTouchTimer;
-
+    std::optional<scheduler::OneShotTimer> mTouchTimer;
     // Timer used to monitor display power mode.
-    int64_t mSetDisplayPowerTimerMs = 0;
-    std::unique_ptr<scheduler::IdleTimer> mDisplayPowerTimer;
+    std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
 
-    std::mutex mCallbackLock;
-    ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
+    ISchedulerCallback& mSchedulerCallback;
 
     // In order to make sure that the features don't override themselves, we need a state machine
     // to keep track which feature requested the config change.
     std::mutex mFeatureStateLock;
-    ContentFeatureState mCurrentContentFeatureState GUARDED_BY(mFeatureStateLock) =
-            ContentFeatureState::CONTENT_DETECTION_OFF;
-    IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
-    TouchState mCurrentTouchState GUARDED_BY(mFeatureStateLock) = TouchState::INACTIVE;
-    DisplayPowerTimerState mDisplayPowerTimerState GUARDED_BY(mFeatureStateLock) =
-            DisplayPowerTimerState::EXPIRED;
-    uint32_t mContentRefreshRate GUARDED_BY(mFeatureStateLock);
-    RefreshRateType mRefreshRateType GUARDED_BY(mFeatureStateLock);
-    bool mIsHDRContent GUARDED_BY(mFeatureStateLock) = false;
-    bool mIsDisplayPowerStateNormal GUARDED_BY(mFeatureStateLock) = true;
+
+    struct {
+        ContentDetectionState contentDetectionV1 = ContentDetectionState::Off;
+        TimerState idleTimer = TimerState::Reset;
+        TouchState touch = TouchState::Inactive;
+        TimerState displayPowerTimer = TimerState::Expired;
+
+        std::optional<HwcConfigIndexType> configId;
+        LayerHistory::Summary contentRequirements;
+
+        bool isDisplayPowerStateNormal = true;
+
+        // Used to cache the last parameters of onPrimaryDisplayConfigChanged
+        struct ConfigChangedParams {
+            ConnectionHandle handle;
+            PhysicalDisplayId displayId;
+            HwcConfigIndexType configId;
+            nsecs_t vsyncPeriod;
+        };
+
+        std::optional<ConfigChangedParams> cachedConfigChangedParams;
+    } mFeatures GUARDED_BY(mFeatureStateLock);
 
     const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
 
-    // Global config to force HDR content to work on DEFAULT refreshRate
-    static constexpr bool mForceHDRContentToDefaultRefreshRate = false;
+    std::mutex mVsyncTimelineLock;
+    std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
+            GUARDED_BY(mVsyncTimelineLock);
+    static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
+
+    // This variable indicates whether to use the content detection feature at all.
+    const bool mUseContentDetection;
+    // This variable indicates whether to use V2 version of the content detection.
+    const bool mUseContentDetectionV2;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
index fb5414f..e8e0444 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
@@ -30,9 +30,10 @@
     }
 
     size_t n = v->size() / 2;
-    nth_element(v->begin(), v->begin() + n, v->end());
+    nth_element(v->begin(), v->begin() + static_cast<long>(n), v->end());
     return v->at(n);
 }
 
 } // namespace scheduler
 } // namespace android
+
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index f193553..04a4cd1 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -16,32 +16,27 @@
 
 #pragma once
 
-#include <chrono>
+#include <utils/Timers.h>
 #include <cinttypes>
 #include <numeric>
 #include <unordered_map>
 #include <vector>
 
-namespace android {
-namespace scheduler {
-using namespace std::chrono_literals;
+namespace android::scheduler {
 
-// This number is used to set the size of the arrays in scheduler that hold information
-// about layers.
-static constexpr size_t ARRAY_SIZE = 30;
+// Opaque handle to scheduler connection.
+struct ConnectionHandle {
+    using Id = std::uintptr_t;
+    static constexpr Id INVALID_ID = static_cast<Id>(-1);
 
-// This number is used when we try to determine how long do we keep layer information around
-// before we remove it. It is also used to determine how long the layer stays relevant.
-// This time period captures infrequent updates when playing YouTube video with static image,
-// or waiting idle in messaging app, when cursor is blinking.
-static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 1200ms;
+    Id id = INVALID_ID;
 
-// Layer is considered low activity if the LOW_ACTIVITY_BUFFERS buffers come more than
-// LOW_ACTIVITY_EPSILON_NS  apart.
-// This is helping SF to vote for lower refresh rates when there is not activity
-// in screen.
-static constexpr int LOW_ACTIVITY_BUFFERS = 2;
-static constexpr std::chrono::nanoseconds LOW_ACTIVITY_EPSILON_NS = 250ms;
+    explicit operator bool() const { return id != INVALID_ID; }
+};
+
+inline bool operator==(ConnectionHandle lhs, ConnectionHandle rhs) {
+    return lhs.id == rhs.id;
+}
 
 // Calculates the statistical mean (average) in the data structure (array, vector). The
 // function does not modify the contents of the array.
@@ -76,5 +71,27 @@
     return static_cast<int>(std::max_element(counts.begin(), counts.end(), compareCounts)->first);
 }
 
-} // namespace scheduler
-} // namespace android
\ No newline at end of file
+template <class T, size_t N>
+constexpr size_t arrayLen(T (&)[N]) {
+    return N;
+}
+
+static constexpr size_t max64print = std::numeric_limits<nsecs_t>::digits10 + 1;
+
+template <typename T>
+static inline T round(float f) {
+    return static_cast<T>(std::round(f));
+}
+
+} // namespace android::scheduler
+
+namespace std {
+
+template <>
+struct hash<android::scheduler::ConnectionHandle> {
+    size_t operator()(android::scheduler::ConnectionHandle handle) const {
+        return hash<android::scheduler::ConnectionHandle::Id>()(handle.id);
+    }
+};
+
+} // namespace std
diff --git a/services/surfaceflinger/Scheduler/StrongTyping.h b/services/surfaceflinger/Scheduler/StrongTyping.h
new file mode 100644
index 0000000..e8ca0ba
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/StrongTyping.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+namespace android {
+
+template <typename T, template <typename> class AbilityType>
+struct Ability {
+    T& base() { return static_cast<T&>(*this); }
+    T const& base() const { return static_cast<T const&>(*this); }
+};
+
+template <typename T>
+struct Add : Ability<T, Add> {
+    inline T operator+(T const& other) const { return T(this->base().value() + other.value()); }
+    inline T& operator++() {
+        ++this->base().value();
+        return this->base();
+    };
+    inline T operator++(int) {
+        T tmp(this->base());
+        operator++();
+        return tmp;
+    };
+    inline T& operator+=(T const& other) {
+        this->base().value() += other.value();
+        return this->base();
+    };
+};
+
+template <typename T>
+struct Compare : Ability<T, Compare> {
+    inline bool operator==(T const& other) const { return this->base().value() == other.value(); };
+    inline bool operator<(T const& other) const { return this->base().value() < other.value(); }
+    inline bool operator<=(T const& other) const { return (*this < other) || (*this == other); }
+    inline bool operator!=(T const& other) const { return !(*this == other); }
+    inline bool operator>=(T const& other) const { return !(*this < other); }
+    inline bool operator>(T const& other) const { return !(*this < other || *this == other); }
+};
+
+template <typename T>
+struct Hash : Ability<T, Hash> {
+    [[nodiscard]] std::size_t hash() const {
+        return std::hash<typename std::remove_const<
+                typename std::remove_reference<decltype(this->base().value())>::type>::type>{}(
+                this->base().value());
+    }
+};
+
+template <typename T, typename W, template <typename> class... Ability>
+struct StrongTyping : Ability<StrongTyping<T, W, Ability...>>... {
+    StrongTyping() : mValue(0) {}
+    explicit StrongTyping(T const& value) : mValue(value) {}
+    StrongTyping(StrongTyping const&) = default;
+    StrongTyping& operator=(StrongTyping const&) = default;
+    explicit inline operator T() const { return mValue; }
+    T const& value() const { return mValue; }
+    T& value() { return mValue; }
+
+private:
+    T mValue;
+};
+} // namespace android
+
+namespace std {
+template <typename T, typename W, template <typename> class... Ability>
+struct hash<android::StrongTyping<T, W, Ability...>> {
+    std::size_t operator()(android::StrongTyping<T, W, Ability...> const& k) const {
+        return k.hash();
+    }
+};
+} // namespace std
diff --git a/services/surfaceflinger/Scheduler/TimeKeeper.h b/services/surfaceflinger/Scheduler/TimeKeeper.h
new file mode 100644
index 0000000..da2195c
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/TimeKeeper.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/Timers.h>
+#include <functional>
+
+namespace android::scheduler {
+
+class Clock {
+public:
+    virtual ~Clock();
+    /*
+     * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time.
+     */
+    virtual nsecs_t now() const = 0;
+
+protected:
+    Clock() = default;
+    Clock(Clock const&) = delete;
+    Clock& operator=(Clock const&) = delete;
+};
+
+/*
+ * TimeKeeper is the interface for a single-shot timer primitive.
+ */
+class TimeKeeper : public Clock {
+public:
+    virtual ~TimeKeeper();
+
+    /*
+     * Arms callback to fired in time nanoseconds.
+     * There is only one timer, and subsequent calls will reset the callback function and the time.
+     */
+    virtual void alarmIn(std::function<void()> const& callback, nsecs_t time) = 0;
+
+    /*
+     * Cancels an existing pending callback
+     */
+    virtual void alarmCancel() = 0;
+
+    virtual void dump(std::string& result) const = 0;
+
+protected:
+    TimeKeeper(TimeKeeper const&) = delete;
+    TimeKeeper& operator=(TimeKeeper const&) = delete;
+    TimeKeeper() = default;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
new file mode 100644
index 0000000..7c5058e
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerTimer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <sys/unistd.h>
+#include <utils/Trace.h>
+#include <chrono>
+#include <cstdint>
+
+#include "SchedulerUtils.h"
+#include "Timer.h"
+
+namespace android::scheduler {
+using base::StringAppendF;
+
+static constexpr size_t kReadPipe = 0;
+static constexpr size_t kWritePipe = 1;
+
+Timer::Timer() {
+    reset();
+    mDispatchThread = std::thread([this]() { threadMain(); });
+}
+
+Timer::~Timer() {
+    endDispatch();
+    mDispatchThread.join();
+    cleanup();
+}
+
+void Timer::reset() {
+    cleanup();
+    mTimerFd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
+    mEpollFd = epoll_create1(EPOLL_CLOEXEC);
+    if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) {
+        ALOGE("could not create TimerDispatch mPipes");
+        return;
+    };
+    setDebugState(DebugState::Reset);
+}
+
+void Timer::cleanup() {
+    if (mTimerFd != -1) {
+        close(mTimerFd);
+        mTimerFd = -1;
+    }
+
+    if (mEpollFd != -1) {
+        close(mEpollFd);
+        mEpollFd = -1;
+    }
+
+    if (mPipes[kReadPipe] != -1) {
+        close(mPipes[kReadPipe]);
+        mPipes[kReadPipe] = -1;
+    }
+
+    if (mPipes[kWritePipe] != -1) {
+        close(mPipes[kWritePipe]);
+        mPipes[kWritePipe] = -1;
+    }
+}
+
+void Timer::endDispatch() {
+    static constexpr unsigned char end = 'e';
+    write(mPipes[kWritePipe], &end, sizeof(end));
+}
+
+nsecs_t Timer::now() const {
+    return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    using namespace std::literals;
+    static constexpr int ns_per_s =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+
+    mCallback = cb;
+
+    struct itimerspec old_timer;
+    struct itimerspec new_timer {
+        .it_interval = {.tv_sec = 0, .tv_nsec = 0},
+        .it_value = {.tv_sec = static_cast<long>(fireIn / ns_per_s),
+                     .tv_nsec = static_cast<long>(fireIn % ns_per_s)},
+    };
+
+    if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
+        ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
+    }
+}
+
+void Timer::alarmCancel() {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+
+    struct itimerspec old_timer;
+    struct itimerspec new_timer {
+        .it_interval = {.tv_sec = 0, .tv_nsec = 0},
+        .it_value = {
+                .tv_sec = 0,
+                .tv_nsec = 0,
+        },
+    };
+
+    if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
+        ALOGW("Failed to disarm timerfd");
+    }
+}
+
+void Timer::threadMain() {
+    while (dispatch()) {
+        reset();
+    }
+}
+
+bool Timer::dispatch() {
+    setDebugState(DebugState::Running);
+    struct sched_param param = {0};
+    param.sched_priority = 2;
+    if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) != 0) {
+        ALOGW("Failed to set SCHED_FIFO on dispatch thread");
+    }
+
+    if (pthread_setname_np(pthread_self(), "TimerDispatch")) {
+        ALOGW("Failed to set thread name on dispatch thread");
+    }
+
+    enum DispatchType : uint32_t { TIMER, TERMINATE, MAX_DISPATCH_TYPE };
+    epoll_event timerEvent;
+    timerEvent.events = EPOLLIN;
+    timerEvent.data.u32 = DispatchType::TIMER;
+    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mTimerFd, &timerEvent) == -1) {
+        ALOGE("Error adding timer fd to epoll dispatch loop");
+        return true;
+    }
+
+    epoll_event terminateEvent;
+    terminateEvent.events = EPOLLIN;
+    terminateEvent.data.u32 = DispatchType::TERMINATE;
+    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mPipes[kReadPipe], &terminateEvent) == -1) {
+        ALOGE("Error adding control fd to dispatch loop");
+        return true;
+    }
+
+    uint64_t iteration = 0;
+    char const traceNamePrefix[] = "TimerIteration #";
+    static constexpr size_t maxlen = arrayLen(traceNamePrefix) + max64print;
+    std::array<char, maxlen> str_buffer;
+
+    while (true) {
+        setDebugState(DebugState::Waiting);
+        epoll_event events[DispatchType::MAX_DISPATCH_TYPE];
+        int nfds = epoll_wait(mEpollFd, events, DispatchType::MAX_DISPATCH_TYPE, -1);
+
+        setDebugState(DebugState::Running);
+        if (ATRACE_ENABLED()) {
+            snprintf(str_buffer.data(), str_buffer.size(), "%s%" PRIu64, traceNamePrefix,
+                     iteration++);
+            ATRACE_NAME(str_buffer.data());
+        }
+
+        if (nfds == -1) {
+            if (errno != EINTR) {
+                ALOGE("Error waiting on epoll: %s", strerror(errno));
+                return true;
+            }
+        }
+
+        for (auto i = 0; i < nfds; i++) {
+            if (events[i].data.u32 == DispatchType::TIMER) {
+                static uint64_t mIgnored = 0;
+                setDebugState(DebugState::Reading);
+                read(mTimerFd, &mIgnored, sizeof(mIgnored));
+                setDebugState(DebugState::Running);
+                std::function<void()> cb;
+                {
+                    std::lock_guard<decltype(mMutex)> lk(mMutex);
+                    cb = mCallback;
+                }
+                if (cb) {
+                    setDebugState(DebugState::InCallback);
+                    cb();
+                    setDebugState(DebugState::Running);
+                }
+            }
+            if (events[i].data.u32 == DispatchType::TERMINATE) {
+                ALOGE("Terminated");
+                setDebugState(DebugState::Running);
+                return false;
+            }
+        }
+    }
+}
+
+void Timer::setDebugState(DebugState state) {
+    std::lock_guard lk(mMutex);
+    mDebugState = state;
+}
+
+const char* Timer::strDebugState(DebugState state) const {
+    switch (state) {
+        case DebugState::Reset:
+            return "Reset";
+        case DebugState::Running:
+            return "Running";
+        case DebugState::Waiting:
+            return "Waiting";
+        case DebugState::Reading:
+            return "Reading";
+        case DebugState::InCallback:
+            return "InCallback";
+        case DebugState::Terminated:
+            return "Terminated";
+    }
+}
+
+void Timer::dump(std::string& result) const {
+    std::lock_guard lk(mMutex);
+    StringAppendF(&result, "\t\tDebugState: %s\n", strDebugState(mDebugState));
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Timer.h b/services/surfaceflinger/Scheduler/Timer.h
new file mode 100644
index 0000000..a8e2d5a
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Timer.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "TimeKeeper.h"
+
+#include <android-base/thread_annotations.h>
+#include <array>
+#include <thread>
+
+namespace android::scheduler {
+
+class Timer : public TimeKeeper {
+public:
+    Timer();
+    ~Timer();
+    nsecs_t now() const final;
+
+    // NB: alarmIn and alarmCancel are threadsafe; with the last-returning function being effectual
+    //     Most users will want to serialize thes calls so as to be aware of the timer state.
+    void alarmIn(std::function<void()> const& cb, nsecs_t fireIn) final;
+    void alarmCancel() final;
+    void dump(std::string& result) const final;
+
+private:
+    enum class DebugState { Reset, Running, Waiting, Reading, InCallback, Terminated };
+    void reset();
+    void cleanup();
+    void setDebugState(DebugState state) EXCLUDES(mMutex);
+    const char* strDebugState(DebugState state) const;
+
+    int mTimerFd = -1;
+    int mEpollFd = -1;
+    std::array<int, 2> mPipes = {-1, -1};
+
+    std::thread mDispatchThread;
+    void threadMain();
+    bool dispatch();
+    void endDispatch();
+
+    mutable std::mutex mMutex;
+    std::function<void()> mCallback GUARDED_BY(mMutex);
+    DebugState mDebugState GUARDED_BY(mMutex);
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
new file mode 100644
index 0000000..2a2d7c5
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/Timers.h>
+#include <functional>
+#include <string>
+
+#include "StrongTyping.h"
+
+namespace android::scheduler {
+class TimeKeeper;
+class VSyncTracker;
+
+enum class ScheduleResult { Scheduled, CannotSchedule, Error };
+enum class CancelResult { Cancelled, TooLate, Error };
+
+/*
+ * VSyncDispatch is a class that will dispatch callbacks relative to system vsync events.
+ */
+class VSyncDispatch {
+public:
+    using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare, Hash>;
+
+    virtual ~VSyncDispatch();
+
+    /*
+     * A callback that can be registered to be awoken at a given time relative to a vsync event.
+     * \param [in] vsyncTime The timestamp of the vsync the callback is for.
+     * \param [in] targetWakeupTime The timestamp of intended wakeup time of the cb.
+     *
+     */
+    using Callback = std::function<void(nsecs_t vsyncTime, nsecs_t targetWakeupTime)>;
+
+    /*
+     * Registers a callback that will be called at designated points on the vsync timeline.
+     * The callback can be scheduled, rescheduled targeting vsync times, or cancelled.
+     * The token returned must be cleaned up via unregisterCallback.
+     *
+     * \param [in] callbackFn   A function to schedule for callback. The resources needed to invoke
+     *                          callbackFn must have lifetimes encompassing the lifetime of the
+     *                          CallbackToken returned.
+     * \param [in] callbackName A human-readable, unique name to identify the callback.
+     * \return                  A token that can be used to schedule, reschedule, or cancel the
+     *                          invocation of callbackFn.
+     *
+     */
+    virtual CallbackToken registerCallback(Callback const& callbackFn,
+                                           std::string callbackName) = 0;
+
+    /*
+     * Unregisters a callback.
+     *
+     * \param [in] token        The callback to unregister.
+     *
+     */
+    virtual void unregisterCallback(CallbackToken token) = 0;
+
+    /*
+     * Schedules the registered callback to be dispatched.
+     *
+     * The callback will be dispatched at 'workDuration' nanoseconds before a vsync event.
+     *
+     * The caller designates the earliest vsync event that should be targeted by the earliestVsync
+     * parameter.
+     * The callback will be scheduled at (workDuration - predictedVsync), where predictedVsync
+     * is the first vsync event time where ( predictedVsync >= earliestVsync ).
+     *
+     * If (workDuration - earliestVsync) is in the past, or if a callback has already been
+     * dispatched for the predictedVsync, an error will be returned.
+     *
+     * It is valid to reschedule a callback to a different time.
+     *
+     * \param [in] token           The callback to schedule.
+     * \param [in] workDuration    The time before the actual vsync time to invoke the callback
+     *                             associated with token.
+     * \param [in] earliestVsync   The targeted display time. This will be snapped to the closest
+     *                             predicted vsync time after earliestVsync.
+     * \return                     A ScheduleResult::Scheduled if callback was scheduled.
+     *                             A ScheduleResult::CannotSchedule
+     *                             if (workDuration - earliestVsync) is in the past, or
+     *                             if a callback was dispatched for the predictedVsync already.
+     *                             A ScheduleResult::Error if there was another error.
+     */
+    virtual ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
+                                    nsecs_t earliestVsync) = 0;
+
+    /* Cancels a scheduled callback, if possible.
+     *
+     * \param [in] token    The callback to cancel.
+     * \return              A CancelResult::TooLate if the callback was already dispatched.
+     *                      A CancelResult::Cancelled if the callback was successfully cancelled.
+     *                      A CancelResult::Error if there was an pre-condition violation.
+     */
+    virtual CancelResult cancel(CallbackToken token) = 0;
+
+    virtual void dump(std::string& result) const = 0;
+
+protected:
+    VSyncDispatch() = default;
+    VSyncDispatch(VSyncDispatch const&) = delete;
+    VSyncDispatch& operator=(VSyncDispatch const&) = delete;
+};
+
+/*
+ * Helper class to operate on registered callbacks. It is up to user of the class to ensure
+ * that VsyncDispatch lifetime exceeds the lifetime of VSyncCallbackRegistation.
+ */
+class VSyncCallbackRegistration {
+public:
+    VSyncCallbackRegistration(VSyncDispatch&, VSyncDispatch::Callback const& callbackFn,
+                              std::string const& callbackName);
+    VSyncCallbackRegistration(VSyncCallbackRegistration&&);
+    VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&);
+    ~VSyncCallbackRegistration();
+
+    // See documentation for VSyncDispatch::schedule.
+    ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync);
+
+    // See documentation for VSyncDispatch::cancel.
+    CancelResult cancel();
+
+private:
+    VSyncCallbackRegistration(VSyncCallbackRegistration const&) = delete;
+    VSyncCallbackRegistration& operator=(VSyncCallbackRegistration const&) = delete;
+
+    std::reference_wrapper<VSyncDispatch> mDispatch;
+    VSyncDispatch::CallbackToken mToken;
+    bool mValidToken;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
new file mode 100644
index 0000000..a596bce
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -0,0 +1,411 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <android-base/stringprintf.h>
+#include <utils/Trace.h>
+#include <vector>
+
+#include "TimeKeeper.h"
+#include "VSyncDispatchTimerQueue.h"
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+using base::StringAppendF;
+
+VSyncDispatch::~VSyncDispatch() = default;
+VSyncTracker::~VSyncTracker() = default;
+TimeKeeper::~TimeKeeper() = default;
+
+VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name,
+                                                           VSyncDispatch::Callback const& cb,
+                                                           nsecs_t minVsyncDistance)
+      : mName(name),
+        mCallback(cb),
+        mWorkDuration(0),
+        mEarliestVsync(0),
+        mMinVsyncDistance(minVsyncDistance) {}
+
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
+    return mLastDispatchTime;
+}
+
+std::string_view VSyncDispatchTimerQueueEntry::name() const {
+    return mName;
+}
+
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const {
+    if (!mArmedInfo) {
+        return {};
+    }
+    return {mArmedInfo->mActualWakeupTime};
+}
+
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const {
+    if (!mArmedInfo) {
+        return {};
+    }
+    return {mArmedInfo->mActualVsyncTime};
+}
+
+ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
+                                                      VSyncTracker& tracker, nsecs_t now) {
+    auto nextVsyncTime =
+            tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
+
+    bool const wouldSkipAVsyncTarget =
+            mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
+    if (wouldSkipAVsyncTarget) {
+        return ScheduleResult::Scheduled;
+    }
+
+    bool const alreadyDispatchedForVsync = mLastDispatchTime &&
+            ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
+             (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
+    if (alreadyDispatchedForVsync) {
+        nextVsyncTime =
+                tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
+    }
+
+    auto const nextWakeupTime = nextVsyncTime - workDuration;
+    mWorkDuration = workDuration;
+    mEarliestVsync = earliestVsync;
+    mArmedInfo = {nextWakeupTime, nextVsyncTime};
+    return ScheduleResult::Scheduled;
+}
+
+void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(nsecs_t workDuration,
+                                                            nsecs_t earliestVsync) {
+    mWorkloadUpdateInfo = {.earliestVsync = earliestVsync, .duration = workDuration};
+}
+
+bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const {
+    return mWorkloadUpdateInfo.has_value();
+}
+
+void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
+    if (!mArmedInfo && !mWorkloadUpdateInfo) {
+        return;
+    }
+
+    if (mWorkloadUpdateInfo) {
+        mEarliestVsync = mWorkloadUpdateInfo->earliestVsync;
+        mWorkDuration = mWorkloadUpdateInfo->duration;
+        mWorkloadUpdateInfo.reset();
+    }
+
+    auto const nextVsyncTime =
+            tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
+    mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
+}
+
+void VSyncDispatchTimerQueueEntry::disarm() {
+    mArmedInfo.reset();
+}
+
+nsecs_t VSyncDispatchTimerQueueEntry::executing() {
+    mLastDispatchTime = mArmedInfo->mActualVsyncTime;
+    disarm();
+    return *mLastDispatchTime;
+}
+
+void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp) {
+    {
+        std::lock_guard<std::mutex> lk(mRunningMutex);
+        mRunning = true;
+    }
+
+    mCallback(vsyncTimestamp, wakeupTimestamp);
+
+    std::lock_guard<std::mutex> lk(mRunningMutex);
+    mRunning = false;
+    mCv.notify_all();
+}
+
+void VSyncDispatchTimerQueueEntry::ensureNotRunning() {
+    std::unique_lock<std::mutex> lk(mRunningMutex);
+    mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
+}
+
+void VSyncDispatchTimerQueueEntry::dump(std::string& result) const {
+    std::lock_guard<std::mutex> lk(mRunningMutex);
+    std::string armedInfo;
+    if (mArmedInfo) {
+        StringAppendF(&armedInfo, "[wake up in %.2fms for vsync %.2fms from now]",
+                      (mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f,
+                      (mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f);
+    }
+
+    StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
+                  mRunning ? "(in callback function)" : "", armedInfo.c_str());
+    StringAppendF(&result, "\t\t\tmWorkDuration: %.2fms mEarliestVsync: %.2fms relative to now\n",
+                  mWorkDuration / 1e6f, (mEarliestVsync - systemTime()) / 1e6f);
+
+    if (mLastDispatchTime) {
+        StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
+                      (systemTime() - *mLastDispatchTime) / 1e6f);
+    } else {
+        StringAppendF(&result, "\t\t\tmLastDispatchTime unknown\n");
+    }
+}
+
+VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
+                                                 VSyncTracker& tracker, nsecs_t timerSlack,
+                                                 nsecs_t minVsyncDistance)
+      : mTimeKeeper(std::move(tk)),
+        mTracker(tracker),
+        mTimerSlack(timerSlack),
+        mMinVsyncDistance(minVsyncDistance) {}
+
+VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    cancelTimer();
+}
+
+void VSyncDispatchTimerQueue::cancelTimer() {
+    mIntendedWakeupTime = kInvalidTime;
+    mTimeKeeper->alarmCancel();
+}
+
+void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) {
+    mIntendedWakeupTime = targetTime;
+    mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
+                         targetTime - now);
+    mLastTimerSchedule = mTimeKeeper->now();
+}
+
+void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) {
+    rearmTimerSkippingUpdateFor(now, mCallbacks.end());
+}
+
+void VSyncDispatchTimerQueue::TraceBuffer::note(std::string_view name, nsecs_t alarmIn,
+                                                nsecs_t vsFor) {
+    if (ATRACE_ENABLED()) {
+        snprintf(str_buffer.data(), str_buffer.size(), "%.4s%s%" PRId64 "%s%" PRId64,
+                 name.substr(0, kMaxNamePrint).data(), kTraceNamePrefix, alarmIn,
+                 kTraceNameSeparator, vsFor);
+    }
+    ATRACE_NAME(str_buffer.data());
+}
+
+void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
+        nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
+    std::optional<nsecs_t> min;
+    std::optional<nsecs_t> targetVsync;
+    std::optional<std::string_view> nextWakeupName;
+    for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
+        auto& callback = it->second;
+        if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
+            continue;
+        }
+
+        if (it != skipUpdateIt) {
+            callback->update(mTracker, now);
+        }
+        auto const wakeupTime = *callback->wakeupTime();
+        if (!min || (min && *min > wakeupTime)) {
+            nextWakeupName = callback->name();
+            min = wakeupTime;
+            targetVsync = callback->targetVsync();
+        }
+    }
+
+    if (min && (min < mIntendedWakeupTime)) {
+        if (targetVsync && nextWakeupName) {
+            mTraceBuffer.note(*nextWakeupName, *min - now, *targetVsync - now);
+        }
+        setTimer(*min, now);
+    } else {
+        ATRACE_NAME("cancel timer");
+        cancelTimer();
+    }
+}
+
+void VSyncDispatchTimerQueue::timerCallback() {
+    struct Invocation {
+        std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
+        nsecs_t vsyncTimestamp;
+        nsecs_t wakeupTimestamp;
+    };
+    std::vector<Invocation> invocations;
+    {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        mLastTimerCallback = mTimeKeeper->now();
+        for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
+            auto& callback = it->second;
+            auto const wakeupTime = callback->wakeupTime();
+            if (!wakeupTime) {
+                continue;
+            }
+
+            if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) {
+                callback->executing();
+                invocations.emplace_back(
+                        Invocation{callback, *callback->lastExecutedVsyncTarget(), *wakeupTime});
+            }
+        }
+
+        mIntendedWakeupTime = kInvalidTime;
+        rearmTimer(mTimeKeeper->now());
+    }
+
+    for (auto const& invocation : invocations) {
+        invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp);
+    }
+}
+
+VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
+        Callback const& callbackFn, std::string callbackName) {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    return CallbackToken{
+            mCallbacks
+                    .emplace(++mCallbackToken,
+                             std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
+                                                                            callbackFn,
+                                                                            mMinVsyncDistance))
+                    .first->first};
+}
+
+void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
+    std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
+    {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        auto it = mCallbacks.find(token);
+        if (it != mCallbacks.end()) {
+            entry = it->second;
+            mCallbacks.erase(it);
+        }
+    }
+
+    if (entry) {
+        entry->ensureNotRunning();
+    }
+}
+
+ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration,
+                                                 nsecs_t earliestVsync) {
+    auto result = ScheduleResult::Error;
+    {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+
+        auto it = mCallbacks.find(token);
+        if (it == mCallbacks.end()) {
+            return result;
+        }
+        auto& callback = it->second;
+        auto const now = mTimeKeeper->now();
+
+        /* If the timer thread will run soon, we'll apply this work update via the callback
+         * timer recalculation to avoid cancelling a callback that is about to fire. */
+        auto const rearmImminent = now > mIntendedWakeupTime;
+        if (CC_UNLIKELY(rearmImminent)) {
+            callback->addPendingWorkloadUpdate(workDuration, earliestVsync);
+            return ScheduleResult::Scheduled;
+        }
+
+        result = callback->schedule(workDuration, earliestVsync, mTracker, now);
+        if (result == ScheduleResult::CannotSchedule) {
+            return result;
+        }
+
+        if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
+            rearmTimerSkippingUpdateFor(now, it);
+        }
+    }
+
+    return result;
+}
+
+CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+
+    auto it = mCallbacks.find(token);
+    if (it == mCallbacks.end()) {
+        return CancelResult::Error;
+    }
+    auto& callback = it->second;
+
+    auto const wakeupTime = callback->wakeupTime();
+    if (wakeupTime) {
+        callback->disarm();
+
+        if (*wakeupTime == mIntendedWakeupTime) {
+            mIntendedWakeupTime = kInvalidTime;
+            rearmTimer(mTimeKeeper->now());
+        }
+        return CancelResult::Cancelled;
+    }
+    return CancelResult::TooLate;
+}
+
+void VSyncDispatchTimerQueue::dump(std::string& result) const {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    StringAppendF(&result, "\tTimer:\n");
+    mTimeKeeper->dump(result);
+    StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
+                  mMinVsyncDistance / 1e6f);
+    StringAppendF(&result, "\tmIntendedWakeupTime: %.2fms from now\n",
+                  (mIntendedWakeupTime - mTimeKeeper->now()) / 1e6f);
+    StringAppendF(&result, "\tmLastTimerCallback: %.2fms ago mLastTimerSchedule: %.2fms ago\n",
+                  (mTimeKeeper->now() - mLastTimerCallback) / 1e6f,
+                  (mTimeKeeper->now() - mLastTimerSchedule) / 1e6f);
+    StringAppendF(&result, "\tCallbacks:\n");
+    for (const auto& [token, entry] : mCallbacks) {
+        entry->dump(result);
+    }
+}
+
+VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
+                                                     VSyncDispatch::Callback const& callbackFn,
+                                                     std::string const& callbackName)
+      : mDispatch(dispatch),
+        mToken(dispatch.registerCallback(callbackFn, callbackName)),
+        mValidToken(true) {}
+
+VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
+      : mDispatch(other.mDispatch),
+        mToken(std::move(other.mToken)),
+        mValidToken(std::move(other.mValidToken)) {
+    other.mValidToken = false;
+}
+
+VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) {
+    mDispatch = std::move(other.mDispatch);
+    mToken = std::move(other.mToken);
+    mValidToken = std::move(other.mValidToken);
+    other.mValidToken = false;
+    return *this;
+}
+
+VSyncCallbackRegistration::~VSyncCallbackRegistration() {
+    if (mValidToken) mDispatch.get().unregisterCallback(mToken);
+}
+
+ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
+    if (!mValidToken) {
+        return ScheduleResult::Error;
+    }
+    return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
+}
+
+CancelResult VSyncCallbackRegistration::cancel() {
+    if (!mValidToken) {
+        return CancelResult::Error;
+    }
+    return mDispatch.get().cancel(mToken);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
new file mode 100644
index 0000000..957c0d1
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <array>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+
+#include "SchedulerUtils.h"
+#include "VSyncDispatch.h"
+
+namespace android::scheduler {
+
+// VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in
+// VSyncDispatchTimerQueue hoisted to public for unit testing.
+class VSyncDispatchTimerQueueEntry {
+public:
+    // This is the state of the entry. There are 3 states, armed, running, disarmed.
+    // Valid transition: disarmed -> armed ( when scheduled )
+    // Valid transition: armed -> running -> disarmed ( when timer is called)
+    // Valid transition: armed -> disarmed ( when cancelled )
+    VSyncDispatchTimerQueueEntry(std::string const& name, VSyncDispatch::Callback const& fn,
+                                 nsecs_t minVsyncDistance);
+    std::string_view name() const;
+
+    // Start: functions that are not threadsafe.
+    // Return the last vsync time this callback was invoked.
+    std::optional<nsecs_t> lastExecutedVsyncTarget() const;
+
+    // This moves the state from disarmed->armed and will calculate the wakeupTime.
+    ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
+                            nsecs_t now);
+    // This will update armed entries with the latest vsync information. Entry remains armed.
+    void update(VSyncTracker& tracker, nsecs_t now);
+
+    // This will return empty if not armed, or the next calculated wakeup time if armed.
+    // It will not update the wakeupTime.
+    std::optional<nsecs_t> wakeupTime() const;
+
+    std::optional<nsecs_t> targetVsync() const;
+
+    // This moves state from armed->disarmed.
+    void disarm();
+
+    // This moves the state from armed->running.
+    // Store the timestamp that this was intended for as the last called timestamp.
+    nsecs_t executing();
+
+    // Adds a pending upload of the earliestVSync and workDuration that will be applied on the next
+    // call to update()
+    void addPendingWorkloadUpdate(nsecs_t workDuration, nsecs_t earliestVsync);
+
+    // Checks if there is a pending update to the workload, returning true if so.
+    bool hasPendingWorkloadUpdate() const;
+    // End: functions that are not threadsafe.
+
+    // Invoke the callback with the two given timestamps, moving the state from running->disarmed.
+    void callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp);
+    // Block calling thread while the callback is executing.
+    void ensureNotRunning();
+
+    void dump(std::string& result) const;
+
+private:
+    std::string const mName;
+    VSyncDispatch::Callback const mCallback;
+
+    nsecs_t mWorkDuration;
+    nsecs_t mEarliestVsync;
+    nsecs_t const mMinVsyncDistance;
+
+    struct ArmingInfo {
+        nsecs_t mActualWakeupTime;
+        nsecs_t mActualVsyncTime;
+    };
+    std::optional<ArmingInfo> mArmedInfo;
+    std::optional<nsecs_t> mLastDispatchTime;
+
+    struct WorkloadUpdateInfo {
+        nsecs_t duration;
+        nsecs_t earliestVsync;
+    };
+    std::optional<WorkloadUpdateInfo> mWorkloadUpdateInfo;
+
+    mutable std::mutex mRunningMutex;
+    std::condition_variable mCv;
+    bool mRunning GUARDED_BY(mRunningMutex) = false;
+};
+
+/*
+ * VSyncDispatchTimerQueue is a class that will dispatch callbacks as per VSyncDispatch interface
+ * using a single timer queue.
+ */
+class VSyncDispatchTimerQueue : public VSyncDispatch {
+public:
+    // Constructs a VSyncDispatchTimerQueue.
+    // \param[in] tk                    A timekeeper.
+    // \param[in] tracker               A tracker.
+    // \param[in] timerSlack            The threshold at which different similarly timed callbacks
+    //                                  should be grouped into one wakeup.
+    // \param[in] minVsyncDistance      The minimum distance between two vsync estimates before the
+    //                                  vsyncs are considered the same vsync event.
+    explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
+                                     nsecs_t timerSlack, nsecs_t minVsyncDistance);
+    ~VSyncDispatchTimerQueue();
+
+    CallbackToken registerCallback(Callback const& callbackFn, std::string callbackName) final;
+    void unregisterCallback(CallbackToken token) final;
+    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
+    CancelResult cancel(CallbackToken token) final;
+    void dump(std::string& result) const final;
+
+private:
+    VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
+    VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete;
+
+    using CallbackMap =
+            std::unordered_map<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
+
+    void timerCallback();
+    void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
+    void rearmTimer(nsecs_t now) REQUIRES(mMutex);
+    void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
+            REQUIRES(mMutex);
+    void cancelTimer() REQUIRES(mMutex);
+
+    static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
+    std::unique_ptr<TimeKeeper> const mTimeKeeper;
+    VSyncTracker& mTracker;
+    nsecs_t const mTimerSlack;
+    nsecs_t const mMinVsyncDistance;
+
+    std::mutex mutable mMutex;
+    size_t mCallbackToken GUARDED_BY(mMutex) = 0;
+
+    CallbackMap mCallbacks GUARDED_BY(mMutex);
+    nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
+
+    struct TraceBuffer {
+        static constexpr char const kTraceNamePrefix[] = "-alarm in:";
+        static constexpr char const kTraceNameSeparator[] = " for vs:";
+        static constexpr size_t kMaxNamePrint = 4;
+        static constexpr size_t kNumTsPrinted = 2;
+        static constexpr size_t maxlen = kMaxNamePrint + arrayLen(kTraceNamePrefix) +
+                arrayLen(kTraceNameSeparator) - 1 + (kNumTsPrinted * max64print);
+        std::array<char, maxlen> str_buffer;
+        void note(std::string_view name, nsecs_t in, nsecs_t vs);
+    } mTraceBuffer GUARDED_BY(mMutex);
+
+    // For debugging purposes
+    nsecs_t mLastTimerCallback GUARDED_BY(mMutex) = kInvalidTime;
+    nsecs_t mLastTimerSchedule GUARDED_BY(mMutex) = kInvalidTime;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
index 7a3bf8e..2567c04 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "VSyncModulator.h"
@@ -21,39 +25,61 @@
 #include <cutils/properties.h>
 #include <utils/Trace.h>
 
+#include <chrono>
 #include <cinttypes>
 #include <mutex>
 
-namespace android {
+namespace android::scheduler {
 
-using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
-VSyncModulator::VSyncModulator() {
+VSyncModulator::VSyncModulator(IPhaseOffsetControl& phaseOffsetControl,
+                               Scheduler::ConnectionHandle appConnectionHandle,
+                               Scheduler::ConnectionHandle sfConnectionHandle,
+                               const OffsetsConfig& config)
+      : mPhaseOffsetControl(phaseOffsetControl),
+        mAppConnectionHandle(appConnectionHandle),
+        mSfConnectionHandle(sfConnectionHandle),
+        mOffsetsConfig(config) {
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sf.vsync_trace_detailed_info", value, "0");
     mTraceDetailedInfo = atoi(value);
-    // Populate the offset map with some default offsets.
-    const Offsets defaultOffsets = {RefreshRateType::DEFAULT, 0, 0};
-    setPhaseOffsets(defaultOffsets, defaultOffsets, defaultOffsets, 0);
 }
 
-void VSyncModulator::setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late,
-                                     nsecs_t thresholdForNextVsync) {
+void VSyncModulator::setPhaseOffsets(const OffsetsConfig& config) {
     std::lock_guard<std::mutex> lock(mMutex);
-    mOffsetMap.insert_or_assign(OffsetType::Early, early);
-    mOffsetMap.insert_or_assign(OffsetType::EarlyGl, earlyGl);
-    mOffsetMap.insert_or_assign(OffsetType::Late, late);
-    mThresholdForNextVsync = thresholdForNextVsync;
+    mOffsetsConfig = config;
     updateOffsetsLocked();
 }
 
 void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) {
-    if (transactionStart == Scheduler::TransactionStart::EARLY) {
+    switch (transactionStart) {
+        case Scheduler::TransactionStart::EarlyStart:
+            ALOGW_IF(mExplicitEarlyWakeup, "Already in TransactionStart::EarlyStart");
+            mExplicitEarlyWakeup = true;
+            break;
+        case Scheduler::TransactionStart::EarlyEnd:
+            ALOGW_IF(!mExplicitEarlyWakeup, "Not in TransactionStart::EarlyStart");
+            mExplicitEarlyWakeup = false;
+            break;
+        case Scheduler::TransactionStart::Normal:
+        case Scheduler::TransactionStart::Early:
+            // Non explicit don't change the explicit early wakeup state
+            break;
+    }
+
+    if (mTraceDetailedInfo) {
+        ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
+    }
+
+    if (!mExplicitEarlyWakeup &&
+        (transactionStart == Scheduler::TransactionStart::Early ||
+         transactionStart == Scheduler::TransactionStart::EarlyEnd)) {
         mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
+        mEarlyTxnStartTime = std::chrono::steady_clock::now();
     }
 
     // An early transaction stays an early transaction.
     if (transactionStart == mTransactionStart ||
-        mTransactionStart == Scheduler::TransactionStart::EARLY) {
+        mTransactionStart == Scheduler::TransactionStart::EarlyEnd) {
         return;
     }
     mTransactionStart = transactionStart;
@@ -61,8 +87,9 @@
 }
 
 void VSyncModulator::onTransactionHandled() {
-    if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return;
-    mTransactionStart = Scheduler::TransactionStart::NORMAL;
+    mTxnAppliedTime = std::chrono::steady_clock::now();
+    if (mTransactionStart == Scheduler::TransactionStart::Normal) return;
+    mTransactionStart = Scheduler::TransactionStart::Normal;
     updateOffsets();
 }
 
@@ -84,9 +111,15 @@
 
 void VSyncModulator::onRefreshed(bool usedRenderEngine) {
     bool updateOffsetsNeeded = false;
-    if (mRemainingEarlyFrameCount > 0) {
-        mRemainingEarlyFrameCount--;
-        updateOffsetsNeeded = true;
+
+    // Apply a margin to account for potential data races
+    // This might make us stay in early offsets for one
+    // additional frame but it's better to be conservative here.
+    if ((mEarlyTxnStartTime.load() + MARGIN_FOR_TX_APPLY) < mTxnAppliedTime.load()) {
+        if (mRemainingEarlyFrameCount > 0) {
+            mRemainingEarlyFrameCount--;
+            updateOffsetsNeeded = true;
+        }
     }
     if (usedRenderEngine) {
         mRemainingRenderEngineUsageCount = MIN_EARLY_GL_FRAME_COUNT_TRANSACTION;
@@ -100,25 +133,21 @@
     }
 }
 
-VSyncModulator::Offsets VSyncModulator::getOffsets() {
+VSyncModulator::Offsets VSyncModulator::getOffsets() const {
     std::lock_guard<std::mutex> lock(mMutex);
     return mOffsets;
 }
 
-VSyncModulator::Offsets VSyncModulator::getNextOffsets() {
-    return mOffsetMap.at(getNextOffsetType());
-}
-
-VSyncModulator::OffsetType VSyncModulator::getNextOffsetType() {
+const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const {
     // Early offsets are used if we're in the middle of a refresh rate
     // change, or if we recently begin a transaction.
-    if (mTransactionStart == Scheduler::TransactionStart::EARLY || mRemainingEarlyFrameCount > 0 ||
-        mRefreshRateChangePending) {
-        return OffsetType::Early;
+    if (mExplicitEarlyWakeup || mTransactionStart == Scheduler::TransactionStart::EarlyEnd ||
+        mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) {
+        return mOffsetsConfig.early;
     } else if (mRemainingRenderEngineUsageCount > 0) {
-        return OffsetType::EarlyGl;
+        return mOffsetsConfig.earlyGl;
     } else {
-        return OffsetType::Late;
+        return mOffsetsConfig.late;
     }
 }
 
@@ -128,37 +157,27 @@
 }
 
 void VSyncModulator::updateOffsetsLocked() {
-    const Offsets desired = getNextOffsets();
+    const Offsets& offsets = getNextOffsets();
 
-    if (mSfConnectionHandle != nullptr) {
-        mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf);
-    }
+    mPhaseOffsetControl.setPhaseOffset(mSfConnectionHandle, offsets.sf);
+    mPhaseOffsetControl.setPhaseOffset(mAppConnectionHandle, offsets.app);
 
-    if (mAppConnectionHandle != nullptr) {
-        mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app);
-    }
+    mOffsets = offsets;
 
-    flushOffsets();
-}
-
-void VSyncModulator::flushOffsets() {
-    OffsetType type = getNextOffsetType();
-    mOffsets = mOffsetMap.at(type);
     if (!mTraceDetailedInfo) {
         return;
     }
-    ATRACE_INT("Vsync-EarlyOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Early);
-    ATRACE_INT("Vsync-EarlyGLOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::EarlyGl);
-    ATRACE_INT("Vsync-LateOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Late);
-    ATRACE_INT("Vsync-HighFpsEarlyOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Early);
-    ATRACE_INT("Vsync-HighFpsEarlyGLOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::EarlyGl);
-    ATRACE_INT("Vsync-HighFpsLateOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Late);
+
+    const bool isEarly = &offsets == &mOffsetsConfig.early;
+    const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl;
+    const bool isLate = &offsets == &mOffsetsConfig.late;
+
+    ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
+    ATRACE_INT("Vsync-EarlyGLOffsetsOn", isEarlyGl);
+    ATRACE_INT("Vsync-LateOffsetsOn", isLate);
 }
 
-} // namespace android
+} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index ddbd221..ab678c9 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -16,12 +16,12 @@
 
 #pragma once
 
-#include <cinttypes>
+#include <chrono>
 #include <mutex>
 
 #include "Scheduler.h"
 
-namespace android {
+namespace android::scheduler {
 
 /*
  * Modulates the vsync-offsets depending on current SurfaceFlinger state.
@@ -31,51 +31,44 @@
     // Number of frames we'll keep the early phase offsets once they are activated for a
     // transaction. This acts as a low-pass filter in case the client isn't quick enough in
     // sending new transactions.
-    const int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2;
+    static constexpr int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2;
 
     // Number of frames we'll keep the early gl phase offsets once they are activated.
     // This acts as a low-pass filter to avoid scenarios where we rapidly
     // switch in and out of gl composition.
-    const int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
+    static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
+
+    // Margin used to account for potential data races
+    static const constexpr std::chrono::nanoseconds MARGIN_FOR_TX_APPLY = 1ms;
 
 public:
-    VSyncModulator();
-
     // Wrapper for a collection of surfaceflinger/app offsets for a particular
-    // configuration .
+    // configuration.
     struct Offsets {
-        scheduler::RefreshRateConfigs::RefreshRateType fpsMode;
         nsecs_t sf;
         nsecs_t app;
+
+        bool operator==(const Offsets& other) const { return sf == other.sf && app == other.app; }
+
+        bool operator!=(const Offsets& other) const { return !(*this == other); }
     };
 
-    enum class OffsetType {
-        Early,
-        EarlyGl,
-        Late,
+    struct OffsetsConfig {
+        Offsets early;   // For transactions with the eEarlyWakeup flag.
+        Offsets earlyGl; // As above but while compositing with GL.
+        Offsets late;    // Default.
+
+        bool operator==(const OffsetsConfig& other) const {
+            return early == other.early && earlyGl == other.earlyGl && late == other.late;
+        }
+
+        bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
     };
 
-    // Sets the phase offsets
-    //
-    // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction
-    //          as early. May be the same as late, in which case we don't shift offsets.
-    // sfEarlyGl: Like sfEarly, but only if we used GL composition. If we use both GL composition
-    //            and the transaction was marked as early, we'll use sfEarly.
-    // sfLate: The regular SF vsync phase offset.
-    // appEarly: Like sfEarly, but for the app-vsync
-    // appEarlyGl: Like sfEarlyGl, but for the app-vsync.
-    // appLate: The regular app vsync phase offset.
-    void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late,
-                         nsecs_t thresholdForNextVsync) EXCLUDES(mMutex);
+    VSyncModulator(IPhaseOffsetControl&, ConnectionHandle appConnectionHandle,
+                   ConnectionHandle sfConnectionHandle, const OffsetsConfig&);
 
-    // Sets the scheduler and vsync connection handlers.
-    void setSchedulerAndHandles(Scheduler* scheduler,
-                                Scheduler::ConnectionHandle* appConnectionHandle,
-                                Scheduler::ConnectionHandle* sfConnectionHandle) {
-        mScheduler = scheduler;
-        mAppConnectionHandle = appConnectionHandle;
-        mSfConnectionHandle = sfConnectionHandle;
-    }
+    void setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex);
 
     // Signals that a transaction has started, and changes offsets accordingly.
     void setTransactionStart(Scheduler::TransactionStart transactionStart);
@@ -98,36 +91,35 @@
     void onRefreshed(bool usedRenderEngine);
 
     // Returns the offsets that we are currently using
-    Offsets getOffsets() EXCLUDES(mMutex);
+    Offsets getOffsets() const EXCLUDES(mMutex);
 
 private:
+    friend class VSyncModulatorTest;
     // Returns the next offsets that we should be using
-    Offsets getNextOffsets() REQUIRES(mMutex);
-    // Returns the next offset type that we should use.
-    OffsetType getNextOffsetType();
+    const Offsets& getNextOffsets() const REQUIRES(mMutex);
     // Updates offsets and persists them into the scheduler framework.
     void updateOffsets() EXCLUDES(mMutex);
     void updateOffsetsLocked() REQUIRES(mMutex);
-    // Updates the internal offsets and offset type.
-    void flushOffsets() REQUIRES(mMutex);
+
+    IPhaseOffsetControl& mPhaseOffsetControl;
+    const ConnectionHandle mAppConnectionHandle;
+    const ConnectionHandle mSfConnectionHandle;
 
     mutable std::mutex mMutex;
-    std::unordered_map<OffsetType, Offsets> mOffsetMap GUARDED_BY(mMutex);
-    nsecs_t mThresholdForNextVsync;
+    OffsetsConfig mOffsetsConfig GUARDED_BY(mMutex);
 
-    Scheduler* mScheduler = nullptr;
-    Scheduler::ConnectionHandle* mAppConnectionHandle = nullptr;
-    Scheduler::ConnectionHandle* mSfConnectionHandle = nullptr;
-
-    Offsets mOffsets GUARDED_BY(mMutex) = {Scheduler::RefreshRateType::DEFAULT, 0, 0};
+    Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late};
 
     std::atomic<Scheduler::TransactionStart> mTransactionStart =
-            Scheduler::TransactionStart::NORMAL;
+            Scheduler::TransactionStart::Normal;
     std::atomic<bool> mRefreshRateChangePending = false;
+    std::atomic<bool> mExplicitEarlyWakeup = false;
     std::atomic<int> mRemainingEarlyFrameCount = 0;
     std::atomic<int> mRemainingRenderEngineUsageCount = 0;
+    std::atomic<std::chrono::steady_clock::time_point> mEarlyTxnStartTime = {};
+    std::atomic<std::chrono::steady_clock::time_point> mTxnAppliedTime = {};
 
     bool mTraceDetailedInfo = false;
 };
 
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
new file mode 100644
index 0000000..ab5773d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -0,0 +1,296 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+#include "VSyncPredictor.h"
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <algorithm>
+#include <chrono>
+#include <sstream>
+
+namespace android::scheduler {
+using base::StringAppendF;
+
+static auto constexpr kMaxPercent = 100u;
+
+VSyncPredictor::~VSyncPredictor() = default;
+
+VSyncPredictor::VSyncPredictor(nsecs_t idealPeriod, size_t historySize,
+                               size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent)
+      : mTraceOn(property_get_bool("debug.sf.vsp_trace", true)),
+        kHistorySize(historySize),
+        kMinimumSamplesForPrediction(minimumSamplesForPrediction),
+        kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
+        mIdealPeriod(idealPeriod) {
+    resetModel();
+}
+
+inline void VSyncPredictor::traceInt64If(const char* name, int64_t value) const {
+    if (CC_UNLIKELY(mTraceOn)) {
+        ATRACE_INT64(name, value);
+    }
+}
+
+inline size_t VSyncPredictor::next(int i) const {
+    return (i + 1) % mTimestamps.size();
+}
+
+bool VSyncPredictor::validate(nsecs_t timestamp) const {
+    if (mLastTimestampIndex < 0 || mTimestamps.empty()) {
+        return true;
+    }
+
+    auto const aValidTimestamp = mTimestamps[mLastTimestampIndex];
+    auto const percent = (timestamp - aValidTimestamp) % mIdealPeriod * kMaxPercent / mIdealPeriod;
+    return percent < kOutlierTolerancePercent || percent > (kMaxPercent - kOutlierTolerancePercent);
+}
+
+nsecs_t VSyncPredictor::currentPeriod() const {
+    std::lock_guard<std::mutex> lk(mMutex);
+    return std::get<0>(mRateMap.find(mIdealPeriod)->second);
+}
+
+bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
+    std::lock_guard<std::mutex> lk(mMutex);
+
+    if (!validate(timestamp)) {
+        // VSR could elect to ignore the incongruent timestamp or resetModel(). If ts is ignored,
+        // don't insert this ts into mTimestamps ringbuffer.
+        if (!mTimestamps.empty()) {
+            mKnownTimestamp =
+                    std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end()));
+        } else {
+            mKnownTimestamp = timestamp;
+        }
+        return false;
+    }
+
+    if (mTimestamps.size() != kHistorySize) {
+        mTimestamps.push_back(timestamp);
+        mLastTimestampIndex = next(mLastTimestampIndex);
+    } else {
+        mLastTimestampIndex = next(mLastTimestampIndex);
+        mTimestamps[mLastTimestampIndex] = timestamp;
+    }
+
+    if (mTimestamps.size() < kMinimumSamplesForPrediction) {
+        mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
+        return true;
+    }
+
+    // This is a 'simple linear regression' calculation of Y over X, with Y being the
+    // vsync timestamps, and X being the ordinal of vsync count.
+    // The calculated slope is the vsync period.
+    // Formula for reference:
+    // Sigma_i: means sum over all timestamps.
+    // mean(variable): statistical mean of variable.
+    // X: snapped ordinal of the timestamp
+    // Y: vsync timestamp
+    //
+    //         Sigma_i( (X_i - mean(X)) * (Y_i - mean(Y) )
+    // slope = -------------------------------------------
+    //         Sigma_i ( X_i - mean(X) ) ^ 2
+    //
+    // intercept = mean(Y) - slope * mean(X)
+    //
+    std::vector<nsecs_t> vsyncTS(mTimestamps.size());
+    std::vector<nsecs_t> ordinals(mTimestamps.size());
+
+    // normalizing to the oldest timestamp cuts down on error in calculating the intercept.
+    auto const oldest_ts = *std::min_element(mTimestamps.begin(), mTimestamps.end());
+    auto it = mRateMap.find(mIdealPeriod);
+    auto const currentPeriod = std::get<0>(it->second);
+    // TODO (b/144707443): its important that there's some precision in the mean of the ordinals
+    //                     for the intercept calculation, so scale the ordinals by 1000 to continue
+    //                     fixed point calculation. Explore expanding
+    //                     scheduler::utils::calculate_mean to have a fixed point fractional part.
+    static constexpr int64_t kScalingFactor = 1000;
+
+    for (auto i = 0u; i < mTimestamps.size(); i++) {
+        traceInt64If("VSP-ts", mTimestamps[i]);
+
+        vsyncTS[i] = mTimestamps[i] - oldest_ts;
+        ordinals[i] = ((vsyncTS[i] + (currentPeriod / 2)) / currentPeriod) * kScalingFactor;
+    }
+
+    auto meanTS = scheduler::calculate_mean(vsyncTS);
+    auto meanOrdinal = scheduler::calculate_mean(ordinals);
+    for (auto i = 0; i < vsyncTS.size(); i++) {
+        vsyncTS[i] -= meanTS;
+        ordinals[i] -= meanOrdinal;
+    }
+
+    auto top = 0ll;
+    auto bottom = 0ll;
+    for (auto i = 0; i < vsyncTS.size(); i++) {
+        top += vsyncTS[i] * ordinals[i];
+        bottom += ordinals[i] * ordinals[i];
+    }
+
+    if (CC_UNLIKELY(bottom == 0)) {
+        it->second = {mIdealPeriod, 0};
+        clearTimestamps();
+        return false;
+    }
+
+    nsecs_t const anticipatedPeriod = top * kScalingFactor / bottom;
+    nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor);
+
+    auto const percent = std::abs(anticipatedPeriod - mIdealPeriod) * kMaxPercent / mIdealPeriod;
+    if (percent >= kOutlierTolerancePercent) {
+        it->second = {mIdealPeriod, 0};
+        clearTimestamps();
+        return false;
+    }
+
+    traceInt64If("VSP-period", anticipatedPeriod);
+    traceInt64If("VSP-intercept", intercept);
+
+    it->second = {anticipatedPeriod, intercept};
+
+    ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp,
+          anticipatedPeriod, intercept);
+    return true;
+}
+
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
+    std::lock_guard<std::mutex> lk(mMutex);
+
+    auto const [slope, intercept] = getVSyncPredictionModel(lk);
+
+    if (mTimestamps.empty()) {
+        traceInt64If("VSP-mode", 1);
+        auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint;
+        auto const numPeriodsOut = ((timePoint - knownTimestamp) / mIdealPeriod) + 1;
+        return knownTimestamp + numPeriodsOut * mIdealPeriod;
+    }
+
+    auto const oldest = *std::min_element(mTimestamps.begin(), mTimestamps.end());
+
+    // See b/145667109, the ordinal calculation must take into account the intercept.
+    auto const zeroPoint = oldest + intercept;
+    auto const ordinalRequest = (timePoint - zeroPoint + slope) / slope;
+    auto const prediction = (ordinalRequest * slope) + intercept + oldest;
+
+    traceInt64If("VSP-mode", 0);
+    traceInt64If("VSP-timePoint", timePoint);
+    traceInt64If("VSP-prediction", prediction);
+
+    auto const printer = [&, slope = slope, intercept = intercept] {
+        std::stringstream str;
+        str << "prediction made from: " << timePoint << "prediction: " << prediction << " (+"
+            << prediction - timePoint << ") slope: " << slope << " intercept: " << intercept
+            << "oldestTS: " << oldest << " ordinal: " << ordinalRequest;
+        return str.str();
+    };
+
+    ALOGV("%s", printer().c_str());
+    LOG_ALWAYS_FATAL_IF(prediction < timePoint, "VSyncPredictor: model miscalculation: %s",
+                        printer().c_str());
+
+    return prediction;
+}
+
+std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel() const {
+    std::lock_guard<std::mutex> lk(mMutex);
+    return VSyncPredictor::getVSyncPredictionModel(lk);
+}
+
+std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel(
+        std::lock_guard<std::mutex> const&) const {
+    return mRateMap.find(mIdealPeriod)->second;
+}
+
+void VSyncPredictor::setPeriod(nsecs_t period) {
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lk(mMutex);
+    static constexpr size_t kSizeLimit = 30;
+    if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) {
+        mRateMap.erase(mRateMap.begin());
+    }
+
+    mIdealPeriod = period;
+    if (mRateMap.find(period) == mRateMap.end()) {
+        mRateMap[mIdealPeriod] = {period, 0};
+    }
+
+    clearTimestamps();
+}
+
+void VSyncPredictor::clearTimestamps() {
+    if (!mTimestamps.empty()) {
+        auto const maxRb = *std::max_element(mTimestamps.begin(), mTimestamps.end());
+        if (mKnownTimestamp) {
+            mKnownTimestamp = std::max(*mKnownTimestamp, maxRb);
+        } else {
+            mKnownTimestamp = maxRb;
+        }
+
+        mTimestamps.clear();
+        mLastTimestampIndex = 0;
+    }
+}
+
+bool VSyncPredictor::needsMoreSamples(nsecs_t now) const {
+    using namespace std::literals::chrono_literals;
+    std::lock_guard<std::mutex> lk(mMutex);
+    bool needsMoreSamples = true;
+    if (mTimestamps.size() >= kMinimumSamplesForPrediction) {
+        nsecs_t constexpr aLongTime =
+                std::chrono::duration_cast<std::chrono::nanoseconds>(500ms).count();
+        if (!(mLastTimestampIndex < 0 || mTimestamps.empty())) {
+            auto const lastTimestamp = mTimestamps[mLastTimestampIndex];
+            needsMoreSamples = !((lastTimestamp + aLongTime) > now);
+        }
+    }
+
+    ATRACE_INT("VSP-moreSamples", needsMoreSamples);
+    return needsMoreSamples;
+}
+
+void VSyncPredictor::resetModel() {
+    std::lock_guard<std::mutex> lk(mMutex);
+    mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
+    clearTimestamps();
+}
+
+void VSyncPredictor::dump(std::string& result) const {
+    std::lock_guard<std::mutex> lk(mMutex);
+    StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f);
+    StringAppendF(&result, "\tRefresh Rate Map:\n");
+    for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
+        StringAppendF(&result,
+                      "\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
+                      idealPeriod / 1e6f, std::get<0>(periodInterceptTuple) / 1e6f,
+                      std::get<1>(periodInterceptTuple));
+    }
+}
+
+} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
new file mode 100644
index 0000000..3ca878d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+#include "SchedulerUtils.h"
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+
+class VSyncPredictor : public VSyncTracker {
+public:
+    /*
+     * \param [in] idealPeriod  The initial ideal period to use.
+     * \param [in] historySize  The internal amount of entries to store in the model.
+     * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before
+     * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter
+     * samples that fall outlierTolerancePercent from an anticipated vsync event.
+     */
+    VSyncPredictor(nsecs_t idealPeriod, size_t historySize, size_t minimumSamplesForPrediction,
+                   uint32_t outlierTolerancePercent);
+    ~VSyncPredictor();
+
+    bool addVsyncTimestamp(nsecs_t timestamp) final;
+    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
+    nsecs_t currentPeriod() const final;
+    void resetModel() final;
+
+    /*
+     * Inform the model that the period is anticipated to change to a new value.
+     * model will use the period parameter to predict vsync events until enough
+     * timestamps with the new period have been collected.
+     *
+     * \param [in] period   The new period that should be used.
+     */
+    void setPeriod(nsecs_t period) final;
+
+    /* Query if the model is in need of more samples to make a prediction at timePoint.
+     * \param [in] timePoint    The timePoint to inquire of.
+     * \return  True, if model would benefit from more samples, False if not.
+     */
+    bool needsMoreSamples(nsecs_t timePoint) const;
+
+    std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
+
+    void dump(std::string& result) const final;
+
+private:
+    VSyncPredictor(VSyncPredictor const&) = delete;
+    VSyncPredictor& operator=(VSyncPredictor const&) = delete;
+    void clearTimestamps() REQUIRES(mMutex);
+
+    inline void traceInt64If(const char* name, int64_t value) const;
+    bool const mTraceOn;
+
+    size_t const kHistorySize;
+    size_t const kMinimumSamplesForPrediction;
+    size_t const kOutlierTolerancePercent;
+
+    std::mutex mutable mMutex;
+    size_t next(int i) const REQUIRES(mMutex);
+    bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
+    std::tuple<nsecs_t, nsecs_t> getVSyncPredictionModel(std::lock_guard<std::mutex> const&) const
+            REQUIRES(mMutex);
+
+    nsecs_t mIdealPeriod GUARDED_BY(mMutex);
+    std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
+
+    std::unordered_map<nsecs_t, std::tuple<nsecs_t, nsecs_t>> mutable mRateMap GUARDED_BY(mMutex);
+
+    int mLastTimestampIndex GUARDED_BY(mMutex) = 0;
+    std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
new file mode 100644
index 0000000..c743de0
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -0,0 +1,404 @@
+/*
+ * 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "VSyncReactor"
+//#define LOG_NDEBUG 0
+#include "VSyncReactor.h"
+#include <cutils/properties.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+#include "../TracedOrdinal.h"
+#include "TimeKeeper.h"
+#include "VSyncDispatch.h"
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+using base::StringAppendF;
+
+Clock::~Clock() = default;
+nsecs_t SystemClock::now() const {
+    return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+class PredictedVsyncTracer {
+public:
+    PredictedVsyncTracer(VSyncDispatch& dispatch)
+          : mRegistration(dispatch,
+                          std::bind(&PredictedVsyncTracer::callback, this, std::placeholders::_1,
+                                    std::placeholders::_2),
+                          "PredictedVsyncTracer") {
+        mRegistration.schedule(0, 0);
+    }
+
+private:
+    TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
+    VSyncCallbackRegistration mRegistration;
+
+    void callback(nsecs_t /*vsyncTime*/, nsecs_t /*targetWakeupTim*/) {
+        mParity = !mParity;
+        mRegistration.schedule(0, 0);
+    }
+};
+
+VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
+                           std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit,
+                           bool supportKernelIdleTimer)
+      : mClock(std::move(clock)),
+        mTracker(std::move(tracker)),
+        mDispatch(std::move(dispatch)),
+        mPendingLimit(pendingFenceLimit),
+        mPredictedVsyncTracer(property_get_bool("debug.sf.show_predicted_vsync", false)
+                                      ? std::make_unique<PredictedVsyncTracer>(*mDispatch)
+                                      : nullptr),
+        mSupportKernelIdleTimer(supportKernelIdleTimer) {}
+
+VSyncReactor::~VSyncReactor() = default;
+
+// The DispSync interface has a 'repeat this callback at rate' semantic. This object adapts
+// VSyncDispatch's individually-scheduled callbacks so as to meet DispSync's existing semantic
+// for now.
+class CallbackRepeater {
+public:
+    CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
+                     nsecs_t period, nsecs_t offset, nsecs_t notBefore)
+          : mName(name),
+            mCallback(cb),
+            mRegistration(dispatch,
+                          std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
+                                    std::placeholders::_2),
+                          mName),
+            mPeriod(period),
+            mOffset(offset),
+            mLastCallTime(notBefore) {}
+
+    ~CallbackRepeater() {
+        std::lock_guard<std::mutex> lk(mMutex);
+        mRegistration.cancel();
+    }
+
+    void start(nsecs_t offset) {
+        std::lock_guard<std::mutex> lk(mMutex);
+        mStopped = false;
+        mOffset = offset;
+
+        auto const schedule_result = mRegistration.schedule(calculateWorkload(), mLastCallTime);
+        LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
+                            "Error scheduling callback: rc %X", schedule_result);
+    }
+
+    void setPeriod(nsecs_t period) {
+        std::lock_guard<std::mutex> lk(mMutex);
+        if (period == mPeriod) {
+            return;
+        }
+        mPeriod = period;
+    }
+
+    void stop() {
+        std::lock_guard<std::mutex> lk(mMutex);
+        LOG_ALWAYS_FATAL_IF(mStopped, "DispSyncInterface misuse: callback already stopped");
+        mStopped = true;
+        mRegistration.cancel();
+    }
+
+    void dump(std::string& result) const {
+        std::lock_guard<std::mutex> lk(mMutex);
+        StringAppendF(&result, "\t%s: mPeriod=%.2f last vsync time %.2fms relative to now (%s)\n",
+                      mName.c_str(), mPeriod / 1e6f, (mLastCallTime - systemTime()) / 1e6f,
+                      mStopped ? "stopped" : "running");
+    }
+
+private:
+    void callback(nsecs_t vsynctime, nsecs_t wakeupTime) {
+        {
+            std::lock_guard<std::mutex> lk(mMutex);
+            mLastCallTime = vsynctime;
+        }
+
+        mCallback->onDispSyncEvent(wakeupTime, vsynctime);
+
+        {
+            std::lock_guard<std::mutex> lk(mMutex);
+            if (mStopped) {
+                return;
+            }
+            auto const schedule_result = mRegistration.schedule(calculateWorkload(), vsynctime);
+            LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
+                                "Error rescheduling callback: rc %X", schedule_result);
+        }
+    }
+
+    // DispSync offsets are defined as time after the vsync before presentation.
+    // VSyncReactor workloads are defined as time before the intended presentation vsync.
+    // Note change in sign between the two defnitions.
+    nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
+
+    const std::string mName;
+    DispSync::Callback* const mCallback;
+
+    std::mutex mutable mMutex;
+    VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
+    bool mStopped GUARDED_BY(mMutex) = false;
+    nsecs_t mPeriod GUARDED_BY(mMutex);
+    nsecs_t mOffset GUARDED_BY(mMutex);
+    nsecs_t mLastCallTime GUARDED_BY(mMutex);
+};
+
+bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
+    if (!fence) {
+        return false;
+    }
+
+    nsecs_t const signalTime = fence->getCachedSignalTime();
+    if (signalTime == Fence::SIGNAL_TIME_INVALID) {
+        return true;
+    }
+
+    std::lock_guard<std::mutex> lk(mMutex);
+    if (mExternalIgnoreFences || mInternalIgnoreFences) {
+        return true;
+    }
+
+    bool timestampAccepted = true;
+    for (auto it = mUnfiredFences.begin(); it != mUnfiredFences.end();) {
+        auto const time = (*it)->getCachedSignalTime();
+        if (time == Fence::SIGNAL_TIME_PENDING) {
+            it++;
+        } else if (time == Fence::SIGNAL_TIME_INVALID) {
+            it = mUnfiredFences.erase(it);
+        } else {
+            timestampAccepted &= mTracker->addVsyncTimestamp(time);
+
+            it = mUnfiredFences.erase(it);
+        }
+    }
+
+    if (signalTime == Fence::SIGNAL_TIME_PENDING) {
+        if (mPendingLimit == mUnfiredFences.size()) {
+            mUnfiredFences.erase(mUnfiredFences.begin());
+        }
+        mUnfiredFences.push_back(fence);
+    } else {
+        timestampAccepted &= mTracker->addVsyncTimestamp(signalTime);
+    }
+
+    if (!timestampAccepted) {
+        mMoreSamplesNeeded = true;
+        setIgnorePresentFencesInternal(true);
+        mPeriodConfirmationInProgress = true;
+    }
+
+    return mMoreSamplesNeeded;
+}
+
+void VSyncReactor::setIgnorePresentFences(bool ignoration) {
+    std::lock_guard<std::mutex> lk(mMutex);
+    mExternalIgnoreFences = ignoration;
+    updateIgnorePresentFencesInternal();
+}
+
+void VSyncReactor::setIgnorePresentFencesInternal(bool ignoration) {
+    mInternalIgnoreFences = ignoration;
+    updateIgnorePresentFencesInternal();
+}
+
+void VSyncReactor::updateIgnorePresentFencesInternal() {
+    if (mExternalIgnoreFences || mInternalIgnoreFences) {
+        mUnfiredFences.clear();
+    }
+}
+
+nsecs_t VSyncReactor::computeNextRefresh(int periodOffset, nsecs_t now) const {
+    auto const currentPeriod = periodOffset ? mTracker->currentPeriod() : 0;
+    return mTracker->nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod);
+}
+
+nsecs_t VSyncReactor::expectedPresentTime(nsecs_t now) {
+    return mTracker->nextAnticipatedVSyncTimeFrom(now);
+}
+
+void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) {
+    mPeriodConfirmationInProgress = true;
+    mPeriodTransitioningTo = newPeriod;
+    mMoreSamplesNeeded = true;
+    setIgnorePresentFencesInternal(true);
+}
+
+void VSyncReactor::endPeriodTransition() {
+    setIgnorePresentFencesInternal(false);
+    mMoreSamplesNeeded = false;
+    mPeriodTransitioningTo.reset();
+    mPeriodConfirmationInProgress = false;
+    mLastHwVsync.reset();
+}
+
+void VSyncReactor::setPeriod(nsecs_t period) {
+    ATRACE_INT64("VSR-setPeriod", period);
+    std::lock_guard lk(mMutex);
+    mLastHwVsync.reset();
+
+    if (!mSupportKernelIdleTimer && period == getPeriod()) {
+        endPeriodTransition();
+    } else {
+        startPeriodTransition(period);
+    }
+}
+
+nsecs_t VSyncReactor::getPeriod() {
+    return mTracker->currentPeriod();
+}
+
+void VSyncReactor::beginResync() {
+    mTracker->resetModel();
+}
+
+void VSyncReactor::endResync() {}
+
+bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> HwcVsyncPeriod) {
+    if (!mPeriodConfirmationInProgress) {
+        return false;
+    }
+
+    if (!mLastHwVsync && !HwcVsyncPeriod) {
+        return false;
+    }
+
+    const bool periodIsChanging =
+            mPeriodTransitioningTo && (*mPeriodTransitioningTo != getPeriod());
+    if (mSupportKernelIdleTimer && !periodIsChanging) {
+        // Clear out the Composer-provided period and use the allowance logic below
+        HwcVsyncPeriod = {};
+    }
+
+    auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
+    static constexpr int allowancePercent = 10;
+    static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
+    auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
+    if (HwcVsyncPeriod) {
+        return std::abs(*HwcVsyncPeriod - period) < allowance;
+    }
+
+    auto const distance = vsync_timestamp - *mLastHwVsync;
+    return std::abs(distance - period) < allowance;
+}
+
+bool VSyncReactor::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                                   bool* periodFlushed) {
+    assert(periodFlushed);
+
+    std::lock_guard<std::mutex> lk(mMutex);
+    if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
+        if (mPeriodTransitioningTo) {
+            mTracker->setPeriod(*mPeriodTransitioningTo);
+            for (auto& entry : mCallbacks) {
+                entry.second->setPeriod(*mPeriodTransitioningTo);
+            }
+            *periodFlushed = true;
+        }
+        endPeriodTransition();
+    } else if (mPeriodConfirmationInProgress) {
+        mLastHwVsync = timestamp;
+        mMoreSamplesNeeded = true;
+        *periodFlushed = false;
+    } else {
+        mMoreSamplesNeeded = false;
+        *periodFlushed = false;
+    }
+
+    mTracker->addVsyncTimestamp(timestamp);
+    return mMoreSamplesNeeded;
+}
+
+status_t VSyncReactor::addEventListener(const char* name, nsecs_t phase,
+                                        DispSync::Callback* callback,
+                                        nsecs_t /* lastCallbackTime */) {
+    std::lock_guard<std::mutex> lk(mMutex);
+    auto it = mCallbacks.find(callback);
+    if (it == mCallbacks.end()) {
+        // TODO (b/146557561): resolve lastCallbackTime semantics in DispSync i/f.
+        static auto constexpr maxListeners = 4;
+        if (mCallbacks.size() >= maxListeners) {
+            ALOGE("callback %s not added, exceeded callback limit of %i (currently %zu)", name,
+                  maxListeners, mCallbacks.size());
+            return NO_MEMORY;
+        }
+
+        auto const period = mTracker->currentPeriod();
+        auto repeater = std::make_unique<CallbackRepeater>(*mDispatch, callback, name, period,
+                                                           phase, mClock->now());
+        it = mCallbacks.emplace(std::pair(callback, std::move(repeater))).first;
+    }
+
+    it->second->start(phase);
+    return NO_ERROR;
+}
+
+status_t VSyncReactor::removeEventListener(DispSync::Callback* callback,
+                                           nsecs_t* /* outLastCallback */) {
+    std::lock_guard<std::mutex> lk(mMutex);
+    auto const it = mCallbacks.find(callback);
+    LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback %p not registered", callback);
+
+    it->second->stop();
+    return NO_ERROR;
+}
+
+status_t VSyncReactor::changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
+    std::lock_guard<std::mutex> lk(mMutex);
+    auto const it = mCallbacks.find(callback);
+    LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback was %p not registered", callback);
+
+    it->second->start(phase);
+    return NO_ERROR;
+}
+
+void VSyncReactor::dump(std::string& result) const {
+    std::lock_guard<std::mutex> lk(mMutex);
+    StringAppendF(&result, "VsyncReactor in use\n");
+    StringAppendF(&result, "Has %zu unfired fences\n", mUnfiredFences.size());
+    StringAppendF(&result, "mInternalIgnoreFences=%d mExternalIgnoreFences=%d\n",
+                  mInternalIgnoreFences, mExternalIgnoreFences);
+    StringAppendF(&result, "mMoreSamplesNeeded=%d mPeriodConfirmationInProgress=%d\n",
+                  mMoreSamplesNeeded, mPeriodConfirmationInProgress);
+    if (mPeriodTransitioningTo) {
+        StringAppendF(&result, "mPeriodTransitioningTo=%" PRId64 "\n", *mPeriodTransitioningTo);
+    } else {
+        StringAppendF(&result, "mPeriodTransitioningTo=nullptr\n");
+    }
+
+    if (mLastHwVsync) {
+        StringAppendF(&result, "Last HW vsync was %.2fms ago\n",
+                      (mClock->now() - *mLastHwVsync) / 1e6f);
+    } else {
+        StringAppendF(&result, "No Last HW vsync\n");
+    }
+
+    StringAppendF(&result, "CallbackRepeaters:\n");
+    for (const auto& [callback, repeater] : mCallbacks) {
+        repeater->dump(result);
+    }
+
+    StringAppendF(&result, "VSyncTracker:\n");
+    mTracker->dump(result);
+    StringAppendF(&result, "VSyncDispatch:\n");
+    mDispatch->dump(result);
+}
+
+void VSyncReactor::reset() {}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
new file mode 100644
index 0000000..265d89c
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <ui/FenceTime.h>
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+#include "DispSync.h"
+#include "TimeKeeper.h"
+namespace android::scheduler {
+
+class Clock;
+class VSyncDispatch;
+class VSyncTracker;
+class CallbackRepeater;
+class PredictedVsyncTracer;
+
+// TODO (b/145217110): consider renaming.
+class VSyncReactor : public android::DispSync {
+public:
+    VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
+                 std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit,
+                 bool supportKernelIdleTimer);
+    ~VSyncReactor();
+
+    bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final;
+    void setIgnorePresentFences(bool ignoration) final;
+
+    nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const final;
+    nsecs_t expectedPresentTime(nsecs_t now) final;
+
+    void setPeriod(nsecs_t period) final;
+    nsecs_t getPeriod() final;
+
+    // TODO: (b/145626181) remove begin,endResync functions from DispSync i/f when possible.
+    void beginResync() final;
+    bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                         bool* periodFlushed) final;
+    void endResync() final;
+
+    status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
+                              nsecs_t lastCallbackTime) final;
+    status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback) final;
+    status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) final;
+
+    void dump(std::string& result) const final;
+    void reset() final;
+
+private:
+    void setIgnorePresentFencesInternal(bool ignoration) REQUIRES(mMutex);
+    void updateIgnorePresentFencesInternal() REQUIRES(mMutex);
+    void startPeriodTransition(nsecs_t newPeriod) REQUIRES(mMutex);
+    void endPeriodTransition() REQUIRES(mMutex);
+    bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod)
+            REQUIRES(mMutex);
+
+    std::unique_ptr<Clock> const mClock;
+    std::unique_ptr<VSyncTracker> const mTracker;
+    std::unique_ptr<VSyncDispatch> const mDispatch;
+    size_t const mPendingLimit;
+
+    mutable std::mutex mMutex;
+    bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
+    bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
+    std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
+
+    bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
+    bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false;
+    std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
+    std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
+
+    std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks
+            GUARDED_BY(mMutex);
+
+    const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
+    const bool mSupportKernelIdleTimer = false;
+};
+
+class SystemClock : public Clock {
+    nsecs_t now() const final;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
new file mode 100644
index 0000000..05a6fc3
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/Timers.h>
+#include "VSyncDispatch.h"
+
+namespace android::scheduler {
+/*
+ * VSyncTracker is an interface for providing estimates on future Vsync signal times based on
+ * historical vsync timing data.
+ */
+class VSyncTracker {
+public:
+    virtual ~VSyncTracker();
+
+    /*
+     * Adds a known timestamp from a vsync timing source (HWVsync signal, present fence)
+     * to the model.
+     *
+     * \param [in] timestamp    The timestamp when the vsync signal was.
+     * \return                  True if the timestamp was consistent with the internal model,
+     *                          False otherwise
+     */
+    virtual bool addVsyncTimestamp(nsecs_t timestamp) = 0;
+
+    /*
+     * Access the next anticipated vsync time such that the anticipated time >= timePoint.
+     * This will always give the best accurate at the time of calling; multiple
+     * calls with the same timePoint might give differing values if the internal model
+     * is updated.
+     *
+     * \param [in] timePoint    The point in time after which to estimate a vsync event.
+     * \return                  A prediction of the timestamp of a vsync event.
+     */
+    virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0;
+
+    /*
+     * The current period of the vsync signal.
+     *
+     * \return  The current period of the vsync signal
+     */
+    virtual nsecs_t currentPeriod() const = 0;
+
+    /*
+     * Inform the tracker that the period is changing and the tracker needs to recalibrate itself.
+     *
+     * \param [in] period   The period that the system is changing into.
+     */
+    virtual void setPeriod(nsecs_t period) = 0;
+
+    /* Inform the tracker that the samples it has are not accurate for prediction. */
+    virtual void resetModel() = 0;
+
+    virtual void dump(std::string& result) const = 0;
+
+protected:
+    VSyncTracker(VSyncTracker const&) = delete;
+    VSyncTracker& operator=(VSyncTracker const&) = delete;
+    VSyncTracker() = default;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index c033290..1342cfc 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -14,54 +14,64 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include <sys/types.h>
-#include <errno.h>
-#include <dlfcn.h>
+#include "SurfaceFlinger.h"
 
-#include <algorithm>
-#include <cinttypes>
-#include <cmath>
-#include <cstdint>
-#include <functional>
-#include <mutex>
-#include <optional>
-#include <unordered_map>
-
-#include <cutils/properties.h>
-#include <log/log.h>
-
+#include <android-base/properties.h>
+#include <android/configuration.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/types.h>
+#include <android/hardware/power/1.0/IPower.h>
+#include <android/native_window.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
-
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/Display.h>
 #include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/Layer.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/RenderSurface.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputCompositionState.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <configstore/Utils.h>
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <dlfcn.h>
 #include <dvr/vr_flinger.h>
+#include <errno.h>
 #include <gui/BufferQueue.h>
 #include <gui/DebugEGLImageTracker.h>
-
 #include <gui/GuiConfig.h>
 #include <gui/IDisplayEventConnection.h>
 #include <gui/IProducerListener.h>
 #include <gui/LayerDebugInfo.h>
+#include <gui/LayerMetadata.h>
+#include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <hidl/ServiceManagement.h>
 #include <input/IInputFlinger.h>
+#include <layerproto/LayerProtoParser.h>
+#include <log/log.h>
+#include <private/android_filesystem_config.h>
+#include <private/gui/SyncFeatures.h>
 #include <renderengine/RenderEngine.h>
+#include <statslog.h>
+#include <sys/types.h>
 #include <ui/ColorSpace.h>
 #include <ui/DebugUtils.h>
+#include <ui/DisplayConfig.h>
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayStatInfo.h>
+#include <ui/DisplayState.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/PixelFormat.h>
 #include <ui/UiConfig.h>
@@ -72,58 +82,69 @@
 #include <utils/Trace.h>
 #include <utils/misc.h>
 
-#include <private/android_filesystem_config.h>
-#include <private/gui/SyncFeatures.h>
+#include <algorithm>
+#include <cinttypes>
+#include <cmath>
+#include <cstdint>
+#include <functional>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
 
 #include "BufferLayer.h"
 #include "BufferQueueLayer.h"
 #include "BufferStateLayer.h"
 #include "Client.h"
-#include "ColorLayer.h"
 #include "Colorizer.h"
 #include "ContainerLayer.h"
 #include "DisplayDevice.h"
-#include "Layer.h"
-#include "LayerVector.h"
-#include "MonitoredProducer.h"
-#include "NativeWindowSurface.h"
-#include "RefreshRateOverlay.h"
-#include "StartPropertySetThread.h"
-#include "SurfaceFlinger.h"
-#include "SurfaceInterceptor.h"
-
 #include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/DisplayIdentification.h"
 #include "DisplayHardware/FramebufferSurface.h"
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/VirtualDisplaySurface.h"
+#include "EffectLayer.h"
 #include "Effects/Daltonizer.h"
+#include "FrameTracer/FrameTracer.h"
+#include "Layer.h"
+#include "LayerVector.h"
+#include "MonitoredProducer.h"
+#include "NativeWindowSurface.h"
+#include "Promise.h"
+#include "RefreshRateOverlay.h"
 #include "RegionSamplingThread.h"
 #include "Scheduler/DispSync.h"
 #include "Scheduler/DispSyncSource.h"
 #include "Scheduler/EventControlThread.h"
 #include "Scheduler/EventThread.h"
-#include "Scheduler/InjectVSyncSource.h"
+#include "Scheduler/LayerHistory.h"
 #include "Scheduler/MessageQueue.h"
 #include "Scheduler/PhaseOffsets.h"
 #include "Scheduler/Scheduler.h"
+#include "StartPropertySetThread.h"
+#include "SurfaceFlingerProperties.h"
+#include "SurfaceInterceptor.h"
 #include "TimeStats/TimeStats.h"
-
-#include <cutils/compiler.h>
-
+#include "android-base/parseint.h"
 #include "android-base/stringprintf.h"
 
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
-#include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware/power/1.0/IPower.h>
-#include <configstore/Utils.h>
+#define MAIN_THREAD ACQUIRE(mStateLock) RELEASE(mStateLock)
 
-#include <layerproto/LayerProtoParser.h>
-#include "SurfaceFlingerProperties.h"
+#define ON_MAIN_THREAD(expr)                                       \
+    [&] {                                                          \
+        LOG_FATAL_IF(std::this_thread::get_id() != mMainThreadId); \
+        UnnecessaryLock lock(mStateLock);                          \
+        return (expr);                                             \
+    }()
+
+#undef NO_THREAD_SAFETY_ANALYSIS
+#define NO_THREAD_SAFETY_ANALYSIS \
+    _Pragma("GCC error \"Prefer MAIN_THREAD macros or {Conditional,Timed,Unnecessary}Lock.\"")
 
 namespace android {
 
+using namespace std::string_literals;
+
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
 using namespace android::sysprop;
@@ -136,6 +157,8 @@
 using ui::Hdr;
 using ui::RenderIntent;
 
+namespace hal = android::hardware::graphics::composer::hal;
+
 namespace {
 
 #pragma clang diagnostic push
@@ -163,63 +186,72 @@
     return false;
 }
 
-bool isHdrColorMode(const ColorMode colorMode) {
-    switch (colorMode) {
-        case ColorMode::BT2100_PQ:
-        case ColorMode::BT2100_HLG:
-            return true;
-        case ColorMode::DISPLAY_P3:
-        case ColorMode::ADOBE_RGB:
-        case ColorMode::DCI_P3:
-        case ColorMode::BT2020:
-        case ColorMode::DISPLAY_BT2020:
-        case ColorMode::NATIVE:
-        case ColorMode::STANDARD_BT601_625:
-        case ColorMode::STANDARD_BT601_625_UNADJUSTED:
-        case ColorMode::STANDARD_BT601_525:
-        case ColorMode::STANDARD_BT601_525_UNADJUSTED:
-        case ColorMode::STANDARD_BT709:
-        case ColorMode::SRGB:
-            return false;
-    }
-    return false;
-}
-
-ui::Transform::orientation_flags fromSurfaceComposerRotation(ISurfaceComposer::Rotation rotation) {
-    switch (rotation) {
-        case ISurfaceComposer::eRotateNone:
-            return ui::Transform::ROT_0;
-        case ISurfaceComposer::eRotate90:
-            return ui::Transform::ROT_90;
-        case ISurfaceComposer::eRotate180:
-            return ui::Transform::ROT_180;
-        case ISurfaceComposer::eRotate270:
-            return ui::Transform::ROT_270;
-    }
-    ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation);
-    return ui::Transform::ROT_0;
-}
-
 #pragma clang diagnostic pop
 
-class ConditionalLock {
-public:
-    ConditionalLock(Mutex& mutex, bool lock) : mMutex(mutex), mLocked(lock) {
-        if (lock) {
-            mMutex.lock();
-        }
+template <typename Mutex>
+struct SCOPED_CAPABILITY ConditionalLockGuard {
+    ConditionalLockGuard(Mutex& mutex, bool lock) ACQUIRE(mutex) : mutex(mutex), lock(lock) {
+        if (lock) mutex.lock();
     }
-    ~ConditionalLock() { if (mLocked) mMutex.unlock(); }
-private:
-    Mutex& mMutex;
-    bool mLocked;
+
+    ~ConditionalLockGuard() RELEASE() {
+        if (lock) mutex.unlock();
+    }
+
+    Mutex& mutex;
+    const bool lock;
 };
 
+using ConditionalLock = ConditionalLockGuard<Mutex>;
+
+struct SCOPED_CAPABILITY TimedLock {
+    TimedLock(Mutex& mutex, nsecs_t timeout, const char* whence) ACQUIRE(mutex)
+          : mutex(mutex), status(mutex.timedLock(timeout)) {
+        ALOGE_IF(!locked(), "%s timed out locking: %s (%d)", whence, strerror(-status), status);
+    }
+
+    ~TimedLock() RELEASE() {
+        if (locked()) mutex.unlock();
+    }
+
+    bool locked() const { return status == NO_ERROR; }
+
+    Mutex& mutex;
+    const status_t status;
+};
+
+struct SCOPED_CAPABILITY UnnecessaryLock {
+    explicit UnnecessaryLock(Mutex& mutex) ACQUIRE(mutex) {}
+    ~UnnecessaryLock() RELEASE() {}
+};
+
+// TODO(b/141333600): Consolidate with HWC2::Display::Config::Builder::getDefaultDensity.
+constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV;
+
+float getDensityFromProperty(const char* property, bool required) {
+    char value[PROPERTY_VALUE_MAX];
+    const float density = property_get(property, value, nullptr) > 0 ? std::atof(value) : 0.f;
+    if (!density && required) {
+        ALOGE("%s must be defined as a build property", property);
+        return FALLBACK_DENSITY;
+    }
+    return density;
+}
+
 // Currently we only support V0_SRGB and DISPLAY_P3 as composition preference.
 bool validateCompositionDataspace(Dataspace dataspace) {
     return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3;
 }
 
+class FrameRateFlexibilityToken : public BBinder {
+public:
+    FrameRateFlexibilityToken(std::function<void()> callback) : mCallback(callback) {}
+    virtual ~FrameRateFlexibilityToken() { mCallback(); }
+
+private:
+    std::function<void()> mCallback;
+};
+
 }  // namespace anonymous
 
 // ---------------------------------------------------------------------------
@@ -228,6 +260,7 @@
 const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
 const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
 const String16 sDump("android.permission.DUMP");
+const char* KERNEL_IDLE_TIMER_PROP = "graphics.display.kernel_idle_timer.enabled";
 
 // ---------------------------------------------------------------------------
 int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
@@ -236,14 +269,17 @@
 bool SurfaceFlinger::hasSyncFramework;
 bool SurfaceFlinger::useVrFlinger;
 int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
+uint32_t SurfaceFlinger::maxGraphicsWidth;
+uint32_t SurfaceFlinger::maxGraphicsHeight;
 bool SurfaceFlinger::hasWideColorDisplay;
-int SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault;
+ui::Rotation SurfaceFlinger::internalDisplayOrientation = ui::ROTATION_0;
 bool SurfaceFlinger::useColorManagement;
 bool SurfaceFlinger::useContextPriority;
 Dataspace SurfaceFlinger::defaultCompositionDataspace = Dataspace::V0_SRGB;
 ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
 Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
 ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
+bool SurfaceFlinger::useFrameRateApi;
 
 std::string getHwcServiceName() {
     char value[PROPERTY_VALUE_MAX] = {};
@@ -261,11 +297,11 @@
 
 std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
     switch(displayColorSetting) {
-        case DisplayColorSetting::MANAGED:
+        case DisplayColorSetting::kManaged:
             return std::string("Managed");
-        case DisplayColorSetting::UNMANAGED:
+        case DisplayColorSetting::kUnmanaged:
             return std::string("Unmanaged");
-        case DisplayColorSetting::ENHANCED:
+        case DisplayColorSetting::kEnhanced:
             return std::string("Enhanced");
         default:
             return std::string("Unknown ") +
@@ -277,11 +313,13 @@
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
       : mFactory(factory),
-        mPhaseOffsets(mFactory.createPhaseOffsets()),
         mInterceptor(mFactory.createSurfaceInterceptor(this)),
-        mTimeStats(mFactory.createTimeStats()),
+        mTimeStats(std::make_shared<impl::TimeStats>()),
+        mFrameTracer(std::make_unique<FrameTracer>()),
         mEventQueue(mFactory.createMessageQueue()),
-        mCompositionEngine(mFactory.createCompositionEngine()) {}
+        mCompositionEngine(mFactory.createCompositionEngine()),
+        mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
+        mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {}
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
     ALOGI("SurfaceFlinger is starting");
@@ -299,6 +337,9 @@
 
     maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
 
+    maxGraphicsWidth = std::max(max_graphics_width(0), 0);
+    maxGraphicsHeight = std::max(max_graphics_height(0), 0);
+
     hasWideColorDisplay = has_wide_color_display(false);
 
     useColorManagement = use_color_management(false);
@@ -319,23 +360,21 @@
 
     useContextPriority = use_context_priority(true);
 
-    auto tmpPrimaryDisplayOrientation = primary_display_orientation(
-            SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_0);
-    switch (tmpPrimaryDisplayOrientation) {
-        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_90:
-            SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation90;
+    using Values = SurfaceFlingerProperties::primary_display_orientation_values;
+    switch (primary_display_orientation(Values::ORIENTATION_0)) {
+        case Values::ORIENTATION_0:
             break;
-        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180:
-            SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation180;
+        case Values::ORIENTATION_90:
+            internalDisplayOrientation = ui::ROTATION_90;
             break;
-        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270:
-            SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation270;
+        case Values::ORIENTATION_180:
+            internalDisplayOrientation = ui::ROTATION_180;
             break;
-        default:
-            SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault;
+        case Values::ORIENTATION_270:
+            internalDisplayOrientation = ui::ROTATION_270;
             break;
     }
-    ALOGV("Primary Display Orientation is set to %2d.", SurfaceFlinger::primaryDisplayOrientation);
+    ALOGV("Internal Display Orientation: %s", toCString(internalDisplayOrientation));
 
     mInternalDisplayPrimaries = sysprop::getDisplayNativePrimaries();
 
@@ -345,6 +384,9 @@
     property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
     mGpuToCpuSupported = !atoi(value);
 
+    property_get("ro.build.type", value, "user");
+    mIsUserBuild = strcmp(value, "user") == 0;
+
     property_get("debug.sf.showupdates", value, "0");
     mDebugRegion = atoi(value);
 
@@ -372,24 +414,22 @@
     mLayerTripleBufferingDisabled = atoi(value);
     ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
 
-    const size_t defaultListSize = MAX_LAYERS;
+    property_get("ro.surface_flinger.supports_background_blur", value, "0");
+    bool supportsBlurs = atoi(value);
+    mSupportsBlur = supportsBlurs;
+    ALOGI_IF(!mSupportsBlur, "Disabling blur effects, they are not supported.");
+    property_get("ro.sf.blurs_are_expensive", value, "0");
+    mBlursAreExpensive = atoi(value);
+
+    const size_t defaultListSize = ISurfaceComposer::MAX_LAYERS;
     auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
     mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
 
-    mUseSmart90ForVideo = use_smart_90_for_video(false);
-    property_get("debug.sf.use_smart_90_for_video", value, "0");
-
-    int int_value = atoi(value);
-    if (int_value) {
-        mUseSmart90ForVideo = true;
-    }
-
     property_get("debug.sf.luma_sampling", value, "1");
     mLumaSampling = atoi(value);
 
-    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
-    mVsyncModulator.setPhaseOffsets(early, gl, late,
-                                    mPhaseOffsets->getOffsetThresholdForNextVsync());
+    property_get("debug.sf.disable_client_composition_cache", value, "0");
+    mDisableClientCompositionCache = atoi(value);
 
     // We should be reading 'persist.sys.sf.color_saturation' here
     // but since /data may be encrypted, we need to wait until after vold
@@ -404,18 +444,22 @@
         // for production purposes later on.
         android::hardware::details::setTrebleTestingOverride(true);
     }
-}
 
-void SurfaceFlinger::onFirstRef()
-{
-    mEventQueue->init(this);
+    useFrameRateApi = use_frame_rate_api(true);
+
+    mKernelIdleTimerEnabled = mSupportKernelIdleTimer = sysprop::support_kernel_idle_timer(false);
+    base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false");
 }
 
 SurfaceFlinger::~SurfaceFlinger() = default;
 
-void SurfaceFlinger::binderDied(const wp<IBinder>& /* who */)
-{
+void SurfaceFlinger::onFirstRef() {
+    mEventQueue->init(this);
+}
+
+void SurfaceFlinger::binderDied(const wp<IBinder>&) {
     // the window manager died on us. prepare its eulogy.
+    mBootFinished = false;
 
     // restore initial conditions (default device unblank, etc)
     initializeDisplays();
@@ -424,21 +468,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() {
@@ -466,17 +514,17 @@
 }
 
 void SurfaceFlinger::destroyDisplay(const sp<IBinder>& displayToken) {
-    Mutex::Autolock _l(mStateLock);
+    Mutex::Autolock lock(mStateLock);
 
-    ssize_t index = mCurrentState.displays.indexOfKey(displayToken);
+    const ssize_t index = mCurrentState.displays.indexOfKey(displayToken);
     if (index < 0) {
-        ALOGE("destroyDisplay: Invalid display token %p", displayToken.get());
+        ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
         return;
     }
 
     const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
-    if (!state.isVirtual()) {
-        ALOGE("destroyDisplay called for non-virtual display");
+    if (state.physical) {
+        ALOGE("%s: Invalid operation on physical display", __FUNCTION__);
         return;
     }
     mInterceptor->saveDisplayDeletion(state.sequenceId);
@@ -532,6 +580,11 @@
 
 void SurfaceFlinger::bootFinished()
 {
+    if (mBootFinished == true) {
+        ALOGE("Extra call to bootFinished");
+        return;
+    }
+    mBootFinished = true;
     if (mStartPropertySetThread->join() != NO_ERROR) {
         ALOGE("Join StartPropertySetThread failed!");
     }
@@ -539,6 +592,9 @@
     const nsecs_t duration = now - mBootTime;
     ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
 
+    mFrameTracer->initialize();
+    mTimeStats->onBootFinished();
+
     // wait patiently for the window manager death
     const String16 name("window");
     mWindowManager = defaultServiceManager()->getService(name);
@@ -566,20 +622,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 (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
-            // set the refresh rate according to the policy
-            const auto& performanceRefreshRate =
-                    mRefreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE);
-
-            if (isDisplayConfigAllowed(performanceRefreshRate.configId)) {
-                setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::Changed);
-            } else {
-                setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::Changed);
-            }
+        if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
+            enableRefreshRateOverlay(true);
         }
     }));
 }
@@ -600,9 +649,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) {
@@ -618,31 +670,29 @@
 void SurfaceFlinger::init() {
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
-
-    ALOGI("Phase offset NS: %" PRId64 "", mPhaseOffsets->getCurrentAppOffset());
-
     Mutex::Autolock _l(mStateLock);
 
     // Get a RenderEngine for the given display / config (can't fail)
-    int32_t renderEngineFeature = 0;
-    renderEngineFeature |= (useColorManagement ?
-                            renderengine::RenderEngine::USE_COLOR_MANAGEMENT : 0);
-    renderEngineFeature |= (useContextPriority ?
-                            renderengine::RenderEngine::USE_HIGH_PRIORITY_CONTEXT : 0);
-    renderEngineFeature |=
-            (enable_protected_contents(false) ? renderengine::RenderEngine::ENABLE_PROTECTED_CONTEXT
-                                              : 0);
-
     // TODO(b/77156734): We need to stop casting and use HAL types when possible.
     // Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display.
-    mCompositionEngine->setRenderEngine(
-            renderengine::RenderEngine::create(static_cast<int32_t>(defaultCompositionPixelFormat),
-                                               renderEngineFeature, maxFrameBufferAcquiredBuffers));
+    mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(
+            renderengine::RenderEngineCreationArgs::Builder()
+                .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
+                .setImageCacheSize(maxFrameBufferAcquiredBuffers)
+                .setUseColorManagerment(useColorManagement)
+                .setEnableProtectedContext(enable_protected_contents(false))
+                .setPrecacheToneMapperShaderOnly(false)
+                .setSupportsBackgroundBlur(mSupportsBlur)
+                .setContextPriority(useContextPriority
+                        ? renderengine::RenderEngine::ContextPriority::HIGH
+                        : renderengine::RenderEngine::ContextPriority::MEDIUM)
+                .build()));
+    mCompositionEngine->setTimeStats(mTimeStats);
 
     LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
             "Starting with vr flinger active is not currently supported.");
     mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
-    mCompositionEngine->getHwComposer().registerCallback(this, getBE().mComposerSequenceId);
+    mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId);
     // Process any initial hotplug and resulting display changes.
     processDisplayHotplugEventsLocked();
     const auto display = getDefaultDisplayDeviceLocked();
@@ -658,7 +708,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();
@@ -680,12 +730,16 @@
     // set initial conditions (e.g. unblank default device)
     initializeDisplays();
 
-    getRenderEngine().primeCache();
+    char primeShaderCache[PROPERTY_VALUE_MAX];
+    property_get("service.sf.prime_shader_cache", primeShaderCache, "1");
+    if (atoi(primeShaderCache)) {
+        getRenderEngine().primeCache();
+    }
 
     // Inform native graphics APIs whether the present timestamp is supported:
 
     const bool presentFenceReliable =
-            !getHwComposer().hasCapability(HWC2::Capability::PresentFenceIsNotReliable);
+            !getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE);
     mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);
 
     if (mStartPropertySetThread->Start() != NO_ERROR) {
@@ -710,6 +764,11 @@
 
     property_get("persist.sys.sf.color_mode", value, "0");
     mForceColorMode = static_cast<ColorMode>(atoi(value));
+
+    property_get("persist.sys.sf.disable_blurs", value, "0");
+    bool disableBlurs = atoi(value);
+    mDisableBlurs = disableBlurs;
+    ALOGI_IF(disableBlurs, "Disabling blur effects, user preference.");
 }
 
 void SurfaceFlinger::startBootAnim() {
@@ -758,15 +817,68 @@
     };
     ConditionalLock _l(mStateLock,
             std::this_thread::get_id() != mMainThreadId);
-    if (!getHwComposer().hasCapability(
-            HWC2::Capability::PresentFenceIsNotReliable)) {
+    if (!getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
         outSupported->push_back(FrameEvent::DISPLAY_PRESENT);
     }
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState* state) {
+    if (!displayToken || !state) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        return NAME_NOT_FOUND;
+    }
+
+    state->layerStack = display->getLayerStack();
+    state->orientation = display->getOrientation();
+
+    const Rect viewport = display->getViewport();
+    state->viewport = viewport.isValid() ? viewport.getSize() : display->getSize();
+
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& displayToken, DisplayInfo* info) {
+    if (!displayToken || !info) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        return NAME_NOT_FOUND;
+    }
+
+    if (const auto connectionType = display->getConnectionType())
+        info->connectionType = *connectionType;
+    else {
+        return INVALID_OPERATION;
+    }
+
+    if (mEmulatedDisplayDensity) {
+        info->density = mEmulatedDisplayDensity;
+    } else {
+        info->density = info->connectionType == DisplayConnectionType::Internal
+                ? mInternalDisplayDensity
+                : FALLBACK_DENSITY;
+    }
+    info->density /= ACONFIGURATION_DENSITY_MEDIUM;
+
+    info->secure = display->isSecure();
+    info->deviceProductInfo = getDeviceProductInfoLocked(*display);
+
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken,
-                                           Vector<DisplayInfo>* configs) {
+                                           Vector<DisplayConfig>* configs) {
     if (!displayToken || !configs) {
         return BAD_VALUE;
     }
@@ -778,77 +890,43 @@
         return NAME_NOT_FOUND;
     }
 
-    // TODO: Not sure if display density should handled by SF any longer
-    class Density {
-        static float getDensityFromProperty(char const* propName) {
-            char property[PROPERTY_VALUE_MAX];
-            float density = 0.0f;
-            if (property_get(propName, property, nullptr) > 0) {
-                density = strtof(property, nullptr);
-            }
-            return density;
-        }
-    public:
-        static float getEmuDensity() {
-            return getDensityFromProperty("qemu.sf.lcd_density"); }
-        static float getBuildDensity()  {
-            return getDensityFromProperty("ro.sf.lcd_density"); }
-    };
+    const bool isInternal = (displayId == getInternalDisplayIdLocked());
 
     configs->clear();
 
     for (const auto& hwConfig : getHwComposer().getConfigs(*displayId)) {
-        DisplayInfo info = DisplayInfo();
+        DisplayConfig config;
 
-        float xdpi = hwConfig->getDpiX();
-        float ydpi = hwConfig->getDpiY();
+        auto width = hwConfig->getWidth();
+        auto height = hwConfig->getHeight();
 
-        info.w = hwConfig->getWidth();
-        info.h = hwConfig->getHeight();
-        // Default display viewport to display width and height
-        info.viewportW = info.w;
-        info.viewportH = info.h;
+        auto xDpi = hwConfig->getDpiX();
+        auto yDpi = hwConfig->getDpiY();
 
-        if (displayId == getInternalDisplayIdLocked()) {
-            // The density of the device is provided by a build property
-            float density = Density::getBuildDensity() / 160.0f;
-            if (density == 0) {
-                // the build doesn't provide a density -- this is wrong!
-                // use xdpi instead
-                ALOGE("ro.sf.lcd_density must be defined as a build property");
-                density = xdpi / 160.0f;
-            }
-            if (Density::getEmuDensity()) {
-                // if "qemu.sf.lcd_density" is specified, it overrides everything
-                xdpi = ydpi = density = Density::getEmuDensity();
-                density /= 160.0f;
-            }
-            info.density = density;
-
-            // TODO: this needs to go away (currently needed only by webkit)
-            const auto display = getDefaultDisplayDeviceLocked();
-            info.orientation = display ? display->getOrientation() : 0;
-
-            // This is for screenrecord
-            const Rect viewport = display->getViewport();
-            if (viewport.isValid()) {
-                info.viewportW = uint32_t(viewport.getWidth());
-                info.viewportH = uint32_t(viewport.getHeight());
-            }
-        } else {
-            // TODO: where should this value come from?
-            static const int TV_DENSITY = 213;
-            info.density = TV_DENSITY / 160.0f;
-            info.orientation = 0;
+        if (isInternal &&
+            (internalDisplayOrientation == ui::ROTATION_90 ||
+             internalDisplayOrientation == ui::ROTATION_270)) {
+            std::swap(width, height);
+            std::swap(xDpi, yDpi);
         }
 
-        info.xdpi = xdpi;
-        info.ydpi = ydpi;
-        info.fps = 1e9 / hwConfig->getVsyncPeriod();
-        const auto refreshRateType =
-                mRefreshRateConfigs->getRefreshRateTypeFromHwcConfigId(hwConfig->getId());
-        const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(refreshRateType);
-        info.appVsyncOffset = offset.late.app;
+        config.resolution = ui::Size(width, height);
+
+        if (mEmulatedDisplayDensity) {
+            config.xDpi = mEmulatedDisplayDensity;
+            config.yDpi = mEmulatedDisplayDensity;
+        } else {
+            config.xDpi = xDpi;
+            config.yDpi = yDpi;
+        }
+
+        const nsecs_t period = hwConfig->getVsyncPeriod();
+        config.refreshRate = 1e9f / period;
+
+        const auto offsets = mPhaseConfiguration->getOffsetsForRefreshRate(config.refreshRate);
+        config.appVsyncOffset = offsets.late.app;
+        config.sfVsyncOffset = offsets.late.sf;
+        config.configGroup = hwConfig->getConfigGroup();
 
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
@@ -862,17 +940,9 @@
         //
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
-        info.presentationDeadline = hwConfig->getVsyncPeriod() - offset.late.sf + 1000000;
+        config.presentationDeadline = period - config.sfVsyncOffset + 1000000;
 
-        // All non-virtual displays are currently considered secure.
-        info.secure = true;
-
-        if (displayId == getInternalDisplayIdLocked() &&
-            primaryDisplayOrientation & DisplayState::eOrientationSwapMask) {
-            std::swap(info.w, info.h);
-        }
-
-        configs->push_back(info);
+        configs->push_back(config);
     }
 
     return NO_ERROR;
@@ -888,55 +958,99 @@
 }
 
 int SurfaceFlinger::getActiveConfig(const sp<IBinder>& displayToken) {
-    const auto display = getDisplayDevice(displayToken);
-    if (!display) {
-        ALOGE("getActiveConfig: Invalid display token %p", displayToken.get());
-        return BAD_VALUE;
+    int activeConfig;
+    bool isPrimary;
+
+    {
+        Mutex::Autolock lock(mStateLock);
+
+        if (const auto display = getDisplayDeviceLocked(displayToken)) {
+            activeConfig = display->getActiveConfig().value();
+            isPrimary = display->isPrimary();
+        } else {
+            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+            return NAME_NOT_FOUND;
+        }
     }
 
-    return display->getActiveConfig();
+    if (isPrimary) {
+        if (const auto config = getDesiredActiveConfig()) {
+            return config->configId.value();
+        }
+    }
+
+    return activeConfig;
 }
 
 void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) {
     ATRACE_CALL();
+    auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
+    ALOGV("setDesiredActiveConfig(%s)", refreshRate.getName().c_str());
 
-    // Don't check against the current mode yet. Worst case we set the desired
-    // config twice. However event generation config might have changed so we need to update it
-    // accordingly
     std::lock_guard<std::mutex> lock(mActiveConfigLock);
-    const Scheduler::ConfigEvent prevConfig = mDesiredActiveConfig.event;
-    mDesiredActiveConfig = info;
-    mDesiredActiveConfig.event = mDesiredActiveConfig.event | prevConfig;
+    if (mDesiredActiveConfigChanged) {
+        // If a config change is pending, just cache the latest request in
+        // mDesiredActiveConfig
+        const Scheduler::ConfigEvent prevConfig = mDesiredActiveConfig.event;
+        mDesiredActiveConfig = info;
+        mDesiredActiveConfig.event = mDesiredActiveConfig.event | prevConfig;
+    } else {
+        // Check is we are already at the desired config
+        const auto display = getDefaultDisplayDeviceLocked();
+        if (!display || display->getActiveConfig() == refreshRate.getConfigId()) {
+            return;
+        }
 
-    if (!mDesiredActiveConfigChanged) {
+        // Initiate a config change.
+        mDesiredActiveConfigChanged = true;
+        mDesiredActiveConfig = info;
+
         // This will trigger HWC refresh without resetting the idle timer.
         repaintEverythingForHWC();
         // Start receiving vsync samples now, so that we can detect a period
         // switch.
-        mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+        mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
         // As we called to set period, we will call to onRefreshRateChangeCompleted once
         // DispSync model is locked.
-        mVsyncModulator.onRefreshRateChangeInitiated();
-        mPhaseOffsets->setRefreshRateType(info.type);
-        const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
-        mVsyncModulator.setPhaseOffsets(early, gl, late,
-                                        mPhaseOffsets->getOffsetThresholdForNextVsync());
+        mVSyncModulator->onRefreshRateChangeInitiated();
+
+        mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
+        mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+        mScheduler->setConfigChangePending(true);
     }
-    mDesiredActiveConfigChanged = true;
-    ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
 
     if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->changeRefreshRate(mDesiredActiveConfig.type);
+        mRefreshRateOverlay->changeRefreshRate(refreshRate);
     }
 }
 
 status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
     ATRACE_CALL();
 
-    std::vector<int32_t> allowedConfig;
-    allowedConfig.push_back(mode);
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
 
-    return setAllowedDisplayConfigs(displayToken, allowedConfig);
+    auto future = schedule([=]() -> status_t {
+        const auto display = ON_MAIN_THREAD(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");
+            return INVALID_OPERATION;
+        } else {
+            const HwcConfigIndexType config(mode);
+            const float fps = mRefreshRateConfigs->getRefreshRateFromConfigId(config).getFps();
+            const scheduler::RefreshRateConfigs::Policy policy{config, {fps, fps}};
+            constexpr bool kOverridePolicy = false;
+
+            return setDesiredDisplayConfigSpecsInternal(display, policy, kOverridePolicy);
+        }
+    });
+
+    return future.get();
 }
 
 void SurfaceFlinger::setActiveConfigInternal() {
@@ -947,21 +1061,29 @@
         return;
     }
 
-    std::lock_guard<std::mutex> lock(mActiveConfigLock);
-    mRefreshRateConfigs->setCurrentConfig(mUpcomingActiveConfig.configId);
-    mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
+    auto& oldRefreshRate =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig());
 
+    std::lock_guard<std::mutex> lock(mActiveConfigLock);
+    mRefreshRateConfigs->setCurrentConfigId(mUpcomingActiveConfig.configId);
+    mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
     display->setActiveConfig(mUpcomingActiveConfig.configId);
 
-    mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
-    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
-    mVsyncModulator.setPhaseOffsets(early, gl, late,
-                                    mPhaseOffsets->getOffsetThresholdForNextVsync());
-    ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
+    auto& refreshRate =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
+    if (refreshRate.getVsyncPeriod() != oldRefreshRate.getVsyncPeriod()) {
+        mTimeStats->incrementRefreshRateSwitches();
+    }
+    mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
+    mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+    ATRACE_INT("ActiveConfigFPS", refreshRate.getFps());
 
     if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
-        mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
-                                    mUpcomingActiveConfig.configId);
+        const nsecs_t vsyncPeriod =
+                mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId)
+                        .getVsyncPeriod();
+        mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
+                                                  mUpcomingActiveConfig.configId, vsyncPeriod);
     }
 }
 
@@ -969,67 +1091,71 @@
     std::lock_guard<std::mutex> lock(mActiveConfigLock);
     mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
     mDesiredActiveConfigChanged = false;
-    ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
 
-    mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
-    mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
-    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
-    mVsyncModulator.setPhaseOffsets(early, gl, late,
-                                    mPhaseOffsets->getOffsetThresholdForNextVsync());
+    const auto& refreshRate =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
+    mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
+    mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());
+    mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+    mScheduler->setConfigChangePending(false);
 }
 
-bool SurfaceFlinger::performSetActiveConfig() {
+void SurfaceFlinger::performSetActiveConfig() {
     ATRACE_CALL();
-    if (mCheckPendingFence) {
-        if (previousFrameMissed()) {
-            // fence has not signaled yet. wait for the next invalidate
-            mEventQueue->invalidate();
-            return true;
-        }
-
-        // We received the present fence from the HWC, so we assume it successfully updated
-        // the config, hence we update SF.
-        mCheckPendingFence = false;
-        setActiveConfigInternal();
-    }
-
+    ALOGV("performSetActiveConfig");
     // Store the local variable to release the lock.
-    ActiveConfigInfo desiredActiveConfig;
-    {
-        std::lock_guard<std::mutex> lock(mActiveConfigLock);
-        if (!mDesiredActiveConfigChanged) {
-            return false;
-        }
-        desiredActiveConfig = mDesiredActiveConfig;
+    const auto desiredActiveConfig = getDesiredActiveConfig();
+    if (!desiredActiveConfig) {
+        // No desired active config pending to be applied
+        return;
     }
 
+    auto& refreshRate =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig->configId);
+    ALOGV("performSetActiveConfig changing active config to %d(%s)",
+          refreshRate.getConfigId().value(), refreshRate.getName().c_str());
     const auto display = getDefaultDisplayDeviceLocked();
-    if (!display || display->getActiveConfig() == desiredActiveConfig.configId) {
+    if (!display || display->getActiveConfig() == desiredActiveConfig->configId) {
         // display is not valid or we are already in the requested mode
         // on both cases there is nothing left to do
         desiredActiveConfigChangeDone();
-        return false;
+        return;
     }
 
     // Desired active config was set, it is different than the config currently in use, however
     // allowed configs might have change by the time we process the refresh.
     // Make sure the desired config is still allowed
-    if (!isDisplayConfigAllowed(desiredActiveConfig.configId)) {
+    if (!isDisplayConfigAllowed(desiredActiveConfig->configId)) {
         desiredActiveConfigChangeDone();
-        return false;
+        return;
     }
 
-    mUpcomingActiveConfig = desiredActiveConfig;
+    mUpcomingActiveConfig = *desiredActiveConfig;
     const auto displayId = display->getId();
     LOG_ALWAYS_FATAL_IF(!displayId);
 
-    ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId);
-    getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
+    ATRACE_INT("ActiveConfigFPS_HWC", refreshRate.getFps());
 
-    // we need to submit an empty frame to HWC to start the process
-    mCheckPendingFence = true;
-    mEventQueue->invalidate();
-    return false;
+    // TODO(b/142753666) use constrains
+    hal::VsyncPeriodChangeConstraints constraints;
+    constraints.desiredTimeNanos = systemTime();
+    constraints.seamlessRequired = false;
+
+    hal::VsyncPeriodChangeTimeline outTimeline;
+    auto status =
+            getHwComposer().setActiveConfigWithConstraints(*displayId,
+                                                           mUpcomingActiveConfig.configId.value(),
+                                                           constraints, &outTimeline);
+    if (status != NO_ERROR) {
+        // setActiveConfigWithConstraints may fail if a hotplug event is just about
+        // to be sent. We just log the error in this case.
+        ALOGW("setActiveConfigWithConstraints failed: %d", status);
+        return;
+    }
+
+    mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
+    // Scheduler will submit an empty frame to HWC if needed.
+    mSetActiveConfigPending = true;
 }
 
 status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
@@ -1075,7 +1201,7 @@
 
     // Currently we only support this API for a single internal display.
     if (getInternalDisplayToken() != displayToken) {
-        return BAD_VALUE;
+        return NAME_NOT_FOUND;
     }
 
     memcpy(&primaries, &mInternalDisplayPrimaries, sizeof(ui::DisplayPrimaries));
@@ -1083,14 +1209,16 @@
 }
 
 ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& displayToken) {
-    if (const auto display = getDisplayDevice(displayToken)) {
+    Mutex::Autolock lock(mStateLock);
+
+    if (const auto display = getDisplayDeviceLocked(displayToken)) {
         return display->getCompositionDisplay()->getState().colorMode;
     }
     return static_cast<ColorMode>(BAD_VALUE);
 }
 
 status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) {
-    postMessageSync(new LambdaMessage([&] {
+    schedule([=]() MAIN_THREAD {
         Vector<ColorMode> modes;
         getDisplayColorModes(displayToken, &modes);
         bool exists = std::find(std::begin(modes), std::end(modes), mode) != std::end(modes);
@@ -1099,7 +1227,7 @@
                   decodeColorMode(mode).c_str(), mode, displayToken.get());
             return;
         }
-        const auto display = getDisplayDevice(displayToken);
+        const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
             ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p",
                   decodeColorMode(mode).c_str(), mode, displayToken.get());
@@ -1107,14 +1235,76 @@
             ALOGW("Attempt to set active color mode %s (%d) for virtual display",
                   decodeColorMode(mode).c_str(), mode);
         } else {
-            display->getCompositionDisplay()->setColorMode(mode, Dataspace::UNKNOWN,
-                                                           RenderIntent::COLORIMETRIC);
+            display->getCompositionDisplay()->setColorProfile(
+                    compositionengine::Output::ColorProfile{mode, Dataspace::UNKNOWN,
+                                                            RenderIntent::COLORIMETRIC,
+                                                            Dataspace::UNKNOWN});
         }
-    }));
+    }).wait();
 
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
+                                                      bool* outSupport) const {
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        return NAME_NOT_FOUND;
+    }
+    *outSupport =
+            getHwComposer().hasDisplayCapability(*displayId,
+                                                 hal::DisplayCapability::AUTO_LOW_LATENCY_MODE);
+    return NO_ERROR;
+}
+
+void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
+    static_cast<void>(schedule([=]() MAIN_THREAD {
+        if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
+            getHwComposer().setAutoLowLatencyMode(*displayId, on);
+        } else {
+            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+        }
+    }));
+}
+
+status_t SurfaceFlinger::getGameContentTypeSupport(const sp<IBinder>& displayToken,
+                                                   bool* outSupport) const {
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        return NAME_NOT_FOUND;
+    }
+
+    std::vector<hal::ContentType> types;
+    getHwComposer().getSupportedContentTypes(*displayId, &types);
+
+    *outSupport = std::any_of(types.begin(), types.end(),
+                              [](auto type) { return type == hal::ContentType::GAME; });
+    return NO_ERROR;
+}
+
+void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
+    static_cast<void>(schedule([=]() MAIN_THREAD {
+        if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
+            const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE;
+            getHwComposer().setContentType(*displayId, type);
+        } else {
+            ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+        }
+    }));
+}
+
 status_t SurfaceFlinger::clearAnimationFrameStats() {
     Mutex::Autolock _l(mStateLock);
     mAnimFrameTracker.clearStats();
@@ -1129,15 +1319,15 @@
 
 status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& displayToken,
                                             HdrCapabilities* outCapabilities) const {
-    Mutex::Autolock _l(mStateLock);
+    Mutex::Autolock lock(mStateLock);
 
     const auto display = getDisplayDeviceLocked(displayToken);
     if (!display) {
-        ALOGE("getHdrCapabilities: Invalid display token %p", displayToken.get());
-        return BAD_VALUE;
+        ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+        return NAME_NOT_FOUND;
     }
 
-    // At this point the DisplayDeivce should already be set up,
+    // At this point the DisplayDevice should already be set up,
     // meaning the luminance information is already queried from
     // hardware composer and stored properly.
     const HdrCapabilities& capabilities = display->getHdrCapabilities();
@@ -1149,6 +1339,30 @@
     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,
@@ -1156,39 +1370,45 @@
     if (!outFormat || !outDataspace || !outComponentMask) {
         return BAD_VALUE;
     }
-    const auto display = getDisplayDevice(displayToken);
-    if (!display || !display->getId()) {
-        ALOGE("getDisplayedContentSamplingAttributes: Bad display token: %p", display.get());
-        return BAD_VALUE;
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        return NAME_NOT_FOUND;
     }
-    return getHwComposer().getDisplayedContentSamplingAttributes(*display->getId(), outFormat,
+
+    return getHwComposer().getDisplayedContentSamplingAttributes(*displayId, outFormat,
                                                                  outDataspace, outComponentMask);
 }
 
 status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken,
                                                           bool enable, uint8_t componentMask,
-                                                          uint64_t maxFrames) const {
-    const auto display = getDisplayDevice(displayToken);
-    if (!display || !display->getId()) {
-        ALOGE("setDisplayContentSamplingEnabled: Bad display token: %p", display.get());
-        return BAD_VALUE;
-    }
-
-    return getHwComposer().setDisplayContentSamplingEnabled(*display->getId(), enable,
-                                                            componentMask, maxFrames);
+                                                          uint64_t maxFrames) {
+    return schedule([=]() MAIN_THREAD -> 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,
                                                    uint64_t maxFrames, uint64_t timestamp,
                                                    DisplayedFrameStats* outStats) const {
-    const auto display = getDisplayDevice(displayToken);
-    if (!display || !display->getId()) {
-        ALOGE("getDisplayContentSample: Bad display token: %p", displayToken.get());
-        return BAD_VALUE;
+    Mutex::Autolock lock(mStateLock);
+
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        return NAME_NOT_FOUND;
     }
 
-    return getHwComposer().getDisplayedContentSample(*display->getId(), maxFrames, timestamp,
-                                                     outStats);
+    return getHwComposer().getDisplayedContentSample(*displayId, maxFrames, timestamp, outStats);
 }
 
 status_t SurfaceFlinger::getProtectedContentSupport(bool* outSupported) const {
@@ -1204,84 +1424,44 @@
     if (!displayToken || !outIsWideColorDisplay) {
         return BAD_VALUE;
     }
-    Mutex::Autolock _l(mStateLock);
+
+    Mutex::Autolock lock(mStateLock);
     const auto display = getDisplayDeviceLocked(displayToken);
     if (!display) {
-        return BAD_VALUE;
+        return NAME_NOT_FOUND;
     }
 
-    // Use hasWideColorDisplay to override built-in display.
-    const auto displayId = display->getId();
-    if (displayId && displayId == getInternalDisplayIdLocked()) {
-        *outIsWideColorDisplay = hasWideColorDisplay;
-        return NO_ERROR;
-    }
-    *outIsWideColorDisplay = display->hasWideColorGamut();
+    *outIsWideColorDisplay =
+            display->isPrimary() ? hasWideColorDisplay : display->hasWideColorGamut();
     return NO_ERROR;
 }
 
 status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
-    postMessageSync(new LambdaMessage([&] {
-        Mutex::Autolock _l(mStateLock);
+    schedule([=] {
+        Mutex::Autolock lock(mStateLock);
 
-        if (mInjectVSyncs == enable) {
-            return;
+        if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
+            mEventQueue->setEventConnection(
+                    mScheduler->getEventConnection(enable ? handle : mSfConnectionHandle));
         }
-
-        // TODO(b/128863962): Part of the Injector should be refactored, so that it
-        // can be passed to Scheduler.
-        if (enable) {
-            ALOGV("VSync Injections enabled");
-            if (mVSyncInjector.get() == nullptr) {
-                mVSyncInjector = std::make_unique<InjectVSyncSource>();
-                mInjectorEventThread = std::make_unique<
-                        impl::EventThread>(mVSyncInjector.get(),
-                                           impl::EventThread::InterceptVSyncsCallback(),
-                                           "injEventThread");
-            }
-            mEventQueue->setEventThread(mInjectorEventThread.get(), [&] { mScheduler->resync(); });
-        } else {
-            ALOGV("VSync Injections disabled");
-            mEventQueue->setEventThread(mScheduler->getEventThread(mSfConnectionHandle),
-                                        [&] { mScheduler->resync(); });
-        }
-
-        mInjectVSyncs = enable;
-    }));
+    }).wait();
 
     return NO_ERROR;
 }
 
 status_t SurfaceFlinger::injectVSync(nsecs_t when) {
-    Mutex::Autolock _l(mStateLock);
-
-    if (!mInjectVSyncs) {
-        ALOGE("VSync Injections not enabled");
-        return BAD_VALUE;
-    }
-    if (mInjectVSyncs && mInjectorEventThread.get() != nullptr) {
-        ALOGV("Injecting VSync inside SurfaceFlinger");
-        mVSyncInjector->onInjectSyncEvent(when);
-    }
-    return NO_ERROR;
+    Mutex::Autolock lock(mStateLock);
+    return mScheduler->injectVSync(when, calculateExpectedPresentTime(when)) ? NO_ERROR : BAD_VALUE;
 }
 
-status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const
-        NO_THREAD_SAFETY_ANALYSIS {
-    // Try to acquire a lock for 1s, fail gracefully
-    const status_t err = mStateLock.timedLock(s2ns(1));
-    const bool locked = (err == NO_ERROR);
-    if (!locked) {
-        ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err);
-        return TIMED_OUT;
-    }
-
+status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
     outLayers->clear();
-    mCurrentState.traverseInZOrder([&](Layer* layer) {
-        outLayers->push_back(layer->getLayerDebugInfo());
-    });
-
-    mStateLock.unlock();
+    schedule([=] {
+        const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+        mDrawingState.traverseInZOrder([&](Layer* layer) {
+            outLayers->push_back(layer->getLayerDebugInfo(display.get()));
+        });
+    }).wait();
     return NO_ERROR;
 }
 
@@ -1302,7 +1482,9 @@
     if (!listener || samplingArea == Rect::INVALID_RECT) {
         return BAD_VALUE;
     }
-    mRegionSamplingThread->addListener(samplingArea, stopLayerHandle, listener);
+
+    const wp<Layer> stopLayer = fromHandle(stopLayerHandle);
+    mRegionSamplingThread->addListener(samplingArea, stopLayer, listener);
     return NO_ERROR;
 }
 
@@ -1319,25 +1501,33 @@
     if (!displayToken || !outSupport) {
         return BAD_VALUE;
     }
+
+    Mutex::Autolock lock(mStateLock);
+
     const auto displayId = getPhysicalDisplayIdLocked(displayToken);
     if (!displayId) {
         return NAME_NOT_FOUND;
     }
     *outSupport =
-            getHwComposer().hasDisplayCapability(displayId, HWC2::DisplayCapability::Brightness);
+            getHwComposer().hasDisplayCapability(*displayId, hal::DisplayCapability::BRIGHTNESS);
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken,
-                                              float brightness) const {
+status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) {
     if (!displayToken) {
         return BAD_VALUE;
     }
-    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
-    if (!displayId) {
-        return NAME_NOT_FOUND;
-    }
-    return getHwComposer().setDisplayBrightness(*displayId, brightness);
+
+    return promise::chain(schedule([=]() MAIN_THREAD {
+               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) {
@@ -1360,19 +1550,15 @@
     return mScheduler->createDisplayEventConnection(handle, configChanged);
 }
 
-// ----------------------------------------------------------------------------
-
-void SurfaceFlinger::waitForEvent() {
-    mEventQueue->waitMessage();
-}
-
 void SurfaceFlinger::signalTransaction() {
     mScheduler->resetIdleTimer();
+    mPowerAdvisor.notifyDisplayUpdateImminent();
     mEventQueue->invalidate();
 }
 
 void SurfaceFlinger::signalLayerUpdate() {
     mScheduler->resetIdleTimer();
+    mPowerAdvisor.notifyDisplayUpdateImminent();
     mEventQueue->invalidate();
 }
 
@@ -1381,38 +1567,18 @@
     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)) {
         return 0;
     }
 
-    const auto config = getHwComposer().getActiveConfig(*displayId);
-    return config ? config->getVsyncPeriod() : 0;
+    return getHwComposer().getDisplayVsyncPeriod(*displayId);
 }
 
-void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
-                                     int64_t timestamp) {
+void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId,
+                                     int64_t timestamp,
+                                     std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
     ATRACE_NAME("SF onVsync");
 
     Mutex::Autolock lock(mStateLock);
@@ -1431,9 +1597,9 @@
     }
 
     bool periodFlushed = false;
-    mScheduler->addResyncSample(timestamp, &periodFlushed);
+    mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
     if (periodFlushed) {
-        mVsyncModulator.onRefreshRateChangeCompleted();
+        mVSyncModulator->onRefreshRateChangeCompleted();
     }
 }
 
@@ -1442,11 +1608,12 @@
     *compositorTiming = getBE().mCompositorTiming;
 }
 
-bool SurfaceFlinger::isDisplayConfigAllowed(int32_t configId) {
-    return mAllowedDisplayConfigs.empty() || mAllowedDisplayConfigs.count(configId);
+bool SurfaceFlinger::isDisplayConfigAllowed(HwcConfigIndexType configId) const {
+    return mRefreshRateConfigs->isConfigAllowed(configId);
 }
 
-void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::ConfigEvent event) {
+void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate,
+                                             Scheduler::ConfigEvent event) {
     const auto display = getDefaultDisplayDeviceLocked();
     if (!display || mBootStage != BootStage::FINISHED) {
         return;
@@ -1454,21 +1621,19 @@
     ATRACE_CALL();
 
     // Don't do any updating if the current fps is the same as the new one.
-    const auto& refreshRateConfig = mRefreshRateConfigs->getRefreshRateFromType(refreshRate);
-    const int desiredConfigId = refreshRateConfig.configId;
-
-    if (!isDisplayConfigAllowed(desiredConfigId)) {
-        ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
+    if (!isDisplayConfigAllowed(refreshRate.getConfigId())) {
+        ALOGV("Skipping config %d as it is not part of allowed configs",
+              refreshRate.getConfigId().value());
         return;
     }
 
-    setDesiredActiveConfig({refreshRate, desiredConfigId, event});
+    setDesiredActiveConfig({refreshRate.getConfigId(), event});
 }
 
-void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
-                                       HWC2::Connection connection) {
+void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId,
+                                       hal::Connection connection) {
     ALOGV("%s(%d, %" PRIu64 ", %s)", __FUNCTION__, sequenceId, hwcDisplayId,
-          connection == HWC2::Connection::Connected ? "connected" : "disconnected");
+          connection == hal::Connection::CONNECTED ? "connected" : "disconnected");
 
     // Ignore events that do not have the right sequenceId.
     if (sequenceId != getBE().mComposerSequenceId) {
@@ -1491,7 +1656,22 @@
     setTransactionFlags(eDisplayTransactionNeeded);
 }
 
-void SurfaceFlinger::onRefreshReceived(int sequenceId, hwc2_display_t /*hwcDisplayId*/) {
+void SurfaceFlinger::onVsyncPeriodTimingChangedReceived(
+        int32_t sequenceId, hal::HWDisplayId /*display*/,
+        const hal::VsyncPeriodChangeTimeline& updatedTimeline) {
+    Mutex::Autolock lock(mStateLock);
+    if (sequenceId != getBE().mComposerSequenceId) {
+        return;
+    }
+    mScheduler->onNewVsyncPeriodChangeTimeline(updatedTimeline);
+}
+
+void SurfaceFlinger::onSeamlessPossible(int32_t /*sequenceId*/, hal::HWDisplayId /*display*/) {
+    // TODO(b/142753666): use constraints when calling to setActiveConfigWithConstrains and
+    // use this callback to know when to retry in case of SEAMLESS_NOT_POSSIBLE.
+}
+
+void SurfaceFlinger::onRefreshReceived(int sequenceId, hal::HWDisplayId /*hwcDisplayId*/) {
     Mutex::Autolock lock(mStateLock);
     if (sequenceId != getBE().mComposerSequenceId) {
         return;
@@ -1504,24 +1684,22 @@
 
     // 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([=]() MAIN_THREAD { setPrimaryVsyncEnabledInternal(enabled); }));
 }
 
 void SurfaceFlinger::setPrimaryVsyncEnabledInternal(bool enabled) {
     ATRACE_CALL();
 
-    mHWCVsyncPendingState = enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable;
+    mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
 
     if (const auto displayId = getInternalDisplayIdLocked()) {
         sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
         if (display && display->isPoweredOn()) {
-            setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
+            getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
         }
     }
 }
 
-// Note: it is assumed the caller holds |mStateLock| when this is called
 void SurfaceFlinger::resetDisplayState() {
     mScheduler->disableHardwareVsync(true);
     // Clear the drawing state so that the logic inside of
@@ -1554,15 +1732,14 @@
     sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
     LOG_ALWAYS_FATAL_IF(!display);
 
-    const int currentDisplayPowerMode = display->getPowerMode();
+    const hal::PowerMode currentDisplayPowerMode = display->getPowerMode();
 
     // Clear out all the output layers from the composition engine for all
     // displays before destroying the hardware composer interface. This ensures
     // any HWC layers are destroyed through that interface before it becomes
     // invalid.
     for (const auto& [token, displayDevice] : mDisplays) {
-        displayDevice->getCompositionDisplay()->setOutputLayersOrderedByZ(
-                compositionengine::Output::OutputLayers());
+        displayDevice->getCompositionDisplay()->clearOutputLayers();
     }
 
     // This DisplayDevice will no longer be relevant once resetDisplayState() is
@@ -1579,7 +1756,7 @@
     mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
     mCompositionEngine->setHwComposer(getFactory().createHWComposer(
             vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName));
-    getHwComposer().registerCallback(this, ++getBE().mComposerSequenceId);
+    mCompositionEngine->getHwComposer().setConfiguration(this, ++getBE().mComposerSequenceId);
 
     LOG_ALWAYS_FATAL_IF(!getHwComposer().getComposer()->isRemote(),
                         "Switched to non-remote hardware composer");
@@ -1615,127 +1792,240 @@
     setTransactionFlags(eDisplayTransactionNeeded);
 }
 
-bool SurfaceFlinger::previousFrameMissed(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
-    ATRACE_CALL();
+sp<Fence> SurfaceFlinger::previousFrameFence() {
     // We are storing the last 2 present fences. If sf's phase offset is to be
     // woken up before the actual vsync but targeting the next vsync, we need to check
     // fence N-2
-    const sp<Fence>& fence =
-            mVsyncModulator.getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
-            ? mPreviousPresentFences[0]
-            : mPreviousPresentFences[1];
+    return mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
+                                                : mPreviousPresentFences[1];
+}
+
+bool SurfaceFlinger::previousFramePending(int graceTimeMs) {
+    ATRACE_CALL();
+    const sp<Fence>& fence = previousFrameFence();
 
     if (fence == Fence::NO_FENCE) {
         return false;
     }
 
-    if (graceTimeMs > 0 && fence->getStatus() == Fence::Status::Unsignaled) {
-        fence->wait(graceTimeMs);
+    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() {
+    const sp<Fence>& fence = previousFrameFence();
+
+    if (fence == Fence::NO_FENCE) {
+        return Fence::SIGNAL_TIME_INVALID;
     }
 
-    return (fence->getStatus() == Fence::Status::Unsignaled);
+    return fence->getSignalTime();
 }
 
-void SurfaceFlinger::populateExpectedPresentTime() NO_THREAD_SAFETY_ANALYSIS {
+nsecs_t SurfaceFlinger::calculateExpectedPresentTime(nsecs_t now) const {
     DisplayStatInfo stats;
     mScheduler->getDisplayStatInfo(&stats);
-    const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime();
+    const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime(now);
     // Inflate the expected present time if we're targetting the next vsync.
-    mExpectedPresentTime =
-            mVsyncModulator.getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
-            ? presentTime
-            : presentTime + stats.vsyncPeriod;
+    return mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod;
 }
 
-void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
+void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) {
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
-            // calculate the expected present time once and use the cached
-            // value throughout this frame to make sure all layers are
-            // seeing this same value.
-            populateExpectedPresentTime();
-
-            // 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;
-            bool frameMissed = previousFrameMissed(graceTimeForPresentFenceMs);
-            bool hwcFrameMissed = mHadDeviceComposition && frameMissed;
-            bool gpuFrameMissed = mHadClientComposition && frameMissed;
-            ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
-            ATRACE_INT("HwcFrameMissed", static_cast<int>(hwcFrameMissed));
-            ATRACE_INT("GpuFrameMissed", static_cast<int>(gpuFrameMissed));
-            if (frameMissed) {
-                mFrameMissedCount++;
-                mTimeStats->incrementMissedFrames();
-            }
-
-            if (hwcFrameMissed) {
-                mHwcFrameMissedCount++;
-            }
-
-            if (gpuFrameMissed) {
-                mGpuFrameMissedCount++;
-            }
-
-            if (mUseSmart90ForVideo) {
-                // This call is made each time SF wakes up and creates a new frame. It is part
-                // of video detection feature.
-                mScheduler->updateFpsBasedOnContent();
-            }
-
-            if (performSetActiveConfig()) {
-                break;
-            }
-
-            if (frameMissed && mPropagateBackpressure) {
-                if ((hwcFrameMissed && !gpuFrameMissed) ||
-                    mPropagateBackpressureClientComposition) {
-                    signalLayerUpdate();
-                    break;
-                }
-            }
-
-            // 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();
-
-            bool refreshNeeded = handleMessageTransaction();
-            refreshNeeded |= handleMessageInvalidate();
-
-            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
-                signalRefresh();
-            }
+            onMessageInvalidate(expectedVSyncTime);
             break;
         }
         case MessageQueue::REFRESH: {
-            handleMessageRefresh();
+            onMessageRefresh();
             break;
         }
     }
 }
 
+void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) {
+    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;
+        ON_MAIN_THREAD(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();
+    nsecs_t jankDurationToUpload = -1;
+    // If we're in a user build then don't push any atoms
+    if (!mIsUserBuild && mMissedFrameJankCount > 0) {
+        const auto display = ON_MAIN_THREAD(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 (display && display->getPowerMode() == hal::PowerMode::ON) {
+            const nsecs_t currentTime = systemTime();
+            const nsecs_t jankDuration = currentTime - mMissedFrameJankStart;
+            if (jankDuration > kMinJankyDuration && jankDuration < kMaxJankyDuration) {
+                jankDurationToUpload = jankDuration;
+            }
+
+            // 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();
+    }
+
+    ON_MAIN_THREAD(performSetActiveConfig());
+
+    updateCursorAsync();
+    updateInputFlinger();
+
+    refreshNeeded |= mRepaintEverything;
+    if (refreshNeeded && CC_LIKELY(mBootStage != BootStage::BOOTLOADER)) {
+        mLastJankDuration = jankDurationToUpload;
+        // 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();
 
     bool flushedATransaction = flushTransactionQueues();
 
-    bool runHandleTransaction = transactionFlags &&
-            ((transactionFlags != eTransactionFlushNeeded) || flushedATransaction);
+    bool runHandleTransaction =
+            (transactionFlags && (transactionFlags != eTransactionFlushNeeded)) ||
+            flushedATransaction ||
+            mForceTraversal;
 
     if (runHandleTransaction) {
         handleTransaction(eTransactionMask);
@@ -1750,50 +2040,102 @@
     return runHandleTransaction;
 }
 
-void SurfaceFlinger::handleMessageRefresh() {
+void SurfaceFlinger::onMessageRefresh() {
     ATRACE_CALL();
 
     mRefreshPending = false;
 
-    const bool repaintEverything = mRepaintEverything.exchange(false);
-    preComposition();
-    rebuildLayerStacks();
-    calculateWorkingSet();
-    long compositionTime = elapsedRealtimeNano();
-    for (const auto& [token, display] : mDisplays) {
-        beginFrame(display);
-        prepareFrame(display);
-        doDebugFlashRegions(display, repaintEverything);
-        doComposition(display, repaintEverything);
+    compositionengine::CompositionRefreshArgs refreshArgs;
+    const auto& displays = ON_MAIN_THREAD(mDisplays);
+    refreshArgs.outputs.reserve(displays.size());
+    for (const auto& [_, display] : displays) {
+        refreshArgs.outputs.push_back(display->getCompositionDisplay());
+    }
+    mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
+        if (auto layerFE = layer->getCompositionEngineLayerFE())
+            refreshArgs.layers.push_back(layerFE);
+    });
+    refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
+    for (sp<Layer> layer : mLayersWithQueuedFrames) {
+        if (auto layerFE = layer->getCompositionEngineLayerFE())
+            refreshArgs.layersWithQueuedFrames.push_back(layerFE);
     }
 
-    logLayerStats();
+    refreshArgs.repaintEverything = mRepaintEverything.exchange(false);
+    refreshArgs.outputColorSetting = useColorManagement
+            ? mDisplayColorSetting
+            : compositionengine::OutputColorSetting::kUnmanaged;
+    refreshArgs.colorSpaceAgnosticDataspace = mColorSpaceAgnosticDataspace;
+    refreshArgs.forceOutputColorMode = mForceColorMode;
+
+    refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
+    refreshArgs.updatingGeometryThisFrame = mGeometryInvalid || mVisibleRegionsDirty;
+    refreshArgs.blursAreExpensive = mBlursAreExpensive;
+    refreshArgs.internalDisplayRotationFlags = DisplayDevice::getPrimaryDisplayRotationFlags();
+
+    if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
+        refreshArgs.colorTransformMatrix = mDrawingState.colorMatrix;
+        mDrawingState.colorMatrixChanged = false;
+    }
+
+    refreshArgs.devOptForceClientComposition = mDebugDisableHWC || mDebugRegion;
+
+    if (mDebugRegion != 0) {
+        refreshArgs.devOptFlashDirtyRegionsDelay =
+                std::chrono::milliseconds(mDebugRegion > 1 ? mDebugRegion : 0);
+    }
+
+    mGeometryInvalid = false;
+
+    // Store the present time just before calling to the composition engine so we could notify
+    // the scheduler.
+    const auto presentTime = systemTime();
+
+    mCompositionEngine->present(refreshArgs);
+    mTimeStats->recordFrameDuration(mFrameStartTime, systemTime());
+    // Reset the frame start time now that we've recorded this frame.
+    mFrameStartTime = 0;
+
+    mScheduler->onDisplayRefreshed(presentTime);
 
     postFrame();
     postComposition();
 
-    mHadClientComposition = false;
-    mHadDeviceComposition = false;
-    for (const auto& [token, displayDevice] : mDisplays) {
-        auto display = displayDevice->getCompositionDisplay();
-        const auto displayId = display->getId();
-        mHadClientComposition =
-                mHadClientComposition || getHwComposer().hasClientComposition(displayId);
-        mHadDeviceComposition =
-                mHadDeviceComposition || getHwComposer().hasDeviceComposition(displayId);
+    const bool prevFrameHadDeviceComposition = mHadDeviceComposition;
+
+    mHadClientComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
+        const auto& state = pair.second->getCompositionDisplay()->getState();
+        return state.usesClientComposition && !state.reusedClientComposition;
+    });
+    mHadDeviceComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
+        const auto& state = pair.second->getCompositionDisplay()->getState();
+        return state.usesDeviceComposition;
+    });
+    mReusedClientComposition =
+            std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) {
+                const auto& state = pair.second->getCompositionDisplay()->getState();
+                return state.reusedClientComposition;
+            });
+
+    // Only report a strategy change if we move in and out of composition with hw overlays
+    if (prevFrameHadDeviceComposition != mHadDeviceComposition) {
+        mTimeStats->incrementCompositionStrategyChanges();
     }
 
-    mVsyncModulator.onRefreshed(mHadClientComposition);
+    mVSyncModulator->onRefreshed(mHadClientComposition);
 
     mLayersWithQueuedFrames.clear();
     if (mVisibleRegionsDirty) {
         mVisibleRegionsDirty = false;
-        if (mTracingEnabled) {
-            mTracing.notify(compositionTime, "visibleRegionsDirty");
+        if (mTracingEnabled && mAddCompositionStateToTrace) {
+            mTracing.notify("visibleRegionsDirty");
         }
     }
-}
 
+    if (mCompositionEngine->needsAnotherUpdate()) {
+        signalLayerUpdate();
+    }
+}
 
 bool SurfaceFlinger::handleMessageInvalidate() {
     ATRACE_CALL();
@@ -1812,174 +2154,6 @@
     return refreshNeeded;
 }
 
-void SurfaceFlinger::calculateWorkingSet() {
-    ATRACE_CALL();
-    ALOGV(__FUNCTION__);
-
-    // build the h/w work list
-    if (CC_UNLIKELY(mGeometryInvalid)) {
-        mGeometryInvalid = false;
-        for (const auto& [token, displayDevice] : mDisplays) {
-            auto display = displayDevice->getCompositionDisplay();
-
-            uint32_t zOrder = 0;
-
-            for (auto& layer : display->getOutputLayersOrderedByZ()) {
-                auto& compositionState = layer->editState();
-                compositionState.forceClientComposition = false;
-                if (!compositionState.hwc || mDebugDisableHWC || mDebugRegion) {
-                    compositionState.forceClientComposition = true;
-                }
-
-                // The output Z order is set here based on a simple counter.
-                compositionState.z = zOrder++;
-
-                // Update the display independent composition state. This goes
-                // to the general composition layer state structure.
-                // TODO: Do this once per compositionengine::CompositionLayer.
-                layer->getLayerFE().latchCompositionState(layer->getLayer().editState().frontEnd,
-                                                          true);
-
-                // Recalculate the geometry state of the output layer.
-                layer->updateCompositionState(true);
-
-                // Write the updated geometry state to the HWC
-                layer->writeStateToHWC(true);
-            }
-        }
-    }
-
-    // Set the per-frame data
-    for (const auto& [token, displayDevice] : mDisplays) {
-        auto display = displayDevice->getCompositionDisplay();
-        const auto displayId = display->getId();
-        if (!displayId) {
-            continue;
-        }
-        auto* profile = display->getDisplayColorProfile();
-
-        if (mDrawingState.colorMatrixChanged) {
-            display->setColorTransform(mDrawingState.colorMatrix);
-        }
-        Dataspace targetDataspace = Dataspace::UNKNOWN;
-        if (useColorManagement) {
-            ColorMode colorMode;
-            RenderIntent renderIntent;
-            pickColorMode(displayDevice, &colorMode, &targetDataspace, &renderIntent);
-            display->setColorMode(colorMode, targetDataspace, renderIntent);
-
-            if (isHdrColorMode(colorMode)) {
-                targetDataspace = Dataspace::UNKNOWN;
-            } else if (mColorSpaceAgnosticDataspace != Dataspace::UNKNOWN) {
-                targetDataspace = mColorSpaceAgnosticDataspace;
-            }
-        }
-
-        for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-            if (layer->isHdrY410()) {
-                layer->forceClientComposition(displayDevice);
-            } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
-                        layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
-                       !profile->hasHDR10Support()) {
-                layer->forceClientComposition(displayDevice);
-            } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG ||
-                        layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) &&
-                       !profile->hasHLGSupport()) {
-                layer->forceClientComposition(displayDevice);
-            }
-
-            if (layer->getRoundedCornerState().radius > 0.0f) {
-                layer->forceClientComposition(displayDevice);
-            }
-
-            if (layer->getForceClientComposition(displayDevice)) {
-                ALOGV("[%s] Requesting Client composition", layer->getName().string());
-                layer->setCompositionType(displayDevice,
-                                          Hwc2::IComposerClient::Composition::CLIENT);
-                continue;
-            }
-
-            const auto& displayState = display->getState();
-            layer->setPerFrameData(displayDevice, displayState.transform, displayState.viewport,
-                                   displayDevice->getSupportedPerFrameMetadata(), targetDataspace);
-        }
-    }
-
-    mDrawingState.colorMatrixChanged = false;
-
-    for (const auto& [token, displayDevice] : mDisplays) {
-        auto display = displayDevice->getCompositionDisplay();
-        for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-            auto& layerState = layer->getCompositionLayer()->editState().frontEnd;
-            layerState.compositionType = static_cast<Hwc2::IComposerClient::Composition>(
-                    layer->getCompositionType(displayDevice));
-        }
-    }
-}
-
-void SurfaceFlinger::doDebugFlashRegions(const sp<DisplayDevice>& displayDevice,
-                                         bool repaintEverything) {
-    auto display = displayDevice->getCompositionDisplay();
-    const auto& displayState = display->getState();
-
-    // is debugging enabled
-    if (CC_LIKELY(!mDebugRegion))
-        return;
-
-    if (displayState.isEnabled) {
-        // transform the dirty region into this screen's coordinate space
-        const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
-        if (!dirtyRegion.isEmpty()) {
-            base::unique_fd readyFence;
-            // redraw the whole screen
-            doComposeSurfaces(displayDevice, dirtyRegion, &readyFence);
-
-            display->getRenderSurface()->queueBuffer(std::move(readyFence));
-        }
-    }
-
-    postFramebuffer(displayDevice);
-
-    if (mDebugRegion > 1) {
-        usleep(mDebugRegion * 1000);
-    }
-
-    prepareFrame(displayDevice);
-}
-
-void SurfaceFlinger::logLayerStats() {
-    ATRACE_CALL();
-    if (CC_UNLIKELY(mLayerStats.isEnabled())) {
-        for (const auto& [token, display] : mDisplays) {
-            if (display->isPrimary()) {
-                mLayerStats.logLayerStats(dumpVisibleLayersProtoInfo(display));
-                return;
-            }
-        }
-
-        ALOGE("logLayerStats: no primary display");
-    }
-}
-
-void SurfaceFlinger::preComposition()
-{
-    ATRACE_CALL();
-    ALOGV("preComposition");
-
-    mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
-
-    bool needExtraInvalidate = false;
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
-        if (layer->onPreComposition(mRefreshStartTime)) {
-            needExtraInvalidate = true;
-        }
-    });
-
-    if (needExtraInvalidate) {
-        signalLayerUpdate();
-    }
-}
-
 void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
                                             std::shared_ptr<FenceTime>& presentFenceTime) {
     // Update queue of past composite+present times and determine the
@@ -2010,11 +2184,12 @@
                                                 nsecs_t compositeToPresentLatency) {
     // Integer division and modulo round toward 0 not -inf, so we need to
     // treat negative and positive offsets differently.
-    nsecs_t idealLatency = (mPhaseOffsets->getCurrentSfOffset() > 0)
-            ? (stats.vsyncPeriod - (mPhaseOffsets->getCurrentSfOffset() % stats.vsyncPeriod))
-            : ((-mPhaseOffsets->getCurrentSfOffset()) % stats.vsyncPeriod);
+    nsecs_t idealLatency = (mPhaseConfiguration->getCurrentOffsets().late.sf > 0)
+            ? (stats.vsyncPeriod -
+               (mPhaseConfiguration->getCurrentOffsets().late.sf % stats.vsyncPeriod))
+            : ((-mPhaseConfiguration->getCurrentOffsets().late.sf) % stats.vsyncPeriod);
 
-    // Just in case mPhaseOffsets->getCurrentSfOffset() == -vsyncInterval.
+    // Just in case mPhaseConfiguration->getCurrentOffsets().late.sf == -vsyncInterval.
     if (idealLatency <= 0) {
         idealLatency = stats.vsyncPeriod;
     }
@@ -2023,8 +2198,8 @@
     // composition and present times, which often have >1ms of jitter.
     // Reducing jitter is important if an app attempts to extrapolate
     // something (such as user input) to an accurate diasplay time.
-    // Snapping also allows an app to precisely calculate mPhaseOffsets->getCurrentSfOffset()
-    // with (presentLatency % interval).
+    // Snapping also allows an app to precisely calculate
+    // mPhaseConfiguration->getCurrentOffsets().late.sf with (presentLatency % interval).
     nsecs_t bias = stats.vsyncPeriod / 2;
     int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
     nsecs_t snappedCompositeToPresentLatency =
@@ -2041,20 +2216,18 @@
     ATRACE_CALL();
     ALOGV("postComposition");
 
-    // Release any buffers which were replaced this frame
     nsecs_t dequeueReadyTime = systemTime();
     for (auto& layer : mLayersWithQueuedFrames) {
         layer->releasePendingBuffer(dequeueReadyTime);
     }
 
-    // |mStateLock| not needed as we are on the main thread
-    const auto displayDevice = getDefaultDisplayDeviceLocked();
+    const auto* display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()).get();
 
     getBE().mGlCompositionDoneTimeline.updateSignalTimes();
     std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
-    if (displayDevice && getHwComposer().hasClientComposition(displayDevice->getId())) {
+    if (display && display->getCompositionDisplay()->getState().usesClientComposition) {
         glCompositionDoneFenceTime =
-                std::make_shared<FenceTime>(displayDevice->getCompositionDisplay()
+                std::make_shared<FenceTime>(display->getCompositionDisplay()
                                                     ->getRenderSurface()
                                                     ->getClientTargetAcquireFence());
         getBE().mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime);
@@ -2064,52 +2237,45 @@
 
     getBE().mDisplayTimeline.updateSignalTimes();
     mPreviousPresentFences[1] = mPreviousPresentFences[0];
-    mPreviousPresentFences[0] = displayDevice
-            ? getHwComposer().getPresentFence(*displayDevice->getId())
-            : Fence::NO_FENCE;
+    mPreviousPresentFences[0] =
+            display ? getHwComposer().getPresentFence(*display->getId()) : Fence::NO_FENCE;
     auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]);
     getBE().mDisplayTimeline.push(presentFenceTime);
 
     DisplayStatInfo stats;
     mScheduler->getDisplayStatInfo(&stats);
 
-    // We use the mRefreshStartTime which might be sampled a little later than
-    // when we started doing work for this frame, but that should be okay
-    // since updateCompositorTiming has snapping logic.
-    updateCompositorTiming(stats, mRefreshStartTime, presentFenceTime);
+    // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
+    // be sampled a little later than when we started doing work for this frame,
+    // but that should be okay since updateCompositorTiming has snapping logic.
+    updateCompositorTiming(stats, mCompositionEngine->getLastFrameRefreshTimestamp(),
+                           presentFenceTime);
     CompositorTiming compositorTiming;
     {
         std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
         compositorTiming = getBE().mCompositorTiming;
     }
 
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
-        bool frameLatched =
-                layer->onPostComposition(displayDevice->getId(), glCompositionDoneFenceTime,
-                                         presentFenceTime, compositorTiming);
+    mDrawingState.traverse([&](Layer* layer) {
+        const bool frameLatched = layer->onPostComposition(display, glCompositionDoneFenceTime,
+                                                           presentFenceTime, compositorTiming);
         if (frameLatched) {
-            recordBufferingStats(layer->getName().string(),
-                    layer->getOccupancyHistory(false));
+            recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
         }
     });
 
     mTransactionCompletedThread.addPresentFence(mPreviousPresentFences[0]);
+    mTransactionCompletedThread.sendCallbacks();
 
-    // Lock the mStateLock in case SurfaceFlinger is in the middle of applying a transaction.
-    // If we do not lock here, a callback could be sent without all of its SurfaceControls and
-    // metrics.
-    {
-        Mutex::Autolock _l(mStateLock);
-        mTransactionCompletedThread.sendCallbacks();
-    }
-
-    if (presentFenceTime->isValid()) {
+    if (display && display->isPrimary() && display->getPowerMode() == hal::PowerMode::ON &&
+        presentFenceTime->isValid()) {
         mScheduler->addPresentFence(presentFenceTime);
     }
 
+    const bool isDisplayConnected = display && getHwComposer().isConnected(*display->getId());
+
     if (!hasSyncFramework) {
-        if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
-            displayDevice->isPoweredOn()) {
+        if (isDisplayConnected && display->isPoweredOn()) {
             mScheduler->enableHardwareVsync();
         }
     }
@@ -2120,11 +2286,10 @@
         if (presentFenceTime->isValid()) {
             mAnimFrameTracker.setActualPresentFence(
                     std::move(presentFenceTime));
-        } else if (displayDevice && getHwComposer().isConnected(*displayDevice->getId())) {
+        } else if (isDisplayConnected) {
             // The HWC doesn't support present fences, so use the refresh
             // timestamp instead.
-            const nsecs_t presentTime =
-                    getHwComposer().getRefreshTimestamp(*displayDevice->getId());
+            const nsecs_t presentTime = getHwComposer().getRefreshTimestamp(*display->getId());
             mAnimFrameTracker.setActualPresentTime(presentTime);
         }
         mAnimFrameTracker.advanceFrame();
@@ -2135,10 +2300,25 @@
         mTimeStats->incrementClientCompositionFrames();
     }
 
+    if (mReusedClientComposition) {
+        mTimeStats->incrementClientCompositionReusedFrames();
+    }
+
     mTimeStats->setPresentFenceGlobal(presentFenceTime);
 
-    if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
-        !displayDevice->isPoweredOn()) {
+    const size_t sfConnections = mScheduler->getEventThreadConnectionCount(mSfConnectionHandle);
+    const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
+    mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
+
+    if (mLastJankDuration > 0) {
+        ATRACE_NAME("Jank detected");
+        const int32_t jankyDurationMillis = mLastJankDuration / (1000 * 1000);
+        android::util::stats_write(android::util::DISPLAY_JANK_REPORTED, jankyDurationMillis,
+                                   mMissedFrameJankCount);
+        mLastJankDuration = -1;
+    }
+
+    if (isDisplayConnected && !display->isPoweredOn()) {
         return;
     }
 
@@ -2157,6 +2337,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) {
@@ -2181,12 +2364,17 @@
     // Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
     // side-effect of getTotalSize(), so we check that again here
     if (ATRACE_ENABLED()) {
+        // getTotalSize returns the total number of buffers that were allocated by SurfaceFlinger
         ATRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize());
     }
 }
 
+FloatRect SurfaceFlinger::getLayerClipBoundsForDisplay(const DisplayDevice& displayDevice) const {
+    return displayDevice.getViewport().toFloatRect();
+}
+
 void SurfaceFlinger::computeLayerBounds() {
-    for (const auto& pair : mDisplays) {
+    for (const auto& pair : ON_MAIN_THREAD(mDisplays)) {
         const auto& displayDevice = pair.second;
         const auto display = displayDevice->getCompositionDisplay();
         for (const auto& layer : mDrawingState.layersSortedByZ) {
@@ -2195,267 +2383,14 @@
                 continue;
             }
 
-            layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform());
+            layer->computeBounds(getLayerClipBoundsForDisplay(*displayDevice), ui::Transform(),
+                                 0.f /* shadowRadius */);
         }
     }
 }
 
-void SurfaceFlinger::rebuildLayerStacks() {
-    ATRACE_CALL();
-    ALOGV("rebuildLayerStacks");
-
-    // rebuild the visible layer list per screen
-    if (CC_UNLIKELY(mVisibleRegionsDirty)) {
-        ATRACE_NAME("rebuildLayerStacks VR Dirty");
-        invalidateHwcGeometry();
-
-        for (const auto& pair : mDisplays) {
-            const auto& displayDevice = pair.second;
-            auto display = displayDevice->getCompositionDisplay();
-            const auto& displayState = display->getState();
-            Region opaqueRegion;
-            Region dirtyRegion;
-            compositionengine::Output::OutputLayers layersSortedByZ;
-            Vector<sp<Layer>> deprecated_layersSortedByZ;
-            Vector<sp<Layer>> layersNeedingFences;
-            const ui::Transform& tr = displayState.transform;
-            const Rect bounds = displayState.bounds;
-            if (displayState.isEnabled) {
-                computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion);
-
-                mDrawingState.traverseInZOrder([&](Layer* layer) {
-                    auto compositionLayer = layer->getCompositionLayer();
-                    if (compositionLayer == nullptr) {
-                        return;
-                    }
-
-                    const auto displayId = displayDevice->getId();
-                    sp<compositionengine::LayerFE> layerFE = compositionLayer->getLayerFE();
-                    LOG_ALWAYS_FATAL_IF(layerFE.get() == nullptr);
-
-                    bool needsOutputLayer = false;
-
-                    if (display->belongsInOutput(layer->getLayerStack(),
-                                                 layer->getPrimaryDisplayOnly())) {
-                        Region drawRegion(tr.transform(
-                                layer->visibleNonTransparentRegion));
-                        drawRegion.andSelf(bounds);
-                        if (!drawRegion.isEmpty()) {
-                            needsOutputLayer = true;
-                        }
-                    }
-
-                    if (needsOutputLayer) {
-                        layersSortedByZ.emplace_back(
-                                display->getOrCreateOutputLayer(displayId, compositionLayer,
-                                                                layerFE));
-                        deprecated_layersSortedByZ.add(layer);
-
-                        auto& outputLayerState = layersSortedByZ.back()->editState();
-                        outputLayerState.visibleRegion =
-                                tr.transform(layer->visibleRegion.intersect(displayState.viewport));
-                    } else if (displayId) {
-                        // For layers that are being removed from a HWC display,
-                        // and that have queued frames, add them to a a list of
-                        // released layers so we can properly set a fence.
-                        bool hasExistingOutputLayer =
-                                display->getOutputLayerForLayer(compositionLayer.get()) != nullptr;
-                        bool hasQueuedFrames = std::find(mLayersWithQueuedFrames.cbegin(),
-                                                         mLayersWithQueuedFrames.cend(),
-                                                         layer) != mLayersWithQueuedFrames.cend();
-
-                        if (hasExistingOutputLayer && hasQueuedFrames) {
-                            layersNeedingFences.add(layer);
-                        }
-                    }
-                });
-            }
-
-            display->setOutputLayersOrderedByZ(std::move(layersSortedByZ));
-
-            displayDevice->setVisibleLayersSortedByZ(deprecated_layersSortedByZ);
-            displayDevice->setLayersNeedingFences(layersNeedingFences);
-
-            Region undefinedRegion{bounds};
-            undefinedRegion.subtractSelf(tr.transform(opaqueRegion));
-
-            display->editState().undefinedRegion = undefinedRegion;
-            display->editState().dirtyRegion.orSelf(dirtyRegion);
-        }
-    }
-}
-
-// Returns a data space that fits all visible layers.  The returned data space
-// can only be one of
-//  - Dataspace::SRGB (use legacy dataspace and let HWC saturate when colors are enhanced)
-//  - Dataspace::DISPLAY_P3
-//  - Dataspace::DISPLAY_BT2020
-// The returned HDR data space is one of
-//  - Dataspace::UNKNOWN
-//  - Dataspace::BT2020_HLG
-//  - Dataspace::BT2020_PQ
-Dataspace SurfaceFlinger::getBestDataspace(const sp<DisplayDevice>& display,
-                                           Dataspace* outHdrDataSpace,
-                                           bool* outIsHdrClientComposition) const {
-    Dataspace bestDataSpace = Dataspace::V0_SRGB;
-    *outHdrDataSpace = Dataspace::UNKNOWN;
-
-    for (const auto& layer : display->getVisibleLayersSortedByZ()) {
-        switch (layer->getDataSpace()) {
-            case Dataspace::V0_SCRGB:
-            case Dataspace::V0_SCRGB_LINEAR:
-            case Dataspace::BT2020:
-            case Dataspace::BT2020_ITU:
-            case Dataspace::BT2020_LINEAR:
-            case Dataspace::DISPLAY_BT2020:
-                bestDataSpace = Dataspace::DISPLAY_BT2020;
-                break;
-            case Dataspace::DISPLAY_P3:
-                bestDataSpace = Dataspace::DISPLAY_P3;
-                break;
-            case Dataspace::BT2020_PQ:
-            case Dataspace::BT2020_ITU_PQ:
-                bestDataSpace = Dataspace::DISPLAY_P3;
-                *outHdrDataSpace = Dataspace::BT2020_PQ;
-                *outIsHdrClientComposition = layer->getForceClientComposition(display);
-                break;
-            case Dataspace::BT2020_HLG:
-            case Dataspace::BT2020_ITU_HLG:
-                bestDataSpace = Dataspace::DISPLAY_P3;
-                // When there's mixed PQ content and HLG content, we set the HDR
-                // data space to be BT2020_PQ and convert HLG to PQ.
-                if (*outHdrDataSpace == Dataspace::UNKNOWN) {
-                    *outHdrDataSpace = Dataspace::BT2020_HLG;
-                }
-                break;
-            default:
-                break;
-        }
-    }
-
-    return bestDataSpace;
-}
-
-// Pick the ColorMode / Dataspace for the display device.
-void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& display, ColorMode* outMode,
-                                   Dataspace* outDataSpace, RenderIntent* outRenderIntent) const {
-    if (mDisplayColorSetting == DisplayColorSetting::UNMANAGED) {
-        *outMode = ColorMode::NATIVE;
-        *outDataSpace = Dataspace::UNKNOWN;
-        *outRenderIntent = RenderIntent::COLORIMETRIC;
-        return;
-    }
-
-    Dataspace hdrDataSpace;
-    bool isHdrClientComposition = false;
-    Dataspace bestDataSpace = getBestDataspace(display, &hdrDataSpace, &isHdrClientComposition);
-
-    auto* profile = display->getCompositionDisplay()->getDisplayColorProfile();
-
-    switch (mForceColorMode) {
-        case ColorMode::SRGB:
-            bestDataSpace = Dataspace::V0_SRGB;
-            break;
-        case ColorMode::DISPLAY_P3:
-            bestDataSpace = Dataspace::DISPLAY_P3;
-            break;
-        default:
-            break;
-    }
-
-    // respect hdrDataSpace only when there is no legacy HDR support
-    const bool isHdr = hdrDataSpace != Dataspace::UNKNOWN &&
-            !profile->hasLegacyHdrSupport(hdrDataSpace) && !isHdrClientComposition;
-    if (isHdr) {
-        bestDataSpace = hdrDataSpace;
-    }
-
-    RenderIntent intent;
-    switch (mDisplayColorSetting) {
-        case DisplayColorSetting::MANAGED:
-        case DisplayColorSetting::UNMANAGED:
-            intent = isHdr ? RenderIntent::TONE_MAP_COLORIMETRIC : RenderIntent::COLORIMETRIC;
-            break;
-        case DisplayColorSetting::ENHANCED:
-            intent = isHdr ? RenderIntent::TONE_MAP_ENHANCE : RenderIntent::ENHANCE;
-            break;
-        default: // vendor display color setting
-            intent = static_cast<RenderIntent>(mDisplayColorSetting);
-            break;
-    }
-
-    profile->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
-}
-
-void SurfaceFlinger::beginFrame(const sp<DisplayDevice>& displayDevice) {
-    auto display = displayDevice->getCompositionDisplay();
-    const auto& displayState = display->getState();
-
-    bool dirty = !display->getDirtyRegion(false).isEmpty();
-    bool empty = displayDevice->getVisibleLayersSortedByZ().size() == 0;
-    bool wasEmpty = !displayState.lastCompositionHadVisibleLayers;
-
-    // If nothing has changed (!dirty), don't recompose.
-    // If something changed, but we don't currently have any visible layers,
-    //   and didn't when we last did a composition, then skip it this time.
-    // The second rule does two things:
-    // - When all layers are removed from a display, we'll emit one black
-    //   frame, then nothing more until we get new layers.
-    // - When a display is created with a private layer stack, we won't
-    //   emit any black frames until a layer is added to the layer stack.
-    bool mustRecompose = dirty && !(empty && wasEmpty);
-
-    const char flagPrefix[] = {'-', '+'};
-    static_cast<void>(flagPrefix);
-    ALOGV_IF(displayDevice->isVirtual(), "%s: %s composition for %s (%cdirty %cempty %cwasEmpty)",
-             __FUNCTION__, mustRecompose ? "doing" : "skipping",
-             displayDevice->getDebugName().c_str(), flagPrefix[dirty], flagPrefix[empty],
-             flagPrefix[wasEmpty]);
-
-    display->getRenderSurface()->beginFrame(mustRecompose);
-
-    if (mustRecompose) {
-        display->editState().lastCompositionHadVisibleLayers = !empty;
-    }
-}
-
-void SurfaceFlinger::prepareFrame(const sp<DisplayDevice>& displayDevice) {
-    auto display = displayDevice->getCompositionDisplay();
-    const auto& displayState = display->getState();
-
-    if (!displayState.isEnabled) {
-        return;
-    }
-
-    status_t result = display->getRenderSurface()->prepareFrame();
-    ALOGE_IF(result != NO_ERROR, "prepareFrame failed for %s: %d (%s)",
-             displayDevice->getDebugName().c_str(), result, strerror(-result));
-}
-
-void SurfaceFlinger::doComposition(const sp<DisplayDevice>& displayDevice, bool repaintEverything) {
-    ATRACE_CALL();
-    ALOGV("doComposition");
-
-    auto display = displayDevice->getCompositionDisplay();
-    const auto& displayState = display->getState();
-
-    if (displayState.isEnabled) {
-        // transform the dirty region into this screen's coordinate space
-        const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
-
-        // repaint the framebuffer (if needed)
-        doDisplayComposition(displayDevice, dirtyRegion);
-
-        display->editState().dirtyRegion.clear();
-        display->getRenderSurface()->flip();
-    }
-    postFramebuffer(displayDevice);
-}
-
-void SurfaceFlinger::postFrame()
-{
-    // |mStateLock| not needed as we are on the main thread
-    const auto display = getDefaultDisplayDeviceLocked();
+void SurfaceFlinger::postFrame() {
+    const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
     if (display && getHwComposer().isConnected(*display->getId())) {
         uint32_t flipCount = display->getPageFlipCount();
         if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
@@ -2464,65 +2399,6 @@
     }
 }
 
-void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& displayDevice) {
-    ATRACE_CALL();
-    ALOGV("postFramebuffer");
-
-    auto display = displayDevice->getCompositionDisplay();
-    const auto& displayState = display->getState();
-    const auto displayId = display->getId();
-
-    if (displayState.isEnabled) {
-        if (displayId) {
-            getHwComposer().presentAndGetReleaseFences(*displayId);
-        }
-        display->getRenderSurface()->onPresentDisplayCompleted();
-        for (auto& layer : display->getOutputLayersOrderedByZ()) {
-            sp<Fence> releaseFence = Fence::NO_FENCE;
-            bool usedClientComposition = true;
-
-            // The layer buffer from the previous frame (if any) is released
-            // by HWC only when the release fence from this frame (if any) is
-            // signaled.  Always get the release fence from HWC first.
-            if (layer->getState().hwc) {
-                const auto& hwcState = *layer->getState().hwc;
-                releaseFence =
-                        getHwComposer().getLayerReleaseFence(*displayId, hwcState.hwcLayer.get());
-                usedClientComposition =
-                        hwcState.hwcCompositionType == Hwc2::IComposerClient::Composition::CLIENT;
-            }
-
-            // If the layer was client composited in the previous frame, we
-            // need to merge with the previous client target acquire fence.
-            // Since we do not track that, always merge with the current
-            // client target acquire fence when it is available, even though
-            // this is suboptimal.
-            if (usedClientComposition) {
-                releaseFence =
-                        Fence::merge("LayerRelease", releaseFence,
-                                     display->getRenderSurface()->getClientTargetAcquireFence());
-            }
-
-            layer->getLayerFE().onLayerDisplayed(releaseFence);
-        }
-
-        // We've got a list of layers needing fences, that are disjoint with
-        // display->getVisibleLayersSortedByZ.  The best we can do is to
-        // supply them with the present fence.
-        if (!displayDevice->getLayersNeedingFences().isEmpty()) {
-            sp<Fence> presentFence =
-                    displayId ? getHwComposer().getPresentFence(*displayId) : Fence::NO_FENCE;
-            for (auto& layer : displayDevice->getLayersNeedingFences()) {
-                layer->getCompositionLayer()->getLayerFE()->onLayerDisplayed(presentFence);
-            }
-        }
-
-        if (displayId) {
-            getHwComposer().clearReleaseFences(*displayId);
-        }
-    }
-}
-
 void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
 {
     ATRACE_CALL();
@@ -2542,7 +2418,7 @@
     // with mStateLock held to guarantee that mCurrentState won't change
     // until the transaction is committed.
 
-    mVsyncModulator.onTransactionHandled();
+    mVSyncModulator->onTransactionHandled();
     transactionFlags = getTransactionFlags(eTransactionMask);
     handleTransactionLocked(transactionFlags);
 
@@ -2560,30 +2436,45 @@
             continue;
         }
 
-        if (event.connection == HWC2::Connection::Connected) {
-            if (!mPhysicalDisplayTokens.count(info->id)) {
-                ALOGV("Creating display %s", to_string(info->id).c_str());
+        const DisplayId displayId = info->id;
+        const auto it = mPhysicalDisplayTokens.find(displayId);
+
+        if (event.connection == hal::Connection::CONNECTED) {
+            if (it == mPhysicalDisplayTokens.end()) {
+                ALOGV("Creating display %s", to_string(displayId).c_str());
+
                 if (event.hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
-                    initScheduler(info->id);
+                    initScheduler(displayId);
                 }
-                mPhysicalDisplayTokens[info->id] = new BBinder();
+
                 DisplayDeviceState state;
-                state.displayId = info->id;
+                state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId),
+                                  event.hwcDisplayId};
                 state.isSecure = true; // All physical displays are currently considered secure.
                 state.displayName = info->name;
-                mCurrentState.displays.add(mPhysicalDisplayTokens[info->id], state);
+
+                sp<IBinder> token = new BBinder();
+                mCurrentState.displays.add(token, state);
+                mPhysicalDisplayTokens.emplace(displayId, std::move(token));
+
                 mInterceptor->saveDisplayCreation(state);
+            } else {
+                ALOGV("Recreating display %s", to_string(displayId).c_str());
+
+                const auto token = it->second;
+                auto& state = mCurrentState.displays.editValueFor(token);
+                state.sequenceId = DisplayDeviceState{}.sequenceId;
             }
         } else {
-            ALOGV("Removing display %s", to_string(info->id).c_str());
+            ALOGV("Removing display %s", to_string(displayId).c_str());
 
-            ssize_t index = mCurrentState.displays.indexOfKey(mPhysicalDisplayTokens[info->id]);
+            const ssize_t index = mCurrentState.displays.indexOfKey(it->second);
             if (index >= 0) {
                 const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
                 mInterceptor->saveDisplayDeletion(state.sequenceId);
                 mCurrentState.displays.removeItemsAt(index);
             }
-            mPhysicalDisplayTokens.erase(info->id);
+            mPhysicalDisplayTokens.erase(it);
         }
 
         processDisplayChangesLocked();
@@ -2593,22 +2484,28 @@
 }
 
 void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) {
-    mScheduler->hotplugReceived(mAppConnectionHandle, displayId, connected);
-    mScheduler->hotplugReceived(mSfConnectionHandle, displayId, connected);
+    mScheduler->onHotplugReceived(mAppConnectionHandle, displayId, connected);
+    mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected);
 }
 
 sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
-        const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
-        const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& dispSurface,
+        const wp<IBinder>& displayToken,
+        std::shared_ptr<compositionengine::Display> compositionDisplay,
+        const DisplayDeviceState& state,
+        const sp<compositionengine::DisplaySurface>& displaySurface,
         const sp<IGraphicBufferProducer>& producer) {
-    DisplayDeviceCreationArgs creationArgs(this, displayToken, displayId);
+    auto displayId = compositionDisplay->getDisplayId();
+    DisplayDeviceCreationArgs creationArgs(this, displayToken, compositionDisplay);
     creationArgs.sequenceId = state.sequenceId;
-    creationArgs.isVirtual = state.isVirtual();
     creationArgs.isSecure = state.isSecure;
-    creationArgs.displaySurface = dispSurface;
+    creationArgs.displaySurface = displaySurface;
     creationArgs.hasWideColorGamut = false;
     creationArgs.supportedPerFrameMetadata = 0;
 
+    if (const auto& physical = state.physical) {
+        creationArgs.connectionType = physical->type;
+    }
+
     const bool isInternalDisplay = displayId && displayId == getInternalDisplayIdLocked();
     creationArgs.isPrimary = isInternalDisplay;
 
@@ -2642,13 +2539,13 @@
         nativeWindow->setSwapInterval(nativeWindow.get(), 0);
     }
 
-    creationArgs.displayInstallOrientation =
-            isInternalDisplay ? primaryDisplayOrientation : DisplayState::eOrientationDefault;
+    creationArgs.physicalOrientation =
+            isInternalDisplay ? internalDisplayOrientation : ui::ROTATION_0;
 
     // virtual displays are always considered enabled
-    creationArgs.initialPowerMode = state.isVirtual() ? HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;
+    creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF;
 
-    sp<DisplayDevice> display = getFactory().createDisplayDevice(std::move(creationArgs));
+    sp<DisplayDevice> display = getFactory().createDisplayDevice(creationArgs);
 
     if (maxFrameBufferAcquiredBuffers >= 3) {
         nativeWindowSurface->preallocateBuffers();
@@ -2660,11 +2557,14 @@
         defaultColorMode = ColorMode::SRGB;
         defaultDataSpace = Dataspace::V0_SRGB;
     }
-    display->getCompositionDisplay()->setColorMode(defaultColorMode, defaultDataSpace,
-                                                   RenderIntent::COLORIMETRIC);
+    display->getCompositionDisplay()->setColorProfile(
+            compositionengine::Output::ColorProfile{defaultColorMode, defaultDataSpace,
+                                                    RenderIntent::COLORIMETRIC,
+                                                    Dataspace::UNKNOWN});
     if (!state.isVirtual()) {
         LOG_ALWAYS_FATAL_IF(!displayId);
-        display->setActiveConfig(getHwComposer().getActiveConfigIndex(*displayId));
+        auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId));
+        display->setActiveConfig(activeConfigId);
     }
 
     display->setLayerStack(state.layerStack);
@@ -2674,6 +2574,149 @@
     return display;
 }
 
+void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken,
+                                         const DisplayDeviceState& state) {
+    int width = 0;
+    int height = 0;
+    ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
+    if (state.physical) {
+        const auto& activeConfig =
+                getCompositionEngine().getHwComposer().getActiveConfig(state.physical->id);
+        width = activeConfig->getWidth();
+        height = activeConfig->getHeight();
+        pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888);
+    } else if (state.surface != nullptr) {
+        int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
+        ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
+        status = state.surface->query(NATIVE_WINDOW_HEIGHT, &height);
+        ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status);
+        int intPixelFormat;
+        status = state.surface->query(NATIVE_WINDOW_FORMAT, &intPixelFormat);
+        ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
+        pixelFormat = static_cast<ui::PixelFormat>(intPixelFormat);
+    } else {
+        // Virtual displays without a surface are dormant:
+        // they have external state (layer stack, projection,
+        // etc.) but no internal state (i.e. a DisplayDevice).
+        return;
+    }
+
+    compositionengine::DisplayCreationArgsBuilder builder;
+    if (const auto& physical = state.physical) {
+        builder.setPhysical({physical->id, physical->type});
+    }
+    builder.setPixels(ui::Size(width, height));
+    builder.setPixelFormat(pixelFormat);
+    builder.setIsSecure(state.isSecure);
+    builder.setLayerStackId(state.layerStack);
+    builder.setPowerAdvisor(&mPowerAdvisor);
+    builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays || getHwComposer().isUsingVrComposer());
+    builder.setName(state.displayName);
+    const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
+
+    sp<compositionengine::DisplaySurface> displaySurface;
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferProducer> bqProducer;
+    sp<IGraphicBufferConsumer> bqConsumer;
+    getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);
+
+    std::optional<DisplayId> displayId = compositionDisplay->getId();
+
+    if (state.isVirtual()) {
+        sp<VirtualDisplaySurface> vds =
+                new VirtualDisplaySurface(getHwComposer(), displayId, state.surface, bqProducer,
+                                          bqConsumer, state.displayName);
+
+        displaySurface = vds;
+        producer = vds;
+    } else {
+        ALOGE_IF(state.surface != nullptr,
+                 "adding a supported display, but rendering "
+                 "surface is provided (%p), ignoring it",
+                 state.surface.get());
+
+        LOG_ALWAYS_FATAL_IF(!displayId);
+        displaySurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer,
+                                                maxGraphicsWidth, maxGraphicsHeight);
+        producer = bqProducer;
+    }
+
+    LOG_FATAL_IF(!displaySurface);
+    const auto display = setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+                                                       displaySurface, producer);
+    mDisplays.emplace(displayToken, display);
+    if (!state.isVirtual()) {
+        LOG_FATAL_IF(!displayId);
+        dispatchDisplayHotplugEvent(displayId->value, true);
+    }
+
+    if (display->isPrimary()) {
+        mScheduler->onPrimaryDisplayAreaChanged(display->getWidth() * display->getHeight());
+    }
+}
+
+void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) {
+    if (const auto display = getDisplayDeviceLocked(displayToken)) {
+        // Save display ID before disconnecting.
+        const auto displayId = display->getId();
+        display->disconnect();
+
+        if (!display->isVirtual()) {
+            LOG_FATAL_IF(!displayId);
+            dispatchDisplayHotplugEvent(displayId->value, false);
+        }
+    }
+
+    mDisplays.erase(displayToken);
+}
+
+void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken,
+                                           const DisplayDeviceState& currentState,
+                                           const DisplayDeviceState& drawingState) {
+    const sp<IBinder> currentBinder = IInterface::asBinder(currentState.surface);
+    const sp<IBinder> drawingBinder = IInterface::asBinder(drawingState.surface);
+    if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) {
+        // changing the surface is like destroying and recreating the DisplayDevice
+        if (const auto display = getDisplayDeviceLocked(displayToken)) {
+            display->disconnect();
+        }
+        mDisplays.erase(displayToken);
+        if (const auto& physical = currentState.physical) {
+            getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id);
+        }
+        processDisplayAdded(displayToken, currentState);
+        if (currentState.physical) {
+            const auto display = getDisplayDeviceLocked(displayToken);
+            setPowerModeInternal(display, hal::PowerMode::ON);
+        }
+        return;
+    }
+
+    if (const auto display = getDisplayDeviceLocked(displayToken)) {
+        if (currentState.layerStack != drawingState.layerStack) {
+            display->setLayerStack(currentState.layerStack);
+        }
+        if ((currentState.orientation != drawingState.orientation) ||
+            (currentState.viewport != drawingState.viewport) ||
+            (currentState.frame != drawingState.frame)) {
+            display->setProjection(currentState.orientation, currentState.viewport,
+                                   currentState.frame);
+        }
+        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());
+            }
+        }
+    }
+}
+
 void SurfaceFlinger::processDisplayChangesLocked() {
     // here we take advantage of Vector's copy-on-write semantics to
     // improve performance by skipping the transaction entirely when
@@ -2682,133 +2725,31 @@
     const KeyedVector<wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
     if (!curr.isIdenticalTo(draw)) {
         mVisibleRegionsDirty = true;
-        const size_t cc = curr.size();
-        size_t dc = draw.size();
 
         // find the displays that were removed
         // (ie: in drawing state but not in current state)
         // also handle displays that changed
         // (ie: displays that are in both lists)
-        for (size_t i = 0; i < dc;) {
-            const ssize_t j = curr.indexOfKey(draw.keyAt(i));
+        for (size_t i = 0; i < draw.size(); i++) {
+            const wp<IBinder>& displayToken = draw.keyAt(i);
+            const ssize_t j = curr.indexOfKey(displayToken);
             if (j < 0) {
                 // in drawing state but not in current state
-                if (const auto display = getDisplayDeviceLocked(draw.keyAt(i))) {
-                    // Save display ID before disconnecting.
-                    const auto displayId = display->getId();
-                    display->disconnect();
-
-                    if (!display->isVirtual()) {
-                        LOG_ALWAYS_FATAL_IF(!displayId);
-                        dispatchDisplayHotplugEvent(displayId->value, false);
-                    }
-                }
-
-                mDisplays.erase(draw.keyAt(i));
+                processDisplayRemoved(displayToken);
             } else {
                 // this display is in both lists. see if something changed.
-                const DisplayDeviceState& state(curr[j]);
-                const wp<IBinder>& displayToken = curr.keyAt(j);
-                const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
-                const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
-                if (state_binder != draw_binder) {
-                    // changing the surface is like destroying and
-                    // recreating the DisplayDevice, so we just remove it
-                    // from the drawing state, so that it get re-added
-                    // below.
-                    if (const auto display = getDisplayDeviceLocked(displayToken)) {
-                        display->disconnect();
-                    }
-                    mDisplays.erase(displayToken);
-                    mDrawingState.displays.removeItemsAt(i);
-                    dc--;
-                    // at this point we must loop to the next item
-                    continue;
-                }
-
-                if (const auto display = getDisplayDeviceLocked(displayToken)) {
-                    if (state.layerStack != draw[i].layerStack) {
-                        display->setLayerStack(state.layerStack);
-                    }
-                    if ((state.orientation != draw[i].orientation) ||
-                        (state.viewport != draw[i].viewport) || (state.frame != draw[i].frame)) {
-                        display->setProjection(state.orientation, state.viewport, state.frame);
-                    }
-                    if (state.width != draw[i].width || state.height != draw[i].height) {
-                        display->setDisplaySize(state.width, state.height);
-                    }
-                }
+                const DisplayDeviceState& currentState = curr[j];
+                const DisplayDeviceState& drawingState = draw[i];
+                processDisplayChanged(displayToken, currentState, drawingState);
             }
-            ++i;
         }
 
         // find displays that were added
         // (ie: in current state but not in drawing state)
-        for (size_t i = 0; i < cc; i++) {
-            if (draw.indexOfKey(curr.keyAt(i)) < 0) {
-                const DisplayDeviceState& state(curr[i]);
-
-                sp<compositionengine::DisplaySurface> dispSurface;
-                sp<IGraphicBufferProducer> producer;
-                sp<IGraphicBufferProducer> bqProducer;
-                sp<IGraphicBufferConsumer> bqConsumer;
-                getFactory().createBufferQueue(&bqProducer, &bqConsumer, false);
-
-                std::optional<DisplayId> displayId;
-                if (state.isVirtual()) {
-                    // Virtual displays without a surface are dormant:
-                    // they have external state (layer stack, projection,
-                    // etc.) but no internal state (i.e. a DisplayDevice).
-                    if (state.surface != nullptr) {
-                        // Allow VR composer to use virtual displays.
-                        if (mUseHwcVirtualDisplays || getHwComposer().isUsingVrComposer()) {
-                            int width = 0;
-                            int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
-                            ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
-                            int height = 0;
-                            status = state.surface->query(NATIVE_WINDOW_HEIGHT, &height);
-                            ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status);
-                            int intFormat = 0;
-                            status = state.surface->query(NATIVE_WINDOW_FORMAT, &intFormat);
-                            ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
-                            auto format = static_cast<ui::PixelFormat>(intFormat);
-
-                            displayId =
-                                    getHwComposer().allocateVirtualDisplay(width, height, &format);
-                        }
-
-                        // TODO: Plumb requested format back up to consumer
-
-                        sp<VirtualDisplaySurface> vds =
-                                new VirtualDisplaySurface(getHwComposer(), displayId, state.surface,
-                                                          bqProducer, bqConsumer,
-                                                          state.displayName);
-
-                        dispSurface = vds;
-                        producer = vds;
-                    }
-                } else {
-                    ALOGE_IF(state.surface != nullptr,
-                             "adding a supported display, but rendering "
-                             "surface is provided (%p), ignoring it",
-                             state.surface.get());
-
-                    displayId = state.displayId;
-                    LOG_ALWAYS_FATAL_IF(!displayId);
-                    dispSurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer);
-                    producer = bqProducer;
-                }
-
-                const wp<IBinder>& displayToken = curr.keyAt(i);
-                if (dispSurface != nullptr) {
-                    mDisplays.emplace(displayToken,
-                                      setupNewDisplayDeviceInternal(displayToken, displayId, state,
-                                                                    dispSurface, producer));
-                    if (!state.isVirtual()) {
-                        LOG_ALWAYS_FATAL_IF(!displayId);
-                        dispatchDisplayHotplugEvent(displayId->value, true);
-                    }
-                }
+        for (size_t i = 0; i < curr.size(); i++) {
+            const wp<IBinder>& displayToken = curr.keyAt(i);
+            if (draw.indexOfKey(displayToken) < 0) {
+                processDisplayAdded(displayToken, curr[i]);
             }
         }
     }
@@ -2818,9 +2759,11 @@
 
 void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
 {
+    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
+
     // Notify all layers of available frames
-    mCurrentState.traverseInZOrder([](Layer* layer) {
-        layer->notifyAvailableFrames();
+    mCurrentState.traverse([expectedPresentTime](Layer* layer) {
+        layer->notifyAvailableFrames(expectedPresentTime);
     });
 
     /*
@@ -2828,8 +2771,9 @@
      * (perform the transaction for each of them if needed)
      */
 
-    if ((transactionFlags & eTraversalNeeded) || mTraversalNeededMainThread) {
-        mCurrentState.traverseInZOrder([&](Layer* layer) {
+    if ((transactionFlags & eTraversalNeeded) || mForceTraversal) {
+        mForceTraversal = false;
+        mCurrentState.traverse([&](Layer* layer) {
             uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
             if (!trFlags) return;
 
@@ -2841,7 +2785,6 @@
                 mInputInfoChanged = true;
             }
         });
-        mTraversalNeededMainThread = false;
     }
 
     /*
@@ -2853,7 +2796,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).
@@ -2865,7 +2808,7 @@
         // display is used to calculate the hint, otherwise we use the
         // default display.
         //
-        // NOTE: we do this here, rather than in rebuildLayerStacks() so that
+        // NOTE: we do this here, rather than when presenting the display so that
         // the hint is set before we acquire a buffer from the surface texture.
         //
         // NOTE: layer transactions have taken place already, so we use their
@@ -2876,7 +2819,7 @@
         sp<const DisplayDevice> hintDisplay;
         uint32_t currentlayerStack = 0;
         bool first = true;
-        mCurrentState.traverseInZOrder([&](Layer* layer) {
+        mCurrentState.traverse([&](Layer* layer) REQUIRES(mStateLock) {
             // NOTE: we rely on the fact that layers are sorted by
             // layerStack first (so we don't have to traverse the list
             // of displays for every layer).
@@ -2914,14 +2857,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
      */
@@ -2973,7 +2915,7 @@
     std::vector<InputWindowInfo> inputHandles;
 
     mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
-        if (layer->hasInput()) {
+        if (layer->needsInputInfo()) {
             // When calculating the screen bounds we ignore the transparent region since it may
             // result in an unwanted offset.
             inputHandles.push_back(layer->fillInputInfo());
@@ -2986,29 +2928,29 @@
 }
 
 void SurfaceFlinger::commitInputWindowCommands() {
-    mInputWindowCommands = mPendingInputWindowCommands;
+    mInputWindowCommands.merge(mPendingInputWindowCommands);
     mPendingInputWindowCommands.clear();
 }
 
-void SurfaceFlinger::updateCursorAsync()
-{
-    for (const auto& [token, display] : mDisplays) {
-        if (!display->getId()) {
-            continue;
-        }
-
-        for (auto& layer : display->getVisibleLayersSortedByZ()) {
-            layer->updateCursorPosition(display);
+void SurfaceFlinger::updateCursorAsync() {
+    compositionengine::CompositionRefreshArgs refreshArgs;
+    for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
+        if (display->getId()) {
+            refreshArgs.outputs.push_back(display->getCompositionDisplay());
         }
     }
+
+    mCompositionEngine->updateCursorAsync(refreshArgs);
 }
 
-void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) {
-    if (layer->hasReadyFrame()) {
-        bool ignored = false;
-        layer->latchBuffer(ignored, systemTime());
-    }
-    layer->releasePendingBuffer(systemTime());
+void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate,
+                                       Scheduler::ConfigEvent event) {
+    // If this is called from the main thread mStateLock must be locked before
+    // Currently the only way to call this function from the main thread is from
+    // Sheduler::chooseRefreshRateForContent
+
+    ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
+    changeRefreshRateLocked(refreshRate, event);
 }
 
 void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) {
@@ -3019,58 +2961,68 @@
         return;
     }
 
-    int currentConfig = getHwComposer().getActiveConfigIndex(primaryDisplayId);
+    auto currentConfig = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(primaryDisplayId));
     mRefreshRateConfigs =
-            std::make_unique<scheduler::RefreshRateConfigs>(refresh_rate_switching(false),
-                                                            getHwComposer().getConfigs(
+            std::make_unique<scheduler::RefreshRateConfigs>(getHwComposer().getConfigs(
                                                                     primaryDisplayId),
                                                             currentConfig);
     mRefreshRateStats =
             std::make_unique<scheduler::RefreshRateStats>(*mRefreshRateConfigs, *mTimeStats,
-                                                          currentConfig, HWC_POWER_MODE_OFF);
+                                                          currentConfig, hal::PowerMode::OFF);
     mRefreshRateStats->setConfigMode(currentConfig);
 
+    mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs);
+
     // start the EventThread
     mScheduler =
             getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
-                                         *mRefreshRateConfigs);
+                                         *mRefreshRateConfigs, *this);
     mAppConnectionHandle =
-            mScheduler->createConnection("app", mVsyncModulator.getOffsets().app,
-                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
+            mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
                                          impl::EventThread::InterceptVSyncsCallback());
     mSfConnectionHandle =
-            mScheduler->createConnection("sf", mVsyncModulator.getOffsets().sf,
-                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
+            mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
                                          [this](nsecs_t timestamp) {
                                              mInterceptor->saveVSyncEvent(timestamp);
                                          });
 
     mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
-    mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(),
-                                           mSfConnectionHandle.get());
+    mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
+                            mPhaseConfiguration->getCurrentOffsets());
 
     mRegionSamplingThread =
             new RegionSamplingThread(*this, *mScheduler,
                                      RegionSamplingThread::EnvironmentTimingTunables());
-
-    mScheduler->setChangeRefreshRateCallback(
-            [this](RefreshRateType type, Scheduler::ConfigEvent event) {
-                Mutex::Autolock lock(mStateLock);
-                setRefreshRateTo(type, event);
-            });
+    // Dispatch a config change request for the primary display on scheduler
+    // initialization, so that the EventThreads always contain a reference to a
+    // prior configuration.
+    //
+    // This is a bit hacky, but this avoids a back-pointer into the main SF
+    // classes from EventThread, and there should be no run-time binder cost
+    // anyway since there are no connected apps at this point.
+    const nsecs_t vsyncPeriod =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod();
+    mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId.value,
+                                              currentConfig, vsyncPeriod);
 }
 
 void SurfaceFlinger::commitTransaction()
 {
+    commitTransactionLocked();
+    mTransactionPending = false;
+    mAnimTransactionPending = false;
+    mTransactionCV.broadcast();
+}
+
+void SurfaceFlinger::commitTransactionLocked() {
     if (!mLayersPendingRemoval.isEmpty()) {
         // Notify removed layers now that they can't be drawn from
         for (const auto& l : mLayersPendingRemoval) {
-            recordBufferingStats(l->getName().string(),
-                    l->getOccupancyHistory(true));
+            recordBufferingStats(l->getName(), l->getOccupancyHistory(true));
 
             // Ensure any buffers set to display on any children are released.
             if (l->isRemovedFromCurrentState()) {
-                latchAndReleaseBuffer(l);
+                l->latchAndReleaseBuffer();
             }
 
             // If the layer has been removed and has no parent, then it will not be reachable
@@ -3087,52 +3039,27 @@
     // we composite should be considered an animation as well.
     mAnimCompositionPending = mAnimTransactionPending;
 
-    withTracingLock([&]() {
-        mDrawingState = mCurrentState;
-        // clear the "changed" flags in current state
-        mCurrentState.colorMatrixChanged = false;
+    mDrawingState = mCurrentState;
+    // clear the "changed" flags in current state
+    mCurrentState.colorMatrixChanged = false;
 
-        mDrawingState.traverseInZOrder([&](Layer* layer) {
-            layer->commitChildList();
+    mDrawingState.traverse([&](Layer* layer) {
+        layer->commitChildList();
 
-            // If the layer can be reached when traversing mDrawingState, then the layer is no
-            // longer offscreen. Remove the layer from the offscreenLayer set.
-            if (mOffscreenLayers.count(layer)) {
-                mOffscreenLayers.erase(layer);
-            }
-        });
-
-        commitOffscreenLayers();
+        // If the layer can be reached when traversing mDrawingState, then the layer is no
+        // longer offscreen. Remove the layer from the offscreenLayer set.
+        if (mOffscreenLayers.count(layer)) {
+            mOffscreenLayers.erase(layer);
+        }
     });
 
-    mTransactionPending = false;
-    mAnimTransactionPending = false;
-    mTransactionCV.broadcast();
-}
-
-void SurfaceFlinger::withTracingLock(std::function<void()> lockedOperation) {
-    if (mTracingEnabledChanged) {
-        mTracingEnabled = mTracing.isEnabled();
-        mTracingEnabledChanged = false;
-    }
-
-    // Synchronize with Tracing thread
-    std::unique_lock<std::mutex> lock;
-    if (mTracingEnabled) {
-        lock = std::unique_lock<std::mutex>(mDrawingStateLock);
-    }
-
-    lockedOperation();
-
-    // Synchronize with Tracing thread
-    if (mTracingEnabled) {
-        lock.unlock();
-    }
+    commitOffscreenLayers();
+    mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
 }
 
 void SurfaceFlinger::commitOffscreenLayers() {
     for (Layer* offscreenLayer : mOffscreenLayers) {
-        offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, [](Layer* layer) {
+        offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
             uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
             if (!trFlags) return;
 
@@ -3142,149 +3069,8 @@
     }
 }
 
-void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displayDevice,
-                                           Region& outDirtyRegion, Region& outOpaqueRegion) {
-    ATRACE_CALL();
-    ALOGV("computeVisibleRegions");
-
-    auto display = displayDevice->getCompositionDisplay();
-
-    Region aboveOpaqueLayers;
-    Region aboveCoveredLayers;
-    Region dirty;
-
-    outDirtyRegion.clear();
-
-    mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
-        // start with the whole surface at its current location
-        const Layer::State& s(layer->getDrawingState());
-
-        // only consider the layers on the given layer stack
-        if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
-            return;
-        }
-
-        /*
-         * opaqueRegion: area of a surface that is fully opaque.
-         */
-        Region opaqueRegion;
-
-        /*
-         * visibleRegion: area of a surface that is visible on screen
-         * and not fully transparent. This is essentially the layer's
-         * footprint minus the opaque regions above it.
-         * Areas covered by a translucent surface are considered visible.
-         */
-        Region visibleRegion;
-
-        /*
-         * coveredRegion: area of a surface that is covered by all
-         * visible regions above it (which includes the translucent areas).
-         */
-        Region coveredRegion;
-
-        /*
-         * transparentRegion: area of a surface that is hinted to be completely
-         * transparent. This is only used to tell when the layer has no visible
-         * non-transparent regions and can be removed from the layer list. It
-         * does not affect the visibleRegion of this layer or any layers
-         * beneath it. The hint may not be correct if apps don't respect the
-         * SurfaceView restrictions (which, sadly, some don't).
-         */
-        Region transparentRegion;
-
-
-        // handle hidden surfaces by setting the visible region to empty
-        if (CC_LIKELY(layer->isVisible())) {
-            const bool translucent = !layer->isOpaque(s);
-            Rect bounds(layer->getScreenBounds());
-
-            visibleRegion.set(bounds);
-            ui::Transform tr = layer->getTransform();
-            if (!visibleRegion.isEmpty()) {
-                // Remove the transparent area from the visible region
-                if (translucent) {
-                    if (tr.preserveRects()) {
-                        // transform the transparent region
-                        transparentRegion = tr.transform(layer->getActiveTransparentRegion(s));
-                    } else {
-                        // transformation too complex, can't do the
-                        // transparent region optimization.
-                        transparentRegion.clear();
-                    }
-                }
-
-                // compute the opaque region
-                const int32_t layerOrientation = tr.getOrientation();
-                if (layer->getAlpha() == 1.0f && !translucent &&
-                        layer->getRoundedCornerState().radius == 0.0f &&
-                        ((layerOrientation & ui::Transform::ROT_INVALID) == false)) {
-                    // the opaque region is the layer's footprint
-                    opaqueRegion = visibleRegion;
-                }
-            }
-        }
-
-        if (visibleRegion.isEmpty()) {
-            layer->clearVisibilityRegions();
-            return;
-        }
-
-        // Clip the covered region to the visible region
-        coveredRegion = aboveCoveredLayers.intersect(visibleRegion);
-
-        // Update aboveCoveredLayers for next (lower) layer
-        aboveCoveredLayers.orSelf(visibleRegion);
-
-        // subtract the opaque region covered by the layers above us
-        visibleRegion.subtractSelf(aboveOpaqueLayers);
-
-        // compute this layer's dirty region
-        if (layer->contentDirty) {
-            // we need to invalidate the whole region
-            dirty = visibleRegion;
-            // as well, as the old visible region
-            dirty.orSelf(layer->visibleRegion);
-            layer->contentDirty = false;
-        } else {
-            /* compute the exposed region:
-             *   the exposed region consists of two components:
-             *   1) what's VISIBLE now and was COVERED before
-             *   2) what's EXPOSED now less what was EXPOSED before
-             *
-             * note that (1) is conservative, we start with the whole
-             * visible region but only keep what used to be covered by
-             * something -- which mean it may have been exposed.
-             *
-             * (2) handles areas that were not covered by anything but got
-             * exposed because of a resize.
-             */
-            const Region newExposed = visibleRegion - coveredRegion;
-            const Region oldVisibleRegion = layer->visibleRegion;
-            const Region oldCoveredRegion = layer->coveredRegion;
-            const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
-            dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed);
-        }
-        dirty.subtractSelf(aboveOpaqueLayers);
-
-        // accumulate to the screen dirty region
-        outDirtyRegion.orSelf(dirty);
-
-        // Update aboveOpaqueLayers for next (lower) layer
-        aboveOpaqueLayers.orSelf(opaqueRegion);
-
-        // Store the visible region in screen space
-        layer->setVisibleRegion(visibleRegion);
-        layer->setCoveredRegion(coveredRegion);
-        layer->setVisibleNonTransparentRegion(
-                visibleRegion.subtract(transparentRegion));
-    });
-
-    outOpaqueRegion = aboveOpaqueLayers;
-}
-
 void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
-    for (const auto& [token, displayDevice] : mDisplays) {
+    for (const auto& [token, displayDevice] : ON_MAIN_THREAD(mDisplays)) {
         auto display = displayDevice->getCompositionDisplay();
         if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
             display->editState().dirtyRegion.orSelf(dirty);
@@ -3303,6 +3089,8 @@
     bool frameQueued = false;
     bool newDataLatched = false;
 
+    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
+
     // Store the set of layers that need updates. This set must not change as
     // buffers are being latched, as this could result in a deadlock.
     // Example: Two producers share the same command stream and:
@@ -3312,10 +3100,9 @@
     // 3.) Layer 1 is latched.
     // Display is now waiting on Layer 1's frame, which is behind layer 0's
     // second frame. But layer 0's second frame could be waiting on display.
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
+    mDrawingState.traverse([&](Layer* layer) {
         if (layer->hasReadyFrame()) {
             frameQueued = true;
-            const nsecs_t expectedPresentTime = getExpectedPresentTime();
             if (layer->shouldPresentNow(expectedPresentTime)) {
                 mLayersWithQueuedFrames.push_back(layer);
             } else {
@@ -3327,13 +3114,21 @@
         }
     });
 
+    // The client can continue submitting buffers for offscreen layers, but they will not
+    // be shown on screen. Therefore, we need to latch and release buffers of offscreen
+    // layers to ensure dequeueBuffer doesn't block indefinitely.
+    for (Layer* offscreenLayer : mOffscreenLayers) {
+        offscreenLayer->traverse(LayerVector::StateSet::Drawing,
+                                         [&](Layer* l) { l->latchAndReleaseBuffer(); });
+    }
+
     if (!mLayersWithQueuedFrames.empty()) {
         // mStateLock is needed for latchBuffer as LayerRejecter::reject()
         // writes to Layer current state. See also b/119481871
         Mutex::Autolock lock(mStateLock);
 
         for (auto& layer : mLayersWithQueuedFrames) {
-            if (layer->latchBuffer(visibleRegions, latchTime)) {
+            if (layer->latchBuffer(visibleRegions, latchTime, expectedPresentTime)) {
                 mLayersPendingRefresh.push_back(layer);
             }
             layer->useSurfaceDamage();
@@ -3358,6 +3153,8 @@
         mBootStage = BootStage::BOOTANIMATION;
     }
 
+    mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
+
     // Only continue with the refresh if there is actually new work to do
     return !mLayersWithQueuedFrames.empty() && newDataLatched;
 }
@@ -3367,227 +3164,17 @@
     mGeometryInvalid = true;
 }
 
-void SurfaceFlinger::doDisplayComposition(const sp<DisplayDevice>& displayDevice,
-                                          const Region& inDirtyRegion) {
-    auto display = displayDevice->getCompositionDisplay();
-    // We only need to actually compose the display if:
-    // 1) It is being handled by hardware composer, which may need this to
-    //    keep its virtual display state machine in sync, or
-    // 2) There is work to be done (the dirty region isn't empty)
-    if (!displayDevice->getId() && inDirtyRegion.isEmpty()) {
-        ALOGV("Skipping display composition");
-        return;
-    }
-
-    ALOGV("doDisplayComposition");
-    base::unique_fd readyFence;
-    if (!doComposeSurfaces(displayDevice, Region::INVALID_REGION, &readyFence)) return;
-
-    // swap buffers (presentation)
-    display->getRenderSurface()->queueBuffer(std::move(readyFence));
-}
-
-bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice,
-                                       const Region& debugRegion, base::unique_fd* readyFence) {
-    ATRACE_CALL();
-    ALOGV("doComposeSurfaces");
-
-    auto display = displayDevice->getCompositionDisplay();
-    const auto& displayState = display->getState();
-    const auto displayId = display->getId();
-    auto& renderEngine = getRenderEngine();
-    const bool supportProtectedContent = renderEngine.supportsProtectedContent();
-
-    const Region bounds(displayState.bounds);
-    const DisplayRenderArea renderArea(displayDevice);
-    const bool hasClientComposition = getHwComposer().hasClientComposition(displayId);
-    const bool hasFlipClientTargetRequest = getHwComposer().hasFlipClientTargetRequest(displayId);
-    ATRACE_INT("hasClientComposition", hasClientComposition);
-
-    bool applyColorMatrix = false;
-
-    renderengine::DisplaySettings clientCompositionDisplay;
-    std::vector<renderengine::LayerSettings> clientCompositionLayers;
-    sp<GraphicBuffer> buf;
-    base::unique_fd fd;
-
-    if (hasClientComposition) {
-        ALOGV("hasClientComposition");
-
-        if (displayDevice->isPrimary() && supportProtectedContent) {
-            bool needsProtected = false;
-            for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-                // If the layer is a protected layer, mark protected context is needed.
-                if (layer->isProtected()) {
-                    needsProtected = true;
-                    break;
-                }
-            }
-            if (needsProtected != renderEngine.isProtected()) {
-                renderEngine.useProtectedContext(needsProtected);
-            }
-            if (needsProtected != display->getRenderSurface()->isProtected() &&
-                needsProtected == renderEngine.isProtected()) {
-                display->getRenderSurface()->setProtected(needsProtected);
-            }
-        }
-
-        buf = display->getRenderSurface()->dequeueBuffer(&fd);
-
-        if (buf == nullptr) {
-            ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
-                  "client composition for this frame",
-                  displayDevice->getDisplayName().c_str());
-            return false;
-        }
-
-        clientCompositionDisplay.physicalDisplay = displayState.scissor;
-        clientCompositionDisplay.clip = displayState.scissor;
-        const ui::Transform& displayTransform = displayState.transform;
-        clientCompositionDisplay.globalTransform = displayTransform.asMatrix4();
-        clientCompositionDisplay.orientation = displayState.orientation;
-
-        const auto* profile = display->getDisplayColorProfile();
-        Dataspace outputDataspace = Dataspace::UNKNOWN;
-        if (profile->hasWideColorGamut()) {
-            outputDataspace = displayState.dataspace;
-        }
-        clientCompositionDisplay.outputDataspace = outputDataspace;
-        clientCompositionDisplay.maxLuminance =
-                profile->getHdrCapabilities().getDesiredMaxLuminance();
-
-        const bool hasDeviceComposition = getHwComposer().hasDeviceComposition(displayId);
-        const bool skipClientColorTransform =
-                getHwComposer()
-                        .hasDisplayCapability(displayId,
-                                              HWC2::DisplayCapability::SkipClientColorTransform);
-
-        // Compute the global color transform matrix.
-        applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
-        if (applyColorMatrix) {
-            clientCompositionDisplay.colorTransform = displayState.colorTransformMat;
-        }
-    } else if (hasFlipClientTargetRequest) {
-        buf = display->getRenderSurface()->dequeueBuffer(&fd);
-
-        if (buf == nullptr) {
-            ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
-                  "client composition for this frame",
-                  displayDevice->getDisplayName().c_str());
-            return false;
-        }
-    }
-
-    /*
-     * and then, render the layers targeted at the framebuffer
-     */
-
-    ALOGV("Rendering client layers");
-    bool firstLayer = true;
-    Region clearRegion = Region::INVALID_REGION;
-    for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-        const Region viewportRegion(displayState.viewport);
-        const Region clip(viewportRegion.intersect(layer->visibleRegion));
-        ALOGV("Layer: %s", layer->getName().string());
-        ALOGV("  Composition type: %s", toString(layer->getCompositionType(displayDevice)).c_str());
-        if (!clip.isEmpty()) {
-            switch (layer->getCompositionType(displayDevice)) {
-                case Hwc2::IComposerClient::Composition::CURSOR:
-                case Hwc2::IComposerClient::Composition::DEVICE:
-                case Hwc2::IComposerClient::Composition::SIDEBAND:
-                case Hwc2::IComposerClient::Composition::SOLID_COLOR: {
-                    LOG_ALWAYS_FATAL_IF(!displayId);
-                    const Layer::State& state(layer->getDrawingState());
-                    if (layer->getClearClientTarget(displayDevice) && !firstLayer &&
-                        layer->isOpaque(state) && (layer->getAlpha() == 1.0f) &&
-                        layer->getRoundedCornerState().radius == 0.0f && hasClientComposition) {
-                        // never clear the very first layer since we're
-                        // guaranteed the FB is already cleared
-                        renderengine::LayerSettings layerSettings;
-                        Region dummyRegion;
-                        bool prepared =
-                                layer->prepareClientLayer(renderArea, clip, dummyRegion,
-                                                          supportProtectedContent, layerSettings);
-
-                        if (prepared) {
-                            layerSettings.source.buffer.buffer = nullptr;
-                            layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
-                            layerSettings.alpha = half(0.0);
-                            layerSettings.disableBlending = true;
-                            clientCompositionLayers.push_back(layerSettings);
-                        }
-                    }
-                    break;
-                }
-                case Hwc2::IComposerClient::Composition::CLIENT: {
-                    renderengine::LayerSettings layerSettings;
-                    bool prepared =
-                            layer->prepareClientLayer(renderArea, clip, clearRegion,
-                                                      supportProtectedContent, layerSettings);
-                    if (prepared) {
-                        clientCompositionLayers.push_back(layerSettings);
-                    }
-                    break;
-                }
-                default:
-                    break;
-            }
-        } else {
-            ALOGV("  Skipping for empty clip");
-        }
-        firstLayer = false;
-    }
-
-    // Perform some cleanup steps if we used client composition.
-    if (hasClientComposition) {
-        clientCompositionDisplay.clearRegion = clearRegion;
-
-        // We boost GPU frequency here because there will be color spaces conversion
-        // and it's expensive. We boost the GPU frequency so that GPU composition can
-        // finish in time. We must reset GPU frequency afterwards, because high frequency
-        // consumes extra battery.
-        const bool expensiveRenderingExpected =
-                clientCompositionDisplay.outputDataspace == Dataspace::DISPLAY_P3;
-        if (expensiveRenderingExpected && displayId) {
-            mPowerAdvisor.setExpensiveRenderingExpected(*displayId, true);
-        }
-        if (!debugRegion.isEmpty()) {
-            Region::const_iterator it = debugRegion.begin();
-            Region::const_iterator end = debugRegion.end();
-            while (it != end) {
-                const Rect& rect = *it++;
-                renderengine::LayerSettings layerSettings;
-                layerSettings.source.buffer.buffer = nullptr;
-                layerSettings.source.solidColor = half3(1.0, 0.0, 1.0);
-                layerSettings.geometry.boundaries = rect.toFloatRect();
-                layerSettings.alpha = half(1.0);
-                clientCompositionLayers.push_back(layerSettings);
-            }
-        }
-        renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers,
-                                buf->getNativeBuffer(), /*useFramebufferCache=*/true, std::move(fd),
-                                readyFence);
-    } else if (displayId) {
-        mPowerAdvisor.setExpensiveRenderingExpected(*displayId, false);
-    }
-    return true;
-}
-
-void SurfaceFlinger::drawWormhole(const Region& region) const {
-    auto& engine(getRenderEngine());
-    engine.fillRegionWithColor(region, 0, 0, 0, 0);
-}
-
 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);
         sp<Layer> parent;
         if (parentHandle != nullptr) {
-            parent = fromHandle(parentHandle);
+            parent = fromHandleLocked(parentHandle).promote();
             if (parent == nullptr) {
                 return NAME_NOT_FOUND;
             }
@@ -3595,9 +3182,9 @@
             parent = parentLayer;
         }
 
-        if (mNumLayers >= MAX_LAYERS) {
-            ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers,
-                  MAX_LAYERS);
+        if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
+            ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
+                  ISurfaceComposer::MAX_LAYERS);
             return NO_MEMORY;
         }
 
@@ -3620,8 +3207,16 @@
                                         mMaxGraphicBufferProducerListSize,
                                 "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
                                 mGraphicBufferProducerList.size(),
-                                mMaxGraphicBufferProducerListSize, mNumLayers);
+                                mMaxGraphicBufferProducerListSize, mNumLayers.load());
         }
+
+        if (const auto display = getDefaultDisplayDeviceLocked()) {
+            lbc->updateTransformHint(display->getTransformHint());
+        }
+        if (outTransformHint) {
+            *outTransformHint = lbc->getTransformHint();
+        }
+
         mLayersAdded = true;
     }
 
@@ -3631,6 +3226,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;
 }
@@ -3640,19 +3242,23 @@
 }
 
 uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) {
-    return setTransactionFlags(flags, Scheduler::TransactionStart::NORMAL);
+    return setTransactionFlags(flags, Scheduler::TransactionStart::Normal);
 }
 
 uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
                                              Scheduler::TransactionStart transactionStart) {
     uint32_t old = mTransactionFlags.fetch_or(flags);
-    mVsyncModulator.setTransactionStart(transactionStart);
+    mVSyncModulator->setTransactionStart(transactionStart);
     if ((old & flags)==0) { // wake the server up
         signalTransaction();
     }
     return old;
 }
 
+void SurfaceFlinger::setTraversalNeeded() {
+    mForceTraversal = true;
+}
+
 bool SurfaceFlinger::flushTransactionQueues() {
     // to prevent onHandleDestroyed from being called while the lock is held,
     // we must keep a copy of the transactions (specifically the composer
@@ -3676,9 +3282,9 @@
                 transactions.push_back(transaction);
                 applyTransactionState(transaction.states, transaction.displays, transaction.flags,
                                       mPendingInputWindowCommands, transaction.desiredPresentTime,
-                                      transaction.buffer, transaction.callback,
-                                      transaction.postTime, transaction.privileged,
-                                      /*isMainThread*/ true);
+                                      transaction.buffer, transaction.postTime,
+                                      transaction.privileged, transaction.hasListenerCallbacks,
+                                      transaction.listenerCallbacks, /*isMainThread*/ true);
                 transactionQueue.pop();
                 flushedATransaction = true;
             }
@@ -3698,31 +3304,11 @@
     return !mTransactionQueues.empty();
 }
 
-bool SurfaceFlinger::containsAnyInvalidClientState(const Vector<ComposerState>& states) {
-    for (const ComposerState& state : states) {
-        // Here we need to check that the interface we're given is indeed
-        // one of our own. A malicious client could give us a nullptr
-        // IInterface, or one of its own or even one of our own but a
-        // different type. All these situations would cause us to crash.
-        if (state.client == nullptr) {
-            return true;
-        }
-
-        sp<IBinder> binder = IInterface::asBinder(state.client);
-        if (binder == nullptr) {
-            return true;
-        }
-
-        if (binder->queryLocalInterface(ISurfaceComposerClient::descriptor) == nullptr) {
-            return true;
-        }
-    }
-    return false;
-}
 
 bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
                                                    const Vector<ComposerState>& states) {
-    nsecs_t expectedPresentTime = getExpectedPresentTime();
+
+    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
     // Do not present if the desiredPresentTime has not passed unless it is more than one second
     // in the future. We ignore timestamps more than 1 second in the future for stability reasons.
     if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime &&
@@ -3742,13 +3328,11 @@
     return true;
 }
 
-void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states,
-                                         const Vector<DisplayState>& displays, uint32_t flags,
-                                         const sp<IBinder>& applyToken,
-                                         const InputWindowCommands& inputWindowCommands,
-                                         int64_t desiredPresentTime,
-                                         const client_cache_t& uncacheBuffer,
-                                         const std::vector<ListenerCallbacks>& listenerCallbacks) {
+void SurfaceFlinger::setTransactionState(
+        const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
+        const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
+        int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+        const std::vector<ListenerCallbacks>& listenerCallbacks) {
     ATRACE_CALL();
 
     const int64_t postTime = systemTime();
@@ -3757,10 +3341,6 @@
 
     Mutex::Autolock _l(mStateLock);
 
-    if (containsAnyInvalidClientState(states)) {
-        return;
-    }
-
     // If its TransactionQueue already has a pending TransactionState or if it is pending
     auto itr = mTransactionQueues.find(applyToken);
     // if this is an animation frame, wait until prior animation frame has
@@ -3777,27 +3357,32 @@
             itr = mTransactionQueues.find(applyToken);
         }
     }
-    if (itr != mTransactionQueues.end() ||
-        !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
+
+    const bool pendingTransactions = itr != mTransactionQueues.end();
+    // Expected present time is computed and cached on invalidate, so it may be stale.
+    if (!pendingTransactions) {
+        mExpectedPresentTime = calculateExpectedPresentTime(systemTime());
+    }
+
+    if (pendingTransactions || !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
         mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
-                                               uncacheBuffer, listenerCallbacks, postTime,
-                                               privileged);
+                                               uncacheBuffer, postTime, privileged,
+                                               hasListenerCallbacks, listenerCallbacks);
         setTransactionFlags(eTransactionFlushNeeded);
         return;
     }
 
     applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
-                          uncacheBuffer, listenerCallbacks, postTime, privileged);
+                          uncacheBuffer, postTime, privileged, hasListenerCallbacks,
+                          listenerCallbacks);
 }
 
-void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states,
-                                           const Vector<DisplayState>& displays, uint32_t flags,
-                                           const InputWindowCommands& inputWindowCommands,
-                                           const int64_t desiredPresentTime,
-                                           const client_cache_t& uncacheBuffer,
-                                           const std::vector<ListenerCallbacks>& listenerCallbacks,
-                                           const int64_t postTime, bool privileged,
-                                           bool isMainThread) {
+void SurfaceFlinger::applyTransactionState(
+        const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
+        const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
+        const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged,
+        bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+        bool isMainThread) {
     uint32_t transactionFlags = 0;
 
     if (flags & eAnimation) {
@@ -3820,24 +3405,33 @@
         transactionFlags |= setDisplayStateLocked(display);
     }
 
-    // In case the client has sent a Transaction that should receive callbacks but without any
-    // SurfaceControls that should be included in the callback, send the listener and callbackIds
-    // to the callback thread so it can send an empty callback
-    if (!listenerCallbacks.empty()) {
-        mTransactionCompletedThread.run();
-    }
-    for (const auto& [listener, callbackIds] : listenerCallbacks) {
-        mTransactionCompletedThread.addCallback(listener, callbackIds);
+    // start and end registration for listeners w/ no surface so they can get their callback.  Note
+    // that listeners with SurfaceControls will start registration during setClientStateLocked
+    // below.
+    for (const auto& listener : listenerCallbacks) {
+        mTransactionCompletedThread.startRegistration(listener);
+        mTransactionCompletedThread.endRegistration(listener);
     }
 
+    std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
     uint32_t clientStateFlags = 0;
     for (const ComposerState& state : states) {
-        clientStateFlags |= setClientStateLocked(state, desiredPresentTime, listenerCallbacks,
-                                                 postTime, privileged);
+        clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged,
+                                                 listenerCallbacksWithSurfaces);
+        if ((flags & eAnimation) && state.state.surface) {
+            if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
+                mScheduler->recordLayerHistory(layer.get(), desiredPresentTime,
+                                               LayerHistory::LayerUpdateType::AnimationTX);
+            }
+        }
+    }
+
+    for (const auto& listenerCallback : listenerCallbacksWithSurfaces) {
+        mTransactionCompletedThread.endRegistration(listenerCallback);
     }
 
     // If the state doesn't require a traversal and there are callbacks, send them now
-    if (!(clientStateFlags & eTraversalNeeded) && !listenerCallbacks.empty()) {
+    if (!(clientStateFlags & eTraversalNeeded) && hasListenerCallbacks) {
         mTransactionCompletedThread.sendCallbacks();
     }
     transactionFlags |= clientStateFlags;
@@ -3862,31 +3456,60 @@
     // so we don't have to wake up again next frame to preform an uneeded traversal.
     if (isMainThread && (transactionFlags & eTraversalNeeded)) {
         transactionFlags = transactionFlags & (~eTraversalNeeded);
-        mTraversalNeededMainThread = true;
+        mForceTraversal = true;
     }
 
+    const auto transactionStart = [](uint32_t flags) {
+        if (flags & eEarlyWakeup) {
+            return Scheduler::TransactionStart::Early;
+        }
+        if (flags & eExplicitEarlyWakeupEnd) {
+            return Scheduler::TransactionStart::EarlyEnd;
+        }
+        if (flags & eExplicitEarlyWakeupStart) {
+            return Scheduler::TransactionStart::EarlyStart;
+        }
+        return Scheduler::TransactionStart::Normal;
+    }(flags);
+
     if (transactionFlags) {
         if (mInterceptor->isEnabled()) {
             mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags);
         }
 
-        // this triggers the transaction
-        const auto start = (flags & eEarlyWakeup) ? Scheduler::TransactionStart::EARLY
-                                                  : 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;
+        // TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag
+        if (flags & eEarlyWakeup) {
+            ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
         }
+
+        if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
+            ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
+            flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
+        }
+
+        // this triggers the transaction
+        setTransactionFlags(transactionFlags, transactionStart);
+
         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
@@ -3902,6 +3525,13 @@
                 break;
             }
         }
+    } else {
+        // even if a transaction is not needed, we need to update VsyncModulator
+        // about explicit early indications
+        if (transactionStart == Scheduler::TransactionStart::EarlyStart ||
+            transactionStart == Scheduler::TransactionStart::EarlyEnd) {
+            mVSyncModulator->setTransactionStart(transactionStart);
+        }
     }
 }
 
@@ -3953,34 +3583,49 @@
     return flags;
 }
 
-bool SurfaceFlinger::callingThreadHasUnscopedSurfaceFlingerAccess() {
+bool SurfaceFlinger::callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermissionCache) {
     IPCThreadState* ipc = IPCThreadState::self();
     const int pid = ipc->getCallingPid();
     const int uid = ipc->getCallingUid();
     if ((uid != AID_GRAPHICS && uid != AID_SYSTEM) &&
-        !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) {
+        (usePermissionCache ? !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)
+                            : !checkPermission(sAccessSurfaceFlinger, pid, uid))) {
         return false;
     }
     return true;
 }
 
 uint32_t SurfaceFlinger::setClientStateLocked(
-        const ComposerState& composerState, int64_t desiredPresentTime,
-        const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
-        bool privileged) {
+        const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
+        bool privileged,
+        std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
     const layer_state_t& s = composerState.state;
-    sp<Client> client(static_cast<Client*>(composerState.client.get()));
 
-    sp<Layer> layer(client->getLayerUser(s.surface));
+    for (auto& listener : s.listeners) {
+        // note that startRegistration will not re-register if the listener has
+        // already be registered for a prior surface control
+        mTransactionCompletedThread.startRegistration(listener);
+        listenerCallbacks.insert(listener);
+    }
+
+    sp<Layer> layer = nullptr;
+    if (s.surface) {
+        layer = fromHandleLocked(s.surface).promote();
+    } else {
+        // The client may provide us a null handle. Treat it as if the layer was removed.
+        ALOGW("Attempt to set client state with a null layer handle");
+    }
     if (layer == nullptr) {
+        for (auto& [listener, callbackIds] : s.listeners) {
+            mTransactionCompletedThread.registerUnpresentedCallbackHandle(
+                    new CallbackHandle(listener, callbackIds, s.surface));
+        }
         return 0;
     }
 
     uint32_t flags = 0;
 
     const uint64_t what = s.what;
-    bool geometryAppliesWithResize =
-            what & layer_state_t::eGeometryAppliesWithResize;
 
     // If we are deferring transaction, make sure to push the pending state, as otherwise the
     // pending state will also be deferred.
@@ -3988,8 +3633,13 @@
         layer->pushPendingState();
     }
 
+    // Only set by BLAST adapter layers
+    if (what & layer_state_t::eProducerDisconnect) {
+        layer->onDisconnect();
+    }
+
     if (what & layer_state_t::ePositionChanged) {
-        if (layer->setPosition(s.x, s.y, !geometryAppliesWithResize)) {
+        if (layer->setPosition(s.x, s.y)) {
             flags |= eTraversalNeeded;
         }
     }
@@ -4080,30 +3730,33 @@
             flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eCropChanged_legacy) {
-        if (layer->setCrop_legacy(s.crop_legacy, !geometryAppliesWithResize))
-            flags |= eTraversalNeeded;
+        if (layer->setCrop_legacy(s.crop_legacy)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eCornerRadiusChanged) {
         if (layer->setCornerRadius(s.cornerRadius))
             flags |= eTraversalNeeded;
     }
+    if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs && mSupportsBlur) {
+        if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
+    }
     if (what & layer_state_t::eLayerStackChanged) {
         ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
         // We only allow setting layer stacks for top level layers,
         // everything else inherits layer stack from its parent.
         if (layer->hasParent()) {
             ALOGE("Attempt to set layer stack on layer with parent (%s) is invalid",
-                    layer->getName().string());
+                  layer->getDebugName());
         } else if (idx < 0) {
             ALOGE("Attempt to set layer stack on layer without parent (%s) that "
-                    "that also does not appear in the top level layer list. Something"
-                    " has gone wrong.", layer->getName().string());
+                  "that also does not appear in the top level layer list. Something"
+                  " has gone wrong.",
+                  layer->getDebugName());
         } else if (layer->setLayerStack(s.layerStack)) {
             mCurrentState.layersSortedByZ.removeAt(idx);
             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) {
@@ -4123,15 +3776,6 @@
         // We don't trigger a traversal here because if no other state is
         // changed, we don't want this to cause any more work
     }
-    if (what & layer_state_t::eReparent) {
-        bool hadParent = layer->hasParent();
-        if (layer->reparent(s.parentHandleForChild)) {
-            if (!hadParent) {
-                mCurrentState.layersSortedByZ.remove(layer);
-            }
-            flags |= eTransactionNeeded|eTraversalNeeded;
-        }
-    }
     if (what & layer_state_t::eReparentChildren) {
         if (layer->reparentChildren(s.reparentHandle)) {
             flags |= eTransactionNeeded|eTraversalNeeded;
@@ -4192,9 +3836,44 @@
             flags |= eTraversalNeeded;
         }
     }
+    if (what & layer_state_t::eShadowRadiusChanged) {
+        if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eFrameRateSelectionPriority) {
+        if (privileged && layer->setFrameRateSelectionPriority(s.frameRateSelectionPriority)) {
+            flags |= eTraversalNeeded;
+        }
+    }
+    if (what & layer_state_t::eFrameRateChanged) {
+        if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility,
+                              "SurfaceFlinger::setClientStateLocked") &&
+            layer->setFrameRate(Layer::FrameRate(s.frameRate,
+                                                 Layer::FrameRate::convertCompatibility(
+                                                         s.frameRateCompatibility)))) {
+            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
+    // lose its relative z order.
+    if (what & layer_state_t::eReparent) {
+        bool hadParent = layer->hasParent();
+        if (layer->reparent(s.parentHandleForChild)) {
+            if (!hadParent) {
+                mCurrentState.layersSortedByZ.remove(layer);
+            }
+            flags |= eTransactionNeeded | eTraversalNeeded;
+        }
+    }
     std::vector<sp<CallbackHandle>> callbackHandles;
-    if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!listenerCallbacks.empty())) {
-        for (const auto& [listener, callbackIds] : listenerCallbacks) {
+    if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!s.listeners.empty())) {
+        for (auto& [listener, callbackIds] : s.listeners) {
             callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
         }
     }
@@ -4219,7 +3898,8 @@
         buffer = s.buffer;
     }
     if (buffer) {
-        if (layer->setBuffer(buffer, postTime, desiredPresentTime, s.cachedBuffer)) {
+        if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime,
+                             s.cachedBuffer)) {
             flags |= eTraversalNeeded;
         }
     }
@@ -4231,10 +3911,6 @@
 
 uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
     uint32_t flags = 0;
-    if (!inputWindowCommands.transferTouchFocusCommands.empty()) {
-        flags |= eTraversalNeeded;
-    }
-
     if (inputWindowCommands.syncInputWindows) {
         flags |= eTraversalNeeded;
     }
@@ -4243,12 +3919,42 @@
     return flags;
 }
 
+status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
+                                     sp<IBinder>* outHandle) {
+    if (!mirrorFromHandle) {
+        return NAME_NOT_FOUND;
+    }
+
+    sp<Layer> mirrorLayer;
+    sp<Layer> mirrorFrom;
+    std::string uniqueName = getUniqueLayerName("MirrorRoot");
+
+    {
+        Mutex::Autolock _l(mStateLock);
+        mirrorFrom = fromHandleLocked(mirrorFromHandle).promote();
+        if (!mirrorFrom) {
+            return NAME_NOT_FOUND;
+        }
+
+        status_t result = createContainerLayer(client, std::move(uniqueName), -1, -1, 0,
+                                               LayerMetadata(), outHandle, &mirrorLayer);
+        if (result != NO_ERROR) {
+            return result;
+        }
+
+        mirrorLayer->mClonedChild = mirrorFrom->createClone();
+    }
+
+    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,
                                      uint32_t h, PixelFormat format, uint32_t flags,
                                      LayerMetadata metadata, sp<IBinder>* handle,
                                      sp<IGraphicBufferProducer>* gbp,
-                                     const sp<IBinder>& parentHandle,
-                                     const sp<Layer>& parentLayer) {
+                                     const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
+                                     uint32_t* outTransformHint) {
     if (int32_t(w|h) < 0) {
         ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
                 int(w), int(h));
@@ -4263,7 +3969,7 @@
 
     sp<Layer> layer;
 
-    String8 uniqueName = getUniqueLayerName(name);
+    std::string uniqueName = getUniqueLayerName(name.string());
 
     bool primaryDisplayOnly = false;
 
@@ -4279,15 +3985,15 @@
 
     switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
         case ISurfaceComposerClient::eFXSurfaceBufferQueue:
-            result = createBufferQueueLayer(client, uniqueName, w, h, flags, std::move(metadata),
-                                            format, handle, gbp, &layer);
+            result = createBufferQueueLayer(client, std::move(uniqueName), w, h, flags,
+                                            std::move(metadata), format, handle, gbp, &layer);
 
             break;
         case ISurfaceComposerClient::eFXSurfaceBufferState:
-            result = createBufferStateLayer(client, uniqueName, w, h, flags, std::move(metadata),
-                                            handle, &layer);
+            result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags,
+                                            std::move(metadata), handle, &layer);
             break;
-        case ISurfaceComposerClient::eFXSurfaceColor:
+        case ISurfaceComposerClient::eFXSurfaceEffect:
             // check if buffer size is set for color layer.
             if (w > 0 || h > 0) {
                 ALOGE("createLayer() failed, w or h cannot be set for color layer (w=%d, h=%d)",
@@ -4295,8 +4001,8 @@
                 return BAD_VALUE;
             }
 
-            result = createColorLayer(client, uniqueName, w, h, flags, std::move(metadata), handle,
-                                      &layer);
+            result = createEffectLayer(client, std::move(uniqueName), w, h, flags,
+                                       std::move(metadata), handle, &layer);
             break;
         case ISurfaceComposerClient::eFXSurfaceContainer:
             // check if buffer size is set for container layer.
@@ -4305,8 +4011,8 @@
                       int(w), int(h));
                 return BAD_VALUE;
             }
-            result = createContainerLayer(client, uniqueName, w, h, flags, std::move(metadata),
-                                          handle, &layer);
+            result = createContainerLayer(client, std::move(uniqueName), w, h, flags,
+                                          std::move(metadata), handle, &layer);
             break;
         default:
             result = BAD_VALUE;
@@ -4323,7 +4029,7 @@
 
     bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess();
     result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer,
-                            addToCurrentState);
+                            addToCurrentState, outTransformHint);
     if (result != NO_ERROR) {
         return result;
     }
@@ -4333,35 +4039,32 @@
     return result;
 }
 
-String8 SurfaceFlinger::getUniqueLayerName(const String8& name)
-{
-    bool matchFound = true;
-    uint32_t dupeCounter = 0;
+std::string SurfaceFlinger::getUniqueLayerName(const char* name) {
+    unsigned dupeCounter = 0;
 
     // Tack on our counter whether there is a hit or not, so everyone gets a tag
-    String8 uniqueName = name + "#" + String8(std::to_string(dupeCounter).c_str());
+    std::string uniqueName = base::StringPrintf("%s#%u", name, dupeCounter);
 
     // Grab the state lock since we're accessing mCurrentState
     Mutex::Autolock lock(mStateLock);
 
     // Loop over layers until we're sure there is no matching name
+    bool matchFound = true;
     while (matchFound) {
         matchFound = false;
-        mCurrentState.traverseInZOrder([&](Layer* layer) {
+        mCurrentState.traverse([&](Layer* layer) {
             if (layer->getName() == uniqueName) {
                 matchFound = true;
-                uniqueName = name + "#" + String8(std::to_string(++dupeCounter).c_str());
+                uniqueName = base::StringPrintf("%s#%u", name, ++dupeCounter);
             }
         });
     }
 
-    ALOGV_IF(dupeCounter > 0, "duplicate layer name: changing %s to %s", name.c_str(),
-             uniqueName.c_str());
-
+    ALOGV_IF(dupeCounter > 0, "duplicate layer name: changing %s to %s", name, uniqueName.c_str());
     return uniqueName;
 }
 
-status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const String8& name,
+status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, std::string name,
                                                 uint32_t w, uint32_t h, uint32_t flags,
                                                 LayerMetadata metadata, PixelFormat& format,
                                                 sp<IBinder>* handle,
@@ -4378,8 +4081,17 @@
         break;
     }
 
-    sp<BufferQueueLayer> layer = getFactory().createBufferQueueLayer(
-            LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
+    sp<BufferQueueLayer> layer;
+    LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
+    args.textureName = getNewTexture();
+    {
+        // Grab the SF state lock during this since it's the only safe way to access
+        // RenderEngine when creating a BufferLayerConsumer
+        // TODO: Check if this lock is still needed here
+        Mutex::Autolock lock(mStateLock);
+        layer = getFactory().createBufferQueueLayer(args);
+    }
+
     status_t err = layer->setDefaultBufferProperties(w, h, format);
     if (err == NO_ERROR) {
         *handle = layer->getHandle();
@@ -4391,38 +4103,38 @@
     return err;
 }
 
-status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, const String8& name,
+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,
                                                 sp<Layer>* outLayer) {
-    sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(
-            LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
+    LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
+    args.textureName = getNewTexture();
+    sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
     *handle = layer->getHandle();
     *outLayer = layer;
 
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::createColorLayer(const sp<Client>& client, const String8& name, uint32_t w,
-                                          uint32_t h, uint32_t flags, LayerMetadata metadata,
-                                          sp<IBinder>* handle, sp<Layer>* outLayer) {
-    *outLayer = getFactory().createColorLayer(
-            LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
+status_t SurfaceFlinger::createEffectLayer(const sp<Client>& client, std::string name, uint32_t w,
+                                           uint32_t h, uint32_t flags, LayerMetadata metadata,
+                                           sp<IBinder>* handle, sp<Layer>* outLayer) {
+    *outLayer = getFactory().createEffectLayer(
+            {this, client, std::move(name), w, h, flags, std::move(metadata)});
     *handle = (*outLayer)->getHandle();
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, const String8& name,
+status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, std::string name,
                                               uint32_t w, uint32_t h, uint32_t flags,
                                               LayerMetadata metadata, sp<IBinder>* handle,
                                               sp<Layer>* outLayer) {
     *outLayer = getFactory().createContainerLayer(
-            LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
+            {this, client, std::move(name), w, h, flags, std::move(metadata)});
     *handle = (*outLayer)->getHandle();
     return NO_ERROR;
 }
 
-
 void SurfaceFlinger::markLayerPendingRemovalLocked(const sp<Layer>& layer) {
     mLayersPendingRemoval.add(layer);
     mLayersRemoved = true;
@@ -4470,15 +4182,16 @@
              DisplayState::eLayerStackChanged;
     d.token = token;
     d.layerStack = 0;
-    d.orientation = DisplayState::eOrientationDefault;
+    d.orientation = ui::ROTATION_0;
     d.frame.makeInvalid();
     d.viewport.makeInvalid();
     d.width = 0;
     d.height = 0;
     displays.add(d);
-    setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, {});
+    setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, false,
+                        {});
 
-    setPowerModeInternal(display, HWC_POWER_MODE_NORMAL);
+    setPowerModeInternal(display, hal::PowerMode::ON);
 
     const nsecs_t vsyncPeriod = getVsyncPeriod();
     mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);
@@ -4491,18 +4204,10 @@
 
 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]() MAIN_THREAD { onInitializeDisplays(); }));
 }
 
-void SurfaceFlinger::setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled) {
-    if (mHWCVsyncState != enabled) {
-        getHwComposer().setVsyncEnabled(displayId, enabled);
-        mHWCVsyncState = enabled;
-    }
-}
-
-void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int mode) {
+void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
     if (display->isVirtual()) {
         ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
         return;
@@ -4513,7 +4218,7 @@
 
     ALOGD("Setting power mode %d on display %s", mode, to_string(*displayId).c_str());
 
-    int currentMode = display->getPowerMode();
+    const hal::PowerMode currentMode = display->getPowerMode();
     if (mode == currentMode) {
         return;
     }
@@ -4521,14 +4226,16 @@
     display->setPowerMode(mode);
 
     if (mInterceptor->isEnabled()) {
-        mInterceptor->savePowerModeUpdate(display->getSequenceId(), mode);
+        mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode));
     }
 
-    if (currentMode == HWC_POWER_MODE_OFF) {
-        // Turn on the display
+    if (currentMode == hal::PowerMode::OFF) {
+        if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
+            ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno));
+        }
         getHwComposer().setPowerMode(*displayId, mode);
-        if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
-            setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
+        if (display->isPrimary() && mode != hal::PowerMode::DOZE_SUSPEND) {
+            getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState);
             mScheduler->onScreenAcquired(mAppConnectionHandle);
             mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
         }
@@ -4536,39 +4243,30 @@
         mVisibleRegionsDirty = true;
         mHasPoweredOff = true;
         repaintEverything();
-
-        struct sched_param param = {0};
-        param.sched_priority = 1;
-        if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
-            ALOGW("Couldn't set SCHED_FIFO on display on");
-        }
-    } else if (mode == HWC_POWER_MODE_OFF) {
+    } else if (mode == hal::PowerMode::OFF) {
         // Turn off the display
-        struct sched_param param = {0};
-        if (sched_setscheduler(0, SCHED_OTHER, &param) != 0) {
-            ALOGW("Couldn't set SCHED_OTHER on display off");
+        if (SurfaceFlinger::setSchedFifo(false) != NO_ERROR) {
+            ALOGW("Couldn't set SCHED_OTHER on display off: %s\n", strerror(errno));
         }
-
-        if (display->isPrimary() && currentMode != HWC_POWER_MODE_DOZE_SUSPEND) {
+        if (display->isPrimary() && currentMode != hal::PowerMode::DOZE_SUSPEND) {
             mScheduler->disableHardwareVsync(true);
             mScheduler->onScreenReleased(mAppConnectionHandle);
         }
 
         // Make sure HWVsync is disabled before turning off the display
-        setVsyncEnabledInHWC(*displayId, HWC2::Vsync::Disable);
+        getHwComposer().setVsyncEnabled(*displayId, hal::Vsync::DISABLE);
 
         getHwComposer().setPowerMode(*displayId, mode);
         mVisibleRegionsDirty = true;
         // from this point on, SF will stop drawing on this display
-    } else if (mode == HWC_POWER_MODE_DOZE ||
-               mode == HWC_POWER_MODE_NORMAL) {
+    } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
         // Update display while dozing
         getHwComposer().setPowerMode(*displayId, mode);
-        if (display->isPrimary() && currentMode == HWC_POWER_MODE_DOZE_SUSPEND) {
+        if (display->isPrimary() && currentMode == hal::PowerMode::DOZE_SUSPEND) {
             mScheduler->onScreenAcquired(mAppConnectionHandle);
             mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
         }
-    } else if (mode == HWC_POWER_MODE_DOZE_SUSPEND) {
+    } else if (mode == hal::PowerMode::DOZE_SUSPEND) {
         // Leave display going to doze
         if (display->isPrimary()) {
             mScheduler->disableHardwareVsync(true);
@@ -4583,30 +4281,27 @@
     if (display->isPrimary()) {
         mTimeStats->setPowerMode(mode);
         mRefreshRateStats->setPowerMode(mode);
-        mScheduler->setDisplayPowerState(mode == HWC_POWER_MODE_NORMAL);
+        mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON);
     }
 
     ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
 }
 
 void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
-    postMessageSync(new LambdaMessage([&]() NO_THREAD_SAFETY_ANALYSIS {
-        const auto display = getDisplayDevice(displayToken);
+    schedule([=]() MAIN_THREAD {
+        const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
             ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
                   displayToken.get());
         } else if (display->isVirtual()) {
             ALOGW("Attempt to set power mode %d for virtual display", mode);
         } else {
-            setPowerModeInternal(display, mode);
+            setPowerModeInternal(display, static_cast<hal::PowerMode>(mode));
         }
-    }));
+    }).wait();
 }
 
-// ---------------------------------------------------------------------------
-
-status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args,
-                                bool asProto) NO_THREAD_SAFETY_ANALYSIS {
+status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) {
     std::string result;
 
     IPCThreadState* ipc = IPCThreadState::self();
@@ -4618,29 +4313,11 @@
         StringAppendF(&result, "Permission Denial: can't dump SurfaceFlinger from pid=%d, uid=%d\n",
                       pid, uid);
     } else {
-        // Try to get the main lock, but give up after one second
-        // (this would indicate SF is stuck, but we want to be able to
-        // print something in dumpsys).
-        status_t err = mStateLock.timedLock(s2ns(1));
-        bool locked = (err == NO_ERROR);
-        if (!locked) {
-            StringAppendF(&result,
-                          "SurfaceFlinger appears to be unresponsive (%s [%d]), dumping anyways "
-                          "(no locks held)\n",
-                          strerror(-err), err);
-        }
-
-        using namespace std::string_literals;
-
         static const std::unordered_map<std::string, Dumper> dumpers = {
-                {"--clear-layer-stats"s, dumper([this](std::string&) { mLayerStats.clear(); })},
-                {"--disable-layer-stats"s, dumper([this](std::string&) { mLayerStats.disable(); })},
                 {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
-                {"--dispsync"s, dumper([this](std::string& s) {
-                         mScheduler->dumpPrimaryDispSync(s);
-                 })},
-                {"--dump-layer-stats"s, dumper([this](std::string& s) { mLayerStats.dump(s); })},
-                {"--enable-layer-stats"s, dumper([this](std::string&) { mLayerStats.enable(); })},
+                {"--dispsync"s,
+                 dumper([this](std::string& s) { mScheduler->getPrimaryDispSync().dump(s); })},
+                {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
                 {"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)},
                 {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
                 {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
@@ -4653,23 +4330,33 @@
 
         const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
 
-        if (const auto it = dumpers.find(flag); it != dumpers.end()) {
-            (it->second)(args, asProto, result);
-        } else if (!asProto) {
-            dumpAllLocked(args, result);
+        bool dumpLayers = true;
+        {
+            TimedLock lock(mStateLock, s2ns(1), __FUNCTION__);
+            if (!lock.locked()) {
+                StringAppendF(&result, "Dumping without lock after timeout: %s (%d)\n",
+                              strerror(-lock.status), lock.status);
+            }
+
+            if (const auto it = dumpers.find(flag); it != dumpers.end()) {
+                (it->second)(args, asProto, result);
+                dumpLayers = false;
+            } else if (!asProto) {
+                dumpAllLocked(args, result);
+            }
         }
 
-        if (locked) {
-            mStateLock.unlock();
-        }
-
-        LayersProto layersProto = dumpProtoFromMainThread();
-        if (asProto) {
-            result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
-        } else {
-            auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
-            result.append(LayerProtoParser::layerTreeToString(layerTree));
-            result.append("\n");
+        if (dumpLayers) {
+            const LayersProto layersProto = dumpProtoFromMainThread();
+            if (asProto) {
+                result.append(layersProto.SerializeAsString());
+            } else {
+                // Dump info that we need to access from the main thread
+                const auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
+                result.append(LayerProtoParser::layerTreeToString(layerTree));
+                result.append("\n");
+                dumpOffscreenLayers(result);
+            }
         }
     }
     write(fd, result.c_str(), result.size());
@@ -4686,7 +4373,7 @@
 
 void SurfaceFlinger::listLayersLocked(std::string& result) const {
     mCurrentState.traverseInZOrder(
-            [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getName().string()); });
+            [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getDebugName()); });
 }
 
 void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const {
@@ -4695,7 +4382,7 @@
     if (args.size() > 1) {
         const auto name = String8(args[1]);
         mCurrentState.traverseInZOrder([&](Layer* layer) {
-            if (name == layer->getName()) {
+            if (layer->getName() == name.string()) {
                 layer->dumpFrameStats(result);
             }
         });
@@ -4705,8 +4392,11 @@
 }
 
 void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) {
-    mCurrentState.traverseInZOrder([&](Layer* layer) {
-        if (args.size() < 2 || String8(args[1]) == layer->getName()) {
+    const bool clearAll = args.size() < 2;
+    const auto name = clearAll ? String8() : String8(args[1]);
+
+    mCurrentState.traverse([&](Layer* layer) {
+        if (clearAll || layer->getName() == name.string()) {
             layer->clearFrameStats();
         }
     });
@@ -4721,11 +4411,11 @@
 // This should only be called from the main thread.  Otherwise it would need
 // the lock and should use mCurrentState rather than mDrawingState.
 void SurfaceFlinger::logFrameStats() {
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
+    mDrawingState.traverse([&](Layer* layer) {
         layer->logFrameStats();
     });
 
-    mAnimFrameTracker.logAndResetStats(String8("<win-anim>"));
+    mAnimFrameTracker.logAndResetStats("<win-anim>");
 }
 
 void SurfaceFlinger::appendSfConfigString(std::string& result) const {
@@ -4744,24 +4434,36 @@
 }
 
 void SurfaceFlinger::dumpVSync(std::string& result) const {
-    mPhaseOffsets->dump(result);
+    mScheduler->dump(result);
+
+    mRefreshRateStats->dump(result);
+    result.append("\n");
+
+    mPhaseConfiguration->dump(result);
     StringAppendF(&result,
-                  "    present offset: %9" PRId64 " ns\t     VSYNC period: %9" PRId64 " ns\n\n",
+                  "      present offset: %9" PRId64 " ns\t     VSYNC period: %9" PRId64 " ns\n\n",
                   dispSyncPresentTimeOffset, getVsyncPeriod());
 
-    StringAppendF(&result, "Scheduler enabled.");
-    StringAppendF(&result, "+  Smart 90 for video detection: %s\n\n",
-                  mUseSmart90ForVideo ? "on" : "off");
-    StringAppendF(&result, "Allowed Display Configs: ");
-    for (int32_t configId : mAllowedDisplayConfigs) {
-        StringAppendF(&result, "%" PRIu32 " Hz, ",
-                      mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps);
-    }
+    scheduler::RefreshRateConfigs::Policy policy = mRefreshRateConfigs->getDisplayManagerPolicy();
+    StringAppendF(&result,
+                  "DesiredDisplayConfigSpecs (DisplayManager): default config ID: %d"
+                  ", 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"
+                      ", 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);
-    StringAppendF(&result, "+  Refresh rate switching: %s\n",
-                  mRefreshRateConfigs->refreshRateSwitchingSupported() ? "on" : "off");
+    mScheduler->getPrimaryDispSync().dump(result);
 }
 
 void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
@@ -4779,8 +4481,8 @@
                   bucketTimeSec, percent);
 }
 
-void SurfaceFlinger::recordBufferingStats(const char* layerName,
-        std::vector<OccupancyTracker::Segment>&& history) {
+void SurfaceFlinger::recordBufferingStats(const std::string& layerName,
+                                          std::vector<OccupancyTracker::Segment>&& history) {
     Mutex::Autolock lock(getBE().mBufferingStatsMutex);
     auto& stats = getBE().mBufferingStats[layerName];
     for (const auto& segment : history) {
@@ -4861,21 +4563,13 @@
         }
 
         if (!isEdid(data)) {
-            result.append("unknown identification data: ");
-            for (uint8_t byte : data) {
-                StringAppendF(&result, "%x ", byte);
-            }
-            result.append("\n");
+            result.append("unknown identification data\n");
             continue;
         }
 
         const auto edid = parseEdid(data);
         if (!edid) {
-            result.append("invalid EDID: ");
-            for (uint8_t byte : data) {
-                StringAppendF(&result, "%x ", byte);
-            }
-            result.append("\n");
+            result.append("invalid EDID\n");
             continue;
         }
 
@@ -4885,6 +4579,18 @@
     }
 }
 
+void SurfaceFlinger::dumpRawDisplayIdentificationData(const DumpArgs& args,
+                                                      std::string& result) const {
+    hal::HWDisplayId hwcDisplayId;
+    uint8_t port;
+    DisplayIdentificationData data;
+
+    if (args.size() > 1 && base::ParseUint(String8(args[1]), &hwcDisplayId) &&
+        getHwComposer().getDisplayIdentificationData(hwcDisplayId, &port, &data)) {
+        result.append(reinterpret_cast<const char*>(data.data()), data.size());
+    }
+}
+
 void SurfaceFlinger::dumpWideColorInfo(std::string& result) const {
     StringAppendF(&result, "Device has wide color built-in display: %d\n", hasWideColorDisplay);
     StringAppendF(&result, "Device uses color management: %d\n", useColorManagement);
@@ -4913,47 +4619,57 @@
 }
 
 LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
+    // If context is SurfaceTracing thread, mTracingLock blocks display transactions on main thread.
+    const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+
     LayersProto layersProto;
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
-        LayerProto* layerProto = layersProto.add_layers();
-        layer->writeToProtoDrawingState(layerProto, traceFlags);
-        layer->writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
-    });
+    for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
+        layer->writeToProto(layersProto, traceFlags, display.get());
+    }
 
     return layersProto;
 }
 
+void SurfaceFlinger::dumpHwc(std::string& result) const {
+    getHwComposer().dump(result);
+}
+
+void SurfaceFlinger::dumpOffscreenLayersProto(LayersProto& layersProto, uint32_t traceFlags) const {
+    // Add a fake invisible root layer to the proto output and parent all the offscreen layers to
+    // it.
+    LayerProto* rootProto = layersProto.add_layers();
+    const int32_t offscreenRootLayerId = INT32_MAX - 2;
+    rootProto->set_id(offscreenRootLayerId);
+    rootProto->set_name("Offscreen Root");
+    rootProto->set_parent(-1);
+
+    for (Layer* offscreenLayer : mOffscreenLayers) {
+        // Add layer as child of the fake root
+        rootProto->add_children(offscreenLayer->sequence);
+
+        // Add layer
+        LayerProto* layerProto =
+                offscreenLayer->writeToProto(layersProto, traceFlags, nullptr /*device*/);
+        layerProto->set_parent(offscreenRootLayerId);
+    }
+}
+
 LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
-    LayersProto layersProto;
-    postMessageSync(new LambdaMessage([&]() { layersProto = dumpDrawingStateProto(traceFlags); }));
-    return layersProto;
+    return schedule([=] { return dumpDrawingStateProto(traceFlags); }).get();
 }
 
-LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(
-        const sp<DisplayDevice>& displayDevice) const {
-    LayersProto layersProto;
-
-    SizeProto* resolution = layersProto.mutable_resolution();
-    resolution->set_w(displayDevice->getWidth());
-    resolution->set_h(displayDevice->getHeight());
-
-    auto display = displayDevice->getCompositionDisplay();
-    const auto& displayState = display->getState();
-
-    layersProto.set_color_mode(decodeColorMode(displayState.colorMode));
-    layersProto.set_color_transform(decodeColorTransform(displayState.colorTransform));
-    layersProto.set_global_transform(displayState.orientation);
-
-    const auto displayId = displayDevice->getId();
-    LOG_ALWAYS_FATAL_IF(!displayId);
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
-        if (!layer->visibleRegion.isEmpty() && !display->getOutputLayersOrderedByZ().empty()) {
-            LayerProto* layerProto = layersProto.add_layers();
-            layer->writeToProtoCompositionState(layerProto, displayDevice);
-        }
-    });
-
-    return layersProto;
+void SurfaceFlinger::dumpOffscreenLayers(std::string& result) {
+    result.append("Offscreen Layers:\n");
+    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 {
@@ -4990,7 +4706,7 @@
     result.append("\n\n");
 
     colorizer.bold(result);
-    result.append("VSYNC configuration:\n");
+    result.append("Scheduler:\n");
     colorizer.reset(result);
     dumpVSync(result);
     result.append("\n");
@@ -5008,7 +4724,7 @@
      * Dump the visible layer list
      */
     colorizer.bold(result);
-    StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers);
+    StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load());
     StringAppendF(&result, "GraphicBufferProducers: %zu, max %zu\n",
                   mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize);
     colorizer.reset(result);
@@ -5016,8 +4732,13 @@
     {
         StringAppendF(&result, "Composition layers\n");
         mDrawingState.traverseInZOrder([&](Layer* layer) {
-            auto compositionLayer = layer->getCompositionLayer();
-            if (compositionLayer) compositionLayer->dump(result);
+            auto* compositionState = layer->getCompositionState();
+            if (!compositionState) return;
+
+            android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
+                                         layer->getDebugName() ? layer->getDebugName()
+                                                               : "<unknown>");
+            compositionState->dump(result);
         });
     }
 
@@ -5034,6 +4755,12 @@
     result.append("\n");
 
     /*
+     * Dump CompositionEngine state
+     */
+
+    mCompositionEngine->dump(result);
+
+    /*
      * Dump SurfaceFlinger global state
      */
 
@@ -5048,8 +4775,8 @@
     if (const auto display = getDefaultDisplayDeviceLocked()) {
         display->getCompositionDisplay()->getState().undefinedRegion.dump(result,
                                                                           "undefinedRegion");
-        StringAppendF(&result, "  orientation=%d, isPoweredOn=%d\n", display->getOrientation(),
-                      display->isPoweredOn());
+        StringAppendF(&result, "  orientation=%s, isPoweredOn=%d\n",
+                      toCString(display->getOrientation()), display->isPoweredOn());
     }
     StringAppendF(&result,
                   "  transaction-flags         : %08x\n"
@@ -5063,8 +4790,8 @@
                       "  refresh-rate              : %f fps\n"
                       "  x-dpi                     : %f\n"
                       "  y-dpi                     : %f\n",
-                      1e9 / activeConfig->getVsyncPeriod(), activeConfig->getDpiX(),
-                      activeConfig->getDpiY());
+                      1e9 / getHwComposer().getDisplayVsyncPeriod(*displayId),
+                      activeConfig->getDpiX(), activeConfig->getDpiY());
     }
 
     StringAppendF(&result, "  transaction time: %f us\n", inTransactionDuration / 1000.0);
@@ -5086,9 +4813,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");
     }
 
@@ -5117,31 +4844,10 @@
         result.append("\n");
     }
 
-    /**
-     * Scheduler dump state.
-     */
-    result.append("\nScheduler state:\n");
-    result.append(mScheduler->doDump() + "\n");
-    StringAppendF(&result, "+  Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off");
-    result.append(mRefreshRateStats->doDump() + "\n");
-
     result.append(mTimeStats->miniDump());
     result.append("\n");
 }
 
-const Vector<sp<Layer>>& SurfaceFlinger::getLayerSortedByZForHwcDisplay(DisplayId displayId) {
-    // Note: mStateLock is held here
-    for (const auto& [token, display] : mDisplays) {
-        if (display->getId() == displayId) {
-            return getDisplayDeviceLocked(token)->getVisibleLayersSortedByZ();
-        }
-    }
-
-    ALOGE("%s: Invalid display %s", __FUNCTION__, to_string(displayId).c_str());
-    static const Vector<sp<Layer>> empty;
-    return empty;
-}
-
 void SurfaceFlinger::updateColorMatrixLocked() {
     mat4 colorMatrix;
     if (mGlobalSaturationFactor != 1.0f) {
@@ -5179,17 +4885,25 @@
         case ENABLE_VSYNC_INJECTIONS:
         case GET_ANIMATION_FRAME_STATS:
         case GET_HDR_CAPABILITIES:
-        case SET_ACTIVE_CONFIG:
-        case SET_ALLOWED_DISPLAY_CONFIGS:
-        case GET_ALLOWED_DISPLAY_CONFIGS:
+        case SET_DESIRED_DISPLAY_CONFIG_SPECS:
+        case GET_DESIRED_DISPLAY_CONFIG_SPECS:
         case SET_ACTIVE_COLOR_MODE:
+        case GET_AUTO_LOW_LATENCY_MODE_SUPPORT:
+        case SET_AUTO_LOW_LATENCY_MODE:
+        case GET_GAME_CONTENT_TYPE_SUPPORT:
+        case SET_GAME_CONTENT_TYPE:
         case INJECT_VSYNC:
         case SET_POWER_MODE:
         case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
         case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
         case GET_DISPLAYED_CONTENT_SAMPLE:
-        case NOTIFY_POWER_HINT: {
-            if (!callingThreadHasUnscopedSurfaceFlingerAccess()) {
+        case NOTIFY_POWER_HINT:
+        case SET_GLOBAL_SHADOW_SETTINGS:
+        case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
+            // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
+            // necessary permission dynamically. Don't use the permission cache for this check.
+            bool usePermissionCache = code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN;
+            if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
                 IPCThreadState* ipc = IPCThreadState::self();
                 ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
                         ipc->getCallingPid(), ipc->getCallingUid());
@@ -5219,7 +4933,9 @@
         case GET_PHYSICAL_DISPLAY_TOKEN:
         case GET_DISPLAY_COLOR_MODES:
         case GET_DISPLAY_NATIVE_PRIMARIES:
+        case GET_DISPLAY_INFO:
         case GET_DISPLAY_CONFIGS:
+        case GET_DISPLAY_STATE:
         case GET_DISPLAY_STATS:
         case GET_SUPPORTED_FRAME_TIMESTAMPS:
         // Calling setTransactionState is safe, because you need to have been
@@ -5230,6 +4946,9 @@
         case GET_COMPOSITION_PREFERENCE:
         case GET_PROTECTED_CONTENT_SUPPORT:
         case IS_WIDE_COLOR_DISPLAY:
+        // setFrameRate() is deliberately available for apps to call without any
+        // special permissions.
+        case SET_FRAME_RATE:
         case GET_DISPLAY_BRIGHTNESS_SUPPORT:
         case SET_DISPLAY_BRIGHTNESS: {
             return OK;
@@ -5249,12 +4968,6 @@
             }
             return OK;
         }
-        // The following codes are deprecated and should never be allowed to access SF.
-        case CONNECT_DISPLAY_UNUSED:
-        case CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED: {
-            ALOGE("Attempting to access SurfaceFlinger with unused code: %u", code);
-            return PERMISSION_DENIED;
-        }
         case CAPTURE_SCREEN_BY_ID: {
             IPCThreadState* ipc = IPCThreadState::self();
             const int uid = ipc->getCallingUid();
@@ -5273,9 +4986,9 @@
         code == IBinder::SYSPROPS_TRANSACTION) {
         return OK;
     }
-    // Numbers from 1000 to 1034 are currently used for backdoors. The code
+    // Numbers from 1000 to 1036 are currently used for backdoors. The code
     // in onTransact verifies that the user is root, and has access to use SF.
-    if (code >= 1000 && code <= 1035) {
+    if (code >= 1000 && code <= 1036) {
         ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
         return OK;
     }
@@ -5410,13 +5123,8 @@
                 updateColorMatrixLocked();
                 return NO_ERROR;
             }
-            // This is an experimental interface
-            // Needs to be shifted to proper binder interface when we productize
-            case 1016: {
-                n = data.readInt32();
-                // TODO(b/113612090): Evaluate if this can be removed.
-                mScheduler->setRefreshSkipCount(n);
-                return NO_ERROR;
+            case 1016: { // Unused.
+                return NAME_NOT_FOUND;
             }
             case 1017: {
                 n = data.readInt32();
@@ -5476,20 +5184,12 @@
                 n = data.readInt32();
                 if (n) {
                     ALOGD("LayerTracing enabled");
-                    Mutex::Autolock lock(mStateLock);
-                    mTracingEnabledChanged = true;
-                    mTracing.enable();
+                    mTracingEnabledChanged = mTracing.enable();
                     reply->writeInt32(NO_ERROR);
                 } else {
                     ALOGD("LayerTracing disabled");
-                    bool writeFile = false;
-                    {
-                        Mutex::Autolock lock(mStateLock);
-                        mTracingEnabledChanged = true;
-                        writeFile = mTracing.disable();
-                    }
-
-                    if (writeFile) {
+                    mTracingEnabledChanged = mTracing.disable();
+                    if (mTracingEnabledChanged) {
                         reply->writeInt32(mTracing.writeToFile());
                     } else {
                         reply->writeInt32(NO_ERROR);
@@ -5510,13 +5210,13 @@
 
                 DisplayColorSetting setting = static_cast<DisplayColorSetting>(data.readInt32());
                 switch (setting) {
-                    case DisplayColorSetting::MANAGED:
+                    case DisplayColorSetting::kManaged:
                         reply->writeBool(useColorManagement);
                         break;
-                    case DisplayColorSetting::UNMANAGED:
+                    case DisplayColorSetting::kUnmanaged:
                         reply->writeBool(true);
                         break;
-                    case DisplayColorSetting::ENHANCED:
+                    case DisplayColorSetting::kEnhanced:
                         reply->writeBool(display->hasRenderIntent(RenderIntent::ENHANCE));
                         break;
                     default: // vendor display color setting
@@ -5592,19 +5292,15 @@
                 return NO_ERROR;
             }
             case 1034: {
-                // TODO(b/129297325): expose this via developer menu option
-                n = data.readInt32();
-                if (n && !mRefreshRateOverlay &&
-                    mRefreshRateConfigs->refreshRateSwitchingSupported()) {
-                    RefreshRateType type;
-                    {
-                        std::lock_guard<std::mutex> lock(mActiveConfigLock);
-                        type = mDesiredActiveConfig.type;
+                switch (n = data.readInt32()) {
+                    case 0:
+                    case 1:
+                        enableRefreshRateOverlay(static_cast<bool>(n));
+                        break;
+                    default: {
+                        Mutex::Autolock lock(mStateLock);
+                        reply->writeBool(mRefreshRateOverlay != nullptr);
                     }
-                    mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
-                    mRefreshRateOverlay->changeRefreshRate(type);
-                } else if (!n) {
-                    mRefreshRateOverlay.reset();
                 }
                 return NO_ERROR;
             }
@@ -5613,7 +5309,7 @@
                 mDebugDisplayConfigSetByBackdoor = false;
                 if (n >= 0) {
                     const auto displayToken = getInternalDisplayToken();
-                    status_t result = setAllowedDisplayConfigs(displayToken, {n});
+                    status_t result = setActiveConfig(displayToken, n);
                     if (result != NO_ERROR) {
                         return result;
                     }
@@ -5621,6 +5317,18 @@
                 }
                 return NO_ERROR;
             }
+            case 1036: {
+                if (data.readInt32() > 0) {
+                    status_t result =
+                            acquireFrameRateFlexibilityToken(&mDebugFrameRateFlexibilityToken);
+                    if (result != NO_ERROR) {
+                        return result;
+                    }
+                } else {
+                    mDebugFrameRateFlexibilityToken = nullptr;
+                }
+                return NO_ERROR;
+            }
         }
     }
     return err;
@@ -5633,9 +5341,65 @@
 
 void SurfaceFlinger::repaintEverythingForHWC() {
     mRepaintEverything = true;
+    mPowerAdvisor.notifyDisplayUpdateImminent();
     mEventQueue->invalidate();
 }
 
+void SurfaceFlinger::kernelTimerChanged(bool expired) {
+    static bool updateOverlay =
+            property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true);
+    if (!updateOverlay) return;
+    if (Mutex::Autolock lock(mStateLock); !mRefreshRateOverlay) return;
+
+    // Update the overlay on the main thread to avoid race conditions with
+    // mRefreshRateConfigs->getCurrentRefreshRate()
+    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 bool timerExpired = mKernelIdleTimerEnabled && expired;
+
+            if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
+                mRefreshRateOverlay->changeRefreshRate(timerExpired ? min : current);
+            }
+            mEventQueue->invalidate();
+        }
+    }));
+}
+
+void SurfaceFlinger::toggleKernelIdleTimer() {
+    using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+
+    // If the support for kernel idle timer is disabled in SF code, don't do anything.
+    if (!mSupportKernelIdleTimer) {
+        return;
+    }
+    const KernelIdleTimerAction action = mRefreshRateConfigs->getIdleTimerAction();
+
+    switch (action) {
+        case KernelIdleTimerAction::TurnOff:
+            if (mKernelIdleTimerEnabled) {
+                ATRACE_INT("KernelIdleTimer", 0);
+                base::SetProperty(KERNEL_IDLE_TIMER_PROP, "false");
+                mKernelIdleTimerEnabled = false;
+            }
+            break;
+        case KernelIdleTimerAction::TurnOn:
+            if (!mKernelIdleTimerEnabled) {
+                ATRACE_INT("KernelIdleTimer", 1);
+                base::SetProperty(KERNEL_IDLE_TIMER_PROP, "true");
+                mKernelIdleTimerEnabled = true;
+            }
+            break;
+        case KernelIdleTimerAction::NoChange:
+            break;
+    }
+}
+
 // A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
 class WindowDisconnector {
 public:
@@ -5651,24 +5415,26 @@
 
 status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken,
                                        sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers,
-                                       const Dataspace reqDataspace,
-                                       const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
-                                       uint32_t reqWidth, uint32_t reqHeight,
-                                       bool useIdentityTransform,
-                                       ISurfaceComposer::Rotation rotation,
-                                       bool captureSecureLayers) {
+                                       Dataspace reqDataspace, ui::PixelFormat reqPixelFormat,
+                                       const Rect& sourceCrop, uint32_t reqWidth,
+                                       uint32_t reqHeight, bool useIdentityTransform,
+                                       ui::Rotation rotation, bool captureSecureLayers) {
     ATRACE_CALL();
 
     if (!displayToken) return BAD_VALUE;
 
-    auto renderAreaRotation = fromSurfaceComposerRotation(rotation);
+    auto renderAreaRotation = ui::Transform::toRotationFlags(rotation);
+    if (renderAreaRotation == ui::Transform::ROT_INVALID) {
+        ALOGE("%s: Invalid rotation: %s", __FUNCTION__, toCString(rotation));
+        renderAreaRotation = ui::Transform::ROT_0;
+    }
 
     sp<DisplayDevice> display;
     {
-        Mutex::Autolock _l(mStateLock);
+        Mutex::Autolock lock(mStateLock);
 
         display = getDisplayDeviceLocked(displayToken);
-        if (!display) return BAD_VALUE;
+        if (!display) return NAME_NOT_FOUND;
 
         // set the requested width/height to the logical display viewport size
         // by default
@@ -5680,7 +5446,6 @@
 
     DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace,
                                  renderAreaRotation, captureSecureLayers);
-
     auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
                                     std::placeholders::_1);
     return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
@@ -5699,15 +5464,39 @@
     }
 }
 
-const sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
+status_t SurfaceFlinger::setSchedFifo(bool enabled) {
+    static constexpr int kFifoPriority = 2;
+    static constexpr int kOtherPriority = 0;
+
+    struct sched_param param = {0};
+    int sched_policy;
+    if (enabled) {
+        sched_policy = SCHED_FIFO;
+        param.sched_priority = kFifoPriority;
+    } else {
+        sched_policy = SCHED_OTHER;
+        param.sched_priority = kOtherPriority;
+    }
+
+    if (sched_setscheduler(0, sched_policy, &param) != 0) {
+        return -errno;
+    }
+    return NO_ERROR;
+}
+
+sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) {
     const sp<IBinder> displayToken = getPhysicalDisplayTokenLocked(DisplayId{displayOrLayerStack});
     if (displayToken) {
         return getDisplayDeviceLocked(displayToken);
     }
     // Couldn't find display by displayId. Try to get display by layerStack since virtual displays
     // may not have a displayId.
+    return getDisplayByLayerStack(displayOrLayerStack);
+}
+
+sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
     for (const auto& [token, display] : mDisplays) {
-        if (display->getLayerStack() == displayOrLayerStack) {
+        if (display->getLayerStack() == layerStack) {
             return display;
         }
     }
@@ -5719,23 +5508,36 @@
     sp<DisplayDevice> display;
     uint32_t width;
     uint32_t height;
-    ui::Transform::orientation_flags captureOrientation;
+    ui::Transform::RotationFlags captureOrientation;
     {
-        Mutex::Autolock _l(mStateLock);
+        Mutex::Autolock lock(mStateLock);
         display = getDisplayByIdOrLayerStack(displayOrLayerStack);
         if (!display) {
-            return BAD_VALUE;
+            return NAME_NOT_FOUND;
         }
 
         width = uint32_t(display->getViewport().width());
         height = uint32_t(display->getViewport().height());
 
-        captureOrientation = fromSurfaceComposerRotation(
-                static_cast<ISurfaceComposer::Rotation>(display->getOrientation()));
-        if (captureOrientation == ui::Transform::orientation_flags::ROT_90) {
-            captureOrientation = ui::Transform::orientation_flags::ROT_270;
-        } else if (captureOrientation == ui::Transform::orientation_flags::ROT_270) {
-            captureOrientation = ui::Transform::orientation_flags::ROT_90;
+        const auto orientation = display->getOrientation();
+        captureOrientation = ui::Transform::toRotationFlags(orientation);
+
+        switch (captureOrientation) {
+            case ui::Transform::ROT_90:
+                captureOrientation = ui::Transform::ROT_270;
+                break;
+
+            case ui::Transform::ROT_270:
+                captureOrientation = ui::Transform::ROT_90;
+                break;
+
+            case ui::Transform::ROT_INVALID:
+                ALOGE("%s: Invalid orientation: %s", __FUNCTION__, toCString(orientation));
+                captureOrientation = ui::Transform::ROT_0;
+                break;
+
+            default:
+                break;
         }
         *outDataspace =
                 pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
@@ -5763,18 +5565,15 @@
     public:
         LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
                         int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
-                        bool childrenOnly)
-              : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace),
+                        bool childrenOnly, const Rect& displayViewport)
+              : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace, displayViewport),
                 mLayer(layer),
                 mCrop(crop),
                 mNeedsFiltering(false),
                 mFlinger(flinger),
                 mChildrenOnly(childrenOnly) {}
         const ui::Transform& getTransform() const override { return mTransform; }
-        Rect getBounds() const override {
-            const Layer::State& layerState(mLayer->getDrawingState());
-            return mLayer->getBufferSize(layerState);
-        }
+        Rect getBounds() const override { return mLayer->getBufferSize(mLayer->getDrawingState()); }
         int getHeight() const override {
             return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
         }
@@ -5783,7 +5582,7 @@
         }
         bool isSecure() const override { return false; }
         bool needsFiltering() const override { return mNeedsFiltering; }
-        const sp<const DisplayDevice> getDisplayDevice() const override { return nullptr; }
+        sp<const DisplayDevice> getDisplayDevice() const override { return nullptr; }
         Rect getSourceCrop() const override {
             if (mCrop.isEmpty()) {
                 return getBounds();
@@ -5800,7 +5599,8 @@
                                const Rect& drawingBounds)
                   : oldParent(oldParent), newParent(newParent) {
                 // Compute and cache the bounds for the new parent layer.
-                newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform());
+                newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
+                                         0.f /* shadowRadius */);
                 oldParent->setChildrenDrawingParent(newParent);
             }
             ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
@@ -5816,11 +5616,14 @@
                 mTransform = mLayer->getTransform().inverse();
                 drawLayers();
             } else {
-                Rect bounds = getBounds();
-                screenshotParentLayer = mFlinger->getFactory().createContainerLayer(
-                        LayerCreationArgs(mFlinger, nullptr, String8("Screenshot Parent"),
-                                          bounds.getWidth(), bounds.getHeight(), 0,
-                                          LayerMetadata()));
+                uint32_t w = static_cast<uint32_t>(getWidth());
+                uint32_t h = static_cast<uint32_t>(getHeight());
+                // In the "childrenOnly" case we reparent the children to a screenshot
+                // layer which has no properties set and which does not draw.
+                sp<ContainerLayer> screenshotParentLayer =
+                        mFlinger->getFactory().createContainerLayer({mFlinger, nullptr,
+                                                                     "Screenshot Parent"s, w, h, 0,
+                                                                     LayerMetadata()});
 
                 ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
                 drawLayers();
@@ -5831,9 +5634,6 @@
         const sp<Layer> mLayer;
         const Rect mCrop;
 
-        // In the "childrenOnly" case we reparent the children to a screenshot
-        // layer which has no properties set and which does not draw.
-        sp<ContainerLayer> screenshotParentLayer;
         ui::Transform mTransform;
         bool mNeedsFiltering;
 
@@ -5846,11 +5646,11 @@
     sp<Layer> parent;
     Rect crop(sourceCrop);
     std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
-
+    Rect displayViewport;
     {
-        Mutex::Autolock _l(mStateLock);
+        Mutex::Autolock lock(mStateLock);
 
-        parent = fromHandle(layerHandleBinder);
+        parent = fromHandleLocked(layerHandleBinder).promote();
         if (parent == nullptr || parent->isRemovedFromCurrentState()) {
             ALOGE("captureLayers called with an invalid or removed parent");
             return NAME_NOT_FOUND;
@@ -5863,20 +5663,27 @@
             return PERMISSION_DENIED;
         }
 
+        Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getCurrentState());
         if (sourceCrop.width() <= 0) {
             crop.left = 0;
-            crop.right = parent->getBufferSize(parent->getCurrentState()).getWidth();
+            crop.right = parentSourceBounds.getWidth();
         }
 
         if (sourceCrop.height() <= 0) {
             crop.top = 0;
-            crop.bottom = parent->getBufferSize(parent->getCurrentState()).getHeight();
+            crop.bottom = parentSourceBounds.getHeight();
+        }
+
+        if (crop.isEmpty() || frameScale <= 0.0f) {
+            // Error out if the layer has no source bounds (i.e. they are boundless) and a source
+            // crop was not specified, or an invalid frame scale was provided.
+            return BAD_VALUE;
         }
         reqWidth = crop.width() * frameScale;
         reqHeight = crop.height() * frameScale;
 
         for (const auto& handle : excludeHandles) {
-            sp<Layer> excludeLayer = fromHandle(handle);
+            sp<Layer> excludeLayer = fromHandleLocked(handle).promote();
             if (excludeLayer != nullptr) {
                 excludeLayers.emplace(excludeLayer);
             } else {
@@ -5884,6 +5691,13 @@
                 return NAME_NOT_FOUND;
             }
         }
+
+        const auto display = getDisplayByLayerStack(parent->getLayerStack());
+        if (!display) {
+            return NAME_NOT_FOUND;
+        }
+
+        displayViewport = display->getViewport();
     } // mStateLock
 
     // really small crop or frameScale
@@ -5894,7 +5708,8 @@
         reqHeight = 1;
     }
 
-    LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly);
+    LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly,
+                               displayViewport);
     auto traverseLayers = [parent, childrenOnly,
                            &excludeLayers](const LayerVector::Visitor& visitor) {
         parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
@@ -5938,68 +5753,41 @@
                                              usage, "screenshot");
 
     return captureScreenCommon(renderArea, traverseLayers, *outBuffer, useIdentityTransform,
-                               outCapturedSecureLayers);
+                               false /* regionSampling */, outCapturedSecureLayers);
 }
 
 status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea,
                                              TraverseLayersFunction traverseLayers,
                                              const sp<GraphicBuffer>& buffer,
-                                             bool useIdentityTransform,
+                                             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,
-                                                 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);
@@ -6012,111 +5800,108 @@
 void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
                                             TraverseLayersFunction traverseLayers,
                                             ANativeWindowBuffer* buffer, bool useIdentityTransform,
-                                            int* outSyncFd) {
+                                            bool regionSampling, int* outSyncFd) {
     ATRACE_CALL();
 
     const auto reqWidth = renderArea.getReqWidth();
     const auto reqHeight = renderArea.getReqHeight();
-    const auto rotation = renderArea.getRotationFlags();
-    const auto transform = renderArea.getTransform();
     const auto sourceCrop = renderArea.getSourceCrop();
+    const auto transform = renderArea.getTransform();
+    const auto rotation = renderArea.getRotationFlags();
+    const auto& displayViewport = renderArea.getDisplayViewport();
 
     renderengine::DisplaySettings clientCompositionDisplay;
-    std::vector<renderengine::LayerSettings> clientCompositionLayers;
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompositionLayers;
 
     // assume that bounds are never offset, and that they are the same as the
     // buffer bounds.
     clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight);
     clientCompositionDisplay.clip = sourceCrop;
-    clientCompositionDisplay.globalTransform = transform.asMatrix4();
-
-    // Now take into account the rotation flag. We append a transform that
-    // rotates the layer stack about the origin, then translate by buffer
-    // boundaries to be in the right quadrant.
-    mat4 rotMatrix;
-    int displacementX = 0;
-    int displacementY = 0;
-    float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
-    switch (rotation) {
-        case ui::Transform::ROT_90:
-            rotMatrix = mat4::rotate(rot90InRadians, vec3(0, 0, 1));
-            displacementX = renderArea.getBounds().getHeight();
-            break;
-        case ui::Transform::ROT_180:
-            rotMatrix = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
-            displacementY = renderArea.getBounds().getWidth();
-            displacementX = renderArea.getBounds().getHeight();
-            break;
-        case ui::Transform::ROT_270:
-            rotMatrix = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
-            displacementY = renderArea.getBounds().getWidth();
-            break;
-        default:
-            break;
-    }
-
-    // We need to transform the clipping window into the right spot.
-    // First, rotate the clipping rectangle by the rotation hint to get the
-    // right orientation
-    const vec4 clipTL = vec4(sourceCrop.left, sourceCrop.top, 0, 1);
-    const vec4 clipBR = vec4(sourceCrop.right, sourceCrop.bottom, 0, 1);
-    const vec4 rotClipTL = rotMatrix * clipTL;
-    const vec4 rotClipBR = rotMatrix * clipBR;
-    const int newClipLeft = std::min(rotClipTL[0], rotClipBR[0]);
-    const int newClipTop = std::min(rotClipTL[1], rotClipBR[1]);
-    const int newClipRight = std::max(rotClipTL[0], rotClipBR[0]);
-    const int newClipBottom = std::max(rotClipTL[1], rotClipBR[1]);
-
-    // Now reposition the clipping rectangle with the displacement vector
-    // computed above.
-    const mat4 displacementMat = mat4::translate(vec4(displacementX, displacementY, 0, 1));
-    clientCompositionDisplay.clip =
-            Rect(newClipLeft + displacementX, newClipTop + displacementY,
-                 newClipRight + displacementX, newClipBottom + displacementY);
-
-    mat4 clipTransform = displacementMat * rotMatrix;
-    clientCompositionDisplay.globalTransform =
-            clipTransform * clientCompositionDisplay.globalTransform;
+    clientCompositionDisplay.orientation = rotation;
 
     clientCompositionDisplay.outputDataspace = renderArea.getReqDataSpace();
     clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance;
 
     const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill());
 
-    renderengine::LayerSettings fillLayer;
+    compositionengine::LayerFE::LayerSettings fillLayer;
     fillLayer.source.buffer.buffer = nullptr;
     fillLayer.source.solidColor = half3(0.0, 0.0, 0.0);
-    fillLayer.geometry.boundaries = FloatRect(0.0, 0.0, 1.0, 1.0);
+    fillLayer.geometry.boundaries =
+            FloatRect(sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom);
     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) {
-        renderengine::LayerSettings layerSettings;
-        bool prepared = layer->prepareClientLayer(renderArea, useIdentityTransform, clearRegion,
-                                                  false, layerSettings);
-        if (prepared) {
-            clientCompositionLayers.push_back(layerSettings);
+        const bool supportProtectedContent = false;
+        Region clip(renderArea.getBounds());
+        compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
+                clip,
+                useIdentityTransform,
+                layer->needsFilteringForScreenshots(display.get(), transform) ||
+                        renderArea.needsFiltering(),
+                renderArea.isSecure(),
+                supportProtectedContent,
+                clearRegion,
+                displayViewport,
+                clientCompositionDisplay.outputDataspace,
+                true,  /* realContentIsVisible */
+                false, /* clearContent */
+        };
+        std::vector<compositionengine::LayerFE::LayerSettings> results =
+                layer->prepareClientCompositionList(targetSettings);
+        if (results.size() > 0) {
+            for (auto& settings : results) {
+                settings.geometry.positionTransform =
+                        transform.asMatrix4() * settings.geometry.positionTransform;
+                // There's no need to process blurs when we're executing region sampling,
+                // we're just trying to understand what we're drawing, and doing so without
+                // blurs is already a pretty good approximation.
+                if (regionSampling) {
+                    settings.backgroundBlurRadius = 0;
+                }
+            }
+            clientCompositionLayers.insert(clientCompositionLayers.end(),
+                                           std::make_move_iterator(results.begin()),
+                                           std::make_move_iterator(results.end()));
+            renderedLayers.push_back(layer);
         }
     });
 
+    std::vector<const renderengine::LayerSettings*> clientCompositionLayerPointers(
+            clientCompositionLayers.size());
+    std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(),
+                   clientCompositionLayerPointers.begin(),
+                   std::pointer_traits<renderengine::LayerSettings*>::pointer_to);
+
     clientCompositionDisplay.clearRegion = clearRegion;
     // Use an empty fence for the buffer fence, since we just created the buffer so
     // there is no need for synchronization with the GPU.
     base::unique_fd bufferFence;
     base::unique_fd drawFence;
     getRenderEngine().useProtectedContext(false);
-    getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers, buffer,
+    getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
                                  /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);
 
     *outSyncFd = drawFence.release();
+
+    if (*outSyncFd >= 0) {
+        sp<Fence> releaseFence = new Fence(dup(*outSyncFd));
+        for (auto* layer : renderedLayers) {
+            layer->onLayerDisplayed(releaseFence);
+        }
+    }
 }
 
 status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea,
                                                  TraverseLayersFunction traverseLayers,
                                                  ANativeWindowBuffer* buffer,
                                                  bool useIdentityTransform, bool forSystem,
-                                                 int* outSyncFd, bool& outCapturedSecureLayers) {
+                                                 int* outSyncFd, bool regionSampling,
+                                                 bool& outCapturedSecureLayers) {
     ATRACE_CALL();
 
     traverseLayers([&](Layer* layer) {
@@ -6131,7 +5916,8 @@
         ALOGW("FB is protected: PERMISSION_DENIED");
         return PERMISSION_DENIED;
     }
-    renderScreenImplLocked(renderArea, traverseLayers, buffer, useIdentityTransform, outSyncFd);
+    renderScreenImplLocked(renderArea, traverseLayers, buffer, useIdentityTransform, regionSampling,
+                           outSyncFd);
     return NO_ERROR;
 }
 
@@ -6139,11 +5925,16 @@
     Mutex::Autolock _l(mStateLock);
 
     mPendingSyncInputWindows = false;
+
     mTransactionCV.broadcast();
 }
 
 // ---------------------------------------------------------------------------
 
+void SurfaceFlinger::State::traverse(const LayerVector::Visitor& visitor) const {
+    layersSortedByZ.traverse(visitor);
+}
+
 void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const {
     layersSortedByZ.traverseInZOrder(stateSet, visitor);
 }
@@ -6173,51 +5964,44 @@
     }
 }
 
-void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
-                                                      const std::vector<int32_t>& allowedConfigs) {
+status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal(
+        const sp<DisplayDevice>& display,
+        const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) {
+    Mutex::Autolock lock(mStateLock);
+
+    LOG_ALWAYS_FATAL_IF(!display->isPrimary() && overridePolicy,
+                        "Can only set override policy on the primary display");
+    LOG_ALWAYS_FATAL_IF(!policy && !overridePolicy, "Can only clear the override policy");
+
     if (!display->isPrimary()) {
-        return;
-    }
+        // TODO(b/144711714): For non-primary displays we should be able to set an active config
+        // as well. For now, just call directly to setActiveConfigWithConstraints but ideally
+        // it should go thru setDesiredActiveConfig, similar to primary display.
+        ALOGV("setAllowedDisplayConfigsInternal for non-primary display");
+        const auto displayId = display->getId();
+        LOG_ALWAYS_FATAL_IF(!displayId);
 
-    const auto allowedDisplayConfigs = DisplayConfigs(allowedConfigs.begin(),
-                                                      allowedConfigs.end());
-    if (allowedDisplayConfigs == mAllowedDisplayConfigs) {
-        return;
-    }
+        hal::VsyncPeriodChangeConstraints constraints;
+        constraints.desiredTimeNanos = systemTime();
+        constraints.seamlessRequired = false;
 
-    ALOGV("Updating allowed configs");
-    mAllowedDisplayConfigs = std::move(allowedDisplayConfigs);
-
-    // 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.
-    // Giving current ActiveConfig so that most other listeners would just drop the event
-    mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
-                                display->getActiveConfig());
-
-    if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
-        // Set the highest allowed config by iterating backwards on available refresh rates
-        const auto& refreshRates = mRefreshRateConfigs->getRefreshRateMap();
-        for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) {
-            if (isDisplayConfigAllowed(iter->second.configId)) {
-                ALOGV("switching to allowed config %d", iter->second.configId);
-                setDesiredActiveConfig(
-                        {iter->first, iter->second.configId, Scheduler::ConfigEvent::Changed});
-                break;
-            }
+        hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
+        if (getHwComposer().setActiveConfigWithConstraints(*displayId,
+                                                           policy->defaultConfig.value(),
+                                                           constraints, &timeline) < 0) {
+            return BAD_VALUE;
         }
-    } else if (!isDisplayConfigAllowed(display->getActiveConfig())) {
-        ALOGV("switching to config %d", allowedConfigs[0]);
-        setDesiredActiveConfig(
-                {RefreshRateType::DEFAULT, allowedConfigs[0], Scheduler::ConfigEvent::Changed});
-    }
-}
+        if (timeline.refreshRequired) {
+            repaintEverythingForHWC();
+        }
 
-status_t SurfaceFlinger::setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                                  const std::vector<int32_t>& allowedConfigs) {
-    ATRACE_CALL();
-
-    if (!displayToken || allowedConfigs.empty()) {
-        return BAD_VALUE;
+        display->setActiveConfig(policy->defaultConfig);
+        const nsecs_t vsyncPeriod = getHwComposer()
+                                            .getConfigs(*displayId)[policy->defaultConfig.value()]
+                                            ->getVsyncPeriod();
+        mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
+                                                     policy->defaultConfig, vsyncPeriod);
+        return NO_ERROR;
     }
 
     if (mDebugDisplayConfigSetByBackdoor) {
@@ -6225,64 +6009,331 @@
         return NO_ERROR;
     }
 
-    postMessageSync(new LambdaMessage([&]() {
-        const auto display = getDisplayDeviceLocked(displayToken);
-        if (!display) {
-            ALOGE("Attempt to set allowed display configs for invalid display token %p",
-                  displayToken.get());
-        } else if (display->isVirtual()) {
-            ALOGW("Attempt to set allowed display configs for virtual display");
-        } else {
-            Mutex::Autolock lock(mStateLock);
-            setAllowedDisplayConfigsInternal(display, allowedConfigs);
-        }
-    }));
+    status_t setPolicyResult = overridePolicy
+            ? mRefreshRateConfigs->setOverridePolicy(policy)
+            : mRefreshRateConfigs->setDisplayManagerPolicy(*policy);
+    if (setPolicyResult < 0) {
+        return BAD_VALUE;
+    }
+    if (setPolicyResult == scheduler::RefreshRateConfigs::CURRENT_POLICY_UNCHANGED) {
+        return NO_ERROR;
+    }
+    scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy();
+
+    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): Leave the event in until we do proper testing with all apps that might
+    // be depending in this callback.
+    const nsecs_t vsyncPeriod =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig())
+                    .getVsyncPeriod();
+    mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
+                                              display->getActiveConfig(), vsyncPeriod);
+    toggleKernelIdleTimer();
+
+    auto configId = mScheduler->getPreferredConfigId();
+    auto& preferredRefreshRate = configId
+            ? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
+            // NOTE: Choose the default config ID, if Scheduler doesn't have one in mind.
+            : mRefreshRateConfigs->getRefreshRateFromConfigId(currentPolicy.defaultConfig);
+    ALOGV("trying to switch to Scheduler preferred config %d (%s)",
+          preferredRefreshRate.getConfigId().value(), preferredRefreshRate.getName().c_str());
+
+    if (isDisplayConfigAllowed(preferredRefreshRate.getConfigId())) {
+        ALOGV("switching to Scheduler preferred config %d",
+              preferredRefreshRate.getConfigId().value());
+        setDesiredActiveConfig(
+                {preferredRefreshRate.getConfigId(), Scheduler::ConfigEvent::Changed});
+    } else {
+        LOG_ALWAYS_FATAL("Desired config not allowed: %d",
+                         preferredRefreshRate.getConfigId().value());
+    }
 
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                                  std::vector<int32_t>* outAllowedConfigs) {
+status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                      int32_t defaultConfig,
+                                                      float primaryRefreshRateMin,
+                                                      float primaryRefreshRateMax,
+                                                      float appRequestRefreshRateMin,
+                                                      float appRequestRefreshRateMax) {
     ATRACE_CALL();
 
-    if (!displayToken || !outAllowedConfigs) {
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
+
+    auto future = schedule([=]() -> status_t {
+        const auto display = ON_MAIN_THREAD(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");
+            return INVALID_OPERATION;
+        } else {
+            using Policy = scheduler::RefreshRateConfigs::Policy;
+            const Policy policy{HwcConfigIndexType(defaultConfig),
+                                {primaryRefreshRateMin, primaryRefreshRateMax},
+                                {appRequestRefreshRateMin, appRequestRefreshRateMax}};
+            constexpr bool kOverridePolicy = false;
+
+            return setDesiredDisplayConfigSpecsInternal(display, policy, kOverridePolicy);
+        }
+    });
+
+    return future.get();
+}
+
+status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                      int32_t* outDefaultConfig,
+                                                      float* outPrimaryRefreshRateMin,
+                                                      float* outPrimaryRefreshRateMax,
+                                                      float* outAppRequestRefreshRateMin,
+                                                      float* outAppRequestRefreshRateMax) {
+    ATRACE_CALL();
+
+    if (!displayToken || !outDefaultConfig || !outPrimaryRefreshRateMin ||
+        !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) {
         return BAD_VALUE;
     }
 
     Mutex::Autolock lock(mStateLock);
-
     const auto display = getDisplayDeviceLocked(displayToken);
     if (!display) {
         return NAME_NOT_FOUND;
     }
 
     if (display->isPrimary()) {
-        outAllowedConfigs->assign(mAllowedDisplayConfigs.begin(), mAllowedDisplayConfigs.end());
-    }
+        scheduler::RefreshRateConfigs::Policy policy =
+                mRefreshRateConfigs->getDisplayManagerPolicy();
+        *outDefaultConfig = policy.defaultConfig.value();
+        *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;
+    } else {
+        const auto displayId = display->getId();
+        LOG_FATAL_IF(!displayId);
 
-    return NO_ERROR;
+        *outDefaultConfig = getHwComposer().getActiveConfigIndex(*displayId);
+        auto vsyncPeriod = getHwComposer().getActiveConfig(*displayId)->getVsyncPeriod();
+        *outPrimaryRefreshRateMin = 1e9f / vsyncPeriod;
+        *outPrimaryRefreshRateMax = 1e9f / vsyncPeriod;
+        *outAppRequestRefreshRateMin = 1e9f / vsyncPeriod;
+        *outAppRequestRefreshRateMax = 1e9f / vsyncPeriod;
+        return NO_ERROR;
+    }
 }
 
 void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() {
     mFlinger->setInputWindowsFinished();
 }
 
-sp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
-    BBinder *b = handle->localBinder();
+wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
+    Mutex::Autolock _l(mStateLock);
+    return fromHandleLocked(handle);
+}
+
+wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) {
+    BBinder* b = nullptr;
+    if (handle) {
+        b = handle->localBinder();
+    }
     if (b == nullptr) {
         return nullptr;
     }
     auto it = mLayersByLocalBinderToken.find(b);
     if (it != mLayersByLocalBinderToken.end()) {
-        return it->second.promote();
+        return it->second;
     }
     return nullptr;
 }
 
+void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
+    mNumLayers++;
+    mScheduler->registerLayer(layer);
+}
+
+void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
+    mNumLayers--;
+    removeFromOffscreenLayers(layer);
+}
+
+// WARNING: ONLY CALL THIS FROM LAYER DTOR
+// Here we add children in the current state to offscreen layers and remove the
+// layer itself from the offscreen layer list.  Since
+// this is the dtor, it is safe to access the current state.  This keeps us
+// from dangling children layers such that they are not reachable from the
+// Drawing state nor the offscreen layer list
+// See b/141111965
+void SurfaceFlinger::removeFromOffscreenLayers(Layer* layer) {
+    for (auto& child : layer->getCurrentChildren()) {
+        mOffscreenLayers.emplace(child.get());
+    }
+    mOffscreenLayers.erase(layer);
+}
+
 void SurfaceFlinger::bufferErased(const client_cache_t& clientCacheId) {
     getRenderEngine().unbindExternalTextureBuffer(clientCacheId.id);
 }
 
+status_t SurfaceFlinger::setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+                                                 float lightPosY, float lightPosZ,
+                                                 float lightRadius) {
+    Mutex::Autolock _l(mStateLock);
+    mCurrentState.globalShadowSettings.ambientColor = vec4(ambientColor);
+    mCurrentState.globalShadowSettings.spotColor = vec4(spotColor);
+    mCurrentState.globalShadowSettings.lightPos.y = lightPosY;
+    mCurrentState.globalShadowSettings.lightPos.z = lightPosZ;
+    mCurrentState.globalShadowSettings.lightRadius = lightRadius;
+
+    // these values are overridden when calculating the shadow settings for a layer.
+    mCurrentState.globalShadowSettings.lightPos.x = 0.f;
+    mCurrentState.globalShadowSettings.length = 0.f;
+    return NO_ERROR;
+}
+
+const std::unordered_map<std::string, uint32_t>& SurfaceFlinger::getGenericLayerMetadataKeyMap()
+        const {
+    // TODO(b/149500060): Remove this fixed/static mapping. Please prefer taking
+    // on the work to remove the table in that bug rather than adding more to
+    // it.
+    static const std::unordered_map<std::string, uint32_t> genericLayerMetadataKeyMap{
+            // Note: METADATA_OWNER_UID and METADATA_WINDOW_TYPE are officially
+            // supported, and exposed via the
+            // IVrComposerClient::VrCommand::SET_LAYER_INFO command.
+            {"org.chromium.arc.V1_0.TaskId", METADATA_TASK_ID},
+            {"org.chromium.arc.V1_0.CursorInfo", METADATA_MOUSE_CURSOR},
+    };
+    return genericLayerMetadataKeyMap;
+}
+
+status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
+                                      int8_t compatibility) {
+    if (!ValidateFrameRate(frameRate, compatibility, "SurfaceFlinger::setFrameRate")) {
+        return BAD_VALUE;
+    }
+
+    static_cast<void>(schedule([=] {
+        Mutex::Autolock lock(mStateLock);
+        if (authenticateSurfaceTextureLocked(surface)) {
+            sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+            if (layer->setFrameRate(
+                        Layer::FrameRate(frameRate,
+                                         Layer::FrameRate::convertCompatibility(compatibility)))) {
+                setTransactionFlags(eTraversalNeeded);
+            }
+        } else {
+            ALOGE("Attempt to set frame rate on an unrecognized IGraphicBufferProducer");
+            return BAD_VALUE;
+        }
+        return NO_ERROR;
+    }));
+
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) {
+    if (!outToken) {
+        return BAD_VALUE;
+    }
+
+    auto future = schedule([this] {
+        status_t result = NO_ERROR;
+        sp<IBinder> token;
+
+        if (mFrameRateFlexibilityTokenCount == 0) {
+            const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+
+            // This is a little racy, but not in a way that hurts anything. As we grab the
+            // defaultConfig from the display manager policy, we could be setting a new display
+            // manager policy, leaving us using a stale defaultConfig. The defaultConfig doesn't
+            // matter for the override policy though, since we set allowGroupSwitching to true, so
+            // it's not a problem.
+            scheduler::RefreshRateConfigs::Policy overridePolicy;
+            overridePolicy.defaultConfig =
+                    mRefreshRateConfigs->getDisplayManagerPolicy().defaultConfig;
+            overridePolicy.allowGroupSwitching = true;
+            constexpr bool kOverridePolicy = true;
+            result = setDesiredDisplayConfigSpecsInternal(display, overridePolicy, kOverridePolicy);
+        }
+
+        if (result == NO_ERROR) {
+            mFrameRateFlexibilityTokenCount++;
+            // Handing out a reference to the SurfaceFlinger object, as we're doing in the line
+            // below, is something to consider carefully. The lifetime of the
+            // FrameRateFlexibilityToken isn't tied to SurfaceFlinger object lifetime, so if this
+            // SurfaceFlinger object were to be destroyed while the token still exists, the token
+            // destructor would be accessing a stale SurfaceFlinger reference, and crash. This is ok
+            // in this case, for two reasons:
+            //   1. Once SurfaceFlinger::run() is called by main_surfaceflinger.cpp, the only way
+            //   the program exits is via a crash. So we won't have a situation where the
+            //   SurfaceFlinger object is dead but the process is still up.
+            //   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.
+            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() {
+    static_cast<void>(schedule([this] {
+        LOG_ALWAYS_FATAL_IF(mFrameRateFlexibilityTokenCount == 0,
+                            "Failed tracking frame rate flexibility tokens");
+        mFrameRateFlexibilityTokenCount--;
+        ALOGD("Frame rate flexibility token released. count=%d", mFrameRateFlexibilityTokenCount);
+        if (mFrameRateFlexibilityTokenCount == 0) {
+            const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+            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_)
@@ -6292,3 +6343,6 @@
 #if defined(__gl2_h_)
 #error "don't include gl2/gl2.h in this file"
 #endif
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 316a48c..ccaeb2d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -23,22 +23,25 @@
  */
 
 #include <android-base/thread_annotations.h>
+#include <compositionengine/OutputColorSetting.h>
 #include <cutils/atomic.h>
 #include <cutils/compiler.h>
 #include <gui/BufferQueue.h>
 #include <gui/FrameTimestamps.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/ISurfaceComposerClient.h>
+#include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerState.h>
 #include <gui/OccupancyTracker.h>
-#include <hardware/hwcomposer_defs.h>
 #include <input/ISetInputWindowsListener.h>
 #include <layerproto/LayerProtoHeader.h>
 #include <math/mat4.h>
+#include <renderengine/LayerSettings.h>
 #include <serviceutils/PriorityDumper.h>
 #include <system/graphics.h>
 #include <ui/FenceTime.h>
 #include <ui/PixelFormat.h>
+#include <ui/Size.h>
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
@@ -52,7 +55,6 @@
 #include "DisplayHardware/PowerAdvisor.h"
 #include "Effects/Daltonizer.h"
 #include "FrameTracker.h"
-#include "LayerStats.h"
 #include "LayerVector.h"
 #include "Scheduler/RefreshRateConfigs.h"
 #include "Scheduler/RefreshRateStats.h"
@@ -60,14 +62,17 @@
 #include "Scheduler/VSyncModulator.h"
 #include "SurfaceFlingerFactory.h"
 #include "SurfaceTracing.h"
+#include "TracedOrdinal.h"
 #include "TransactionCompletedThread.h"
 
 #include <atomic>
 #include <cstdint>
 #include <functional>
+#include <future>
 #include <map>
 #include <memory>
 #include <mutex>
+#include <optional>
 #include <queue>
 #include <set>
 #include <string>
@@ -86,15 +91,18 @@
 class HWComposer;
 class IGraphicBufferProducer;
 class IInputFlinger;
-class InjectVSyncSource;
 class Layer;
 class MessageBase;
 class RefreshRateOverlay;
 class RegionSamplingThread;
 class TimeStats;
+class FrameTracer;
 
 namespace compositionengine {
 class DisplaySurface;
+class OutputLayer;
+
+struct CompositionRefreshArgs;
 } // namespace compositionengine
 
 namespace renderengine {
@@ -109,16 +117,12 @@
     eTransactionNeeded = 0x01,
     eTraversalNeeded = 0x02,
     eDisplayTransactionNeeded = 0x04,
-    eDisplayLayerStackChanged = 0x08,
+    eTransformHintUpdateNeeded = 0x08,
     eTransactionFlushNeeded = 0x10,
     eTransactionMask = 0x1f,
 };
 
-enum class DisplayColorSetting : int32_t {
-    MANAGED = 0,
-    UNMANAGED = 1,
-    ENHANCED = 2,
-};
+using DisplayColorSetting = compositionengine::OutputColorSetting;
 
 class SurfaceFlingerBE
 {
@@ -172,7 +176,8 @@
                        public PriorityDumper,
                        public ClientCache::ErasedRecipient,
                        private IBinder::DeathRecipient,
-                       private HWC2::ComposerCallback {
+                       private HWC2::ComposerCallback,
+                       private ISchedulerCallback {
 public:
     SurfaceFlingerBE& getBE() { return mBE; }
     const SurfaceFlingerBE& getBE() const { return mBE; }
@@ -221,11 +226,16 @@
     // FramebufferSurface
     static int64_t maxFrameBufferAcquiredBuffers;
 
+    // Controls the maximum width and height in pixels that the graphics pipeline can support for
+    // GPU fallback composition. For example, 8k devices with 4k GPUs, or 4k devices with 2k GPUs.
+    static uint32_t maxGraphicsWidth;
+    static uint32_t maxGraphicsHeight;
+
     // Indicate if a device has wide color gamut display. This is typically
     // found on devices with wide color gamut (e.g. Display-P3) display.
     static bool hasWideColorDisplay;
 
-    static int primaryDisplayOrientation;
+    static ui::Rotation internalDisplayOrientation;
 
     // Indicate if device wants color management on its display.
     static bool useColorManagement;
@@ -244,6 +254,14 @@
     static ui::Dataspace wideColorGamutCompositionDataspace;
     static ui::PixelFormat wideColorGamutCompositionPixelFormat;
 
+    // Whether to use frame rate API when deciding about the refresh rate of the display. This
+    // variable is caches in SF, so that we can check it with each layer creation, and a void the
+    // overhead that is caused by reading from sysprop.
+    static bool useFrameRateApi;
+
+    // set main thread scheduling policy
+    static status_t setSchedFifo(bool enabled) ANDROID_API;
+
     static char const* getServiceName() ANDROID_API {
         return "SurfaceFlinger";
     }
@@ -259,30 +277,18 @@
     // 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();
 
-    // force full composition on all displays without resetting the scheduler idle timer.
-    void repaintEverythingForHWC();
-
     surfaceflinger::Factory& getFactory() { return mFactory; }
 
     // The CompositionEngine encapsulates all composition related interfaces and actions.
     compositionengine::CompositionEngine& getCompositionEngine() const;
 
-    // returns the default Display
-    sp<const DisplayDevice> getDefaultDisplayDevice() {
-        Mutex::Autolock _l(mStateLock);
-        return getDefaultDisplayDeviceLocked();
-    }
-
     // Obtains a name from the texture pool, or, if the pool is empty, posts a
     // synchronous message to the main thread to obtain one on the fly
     uint32_t getNewTexture();
@@ -295,43 +301,41 @@
     void setPrimaryVsyncEnabled(bool enabled);
 
     // main thread function to enable/disable h/w composer event
-    void setPrimaryVsyncEnabledInternal(bool enabled);
+    void setPrimaryVsyncEnabledInternal(bool enabled) REQUIRES(mStateLock);
 
     // called on the main thread by MessageQueue when an internal message
     // is received
     // TODO: this should be made accessible only to MessageQueue
-    void onMessageReceived(int32_t what);
-
-    // populates the expected present time for this frame.
-    // When we are in negative offsets, we perform a correction so that the
-    // predicted vsync for the *next* frame is used instead.
-    void populateExpectedPresentTime();
-    nsecs_t getExpectedPresentTime() const { return mExpectedPresentTime; }
-
-    // for debugging only
-    // TODO: this should be made accessible only to HWComposer
-    const Vector<sp<Layer>>& getLayerSortedByZForHwcDisplay(DisplayId displayId);
+    void onMessageReceived(int32_t what, nsecs_t expectedVSyncTime);
 
     renderengine::RenderEngine& getRenderEngine() const;
 
     bool authenticateSurfaceTextureLocked(
         const sp<IGraphicBufferProducer>& bufferProducer) const;
 
-    inline void onLayerCreated() { mNumLayers++; }
-    inline void onLayerDestroyed(Layer* layer) {
-        mNumLayers--;
-        mOffscreenLayers.erase(layer);
-    }
+    void onLayerFirstRef(Layer*);
+    void onLayerDestroyed(Layer*);
+
+    void removeFromOffscreenLayers(Layer* layer);
 
     TransactionCompletedThread& getTransactionCompletedThread() {
         return mTransactionCompletedThread;
     }
 
-    sp<Layer> fromHandle(const sp<IBinder>& handle) REQUIRES(mStateLock);
+    // Converts from a binder handle to a Layer
+    // Returns nullptr if the handle does not point to an existing layer.
+    // Otherwise, returns a weak reference so that callers off the main-thread
+    // won't accidentally hold onto the last strong reference.
+    wp<Layer> fromHandle(const sp<IBinder>& handle);
+    wp<Layer> fromHandleLocked(const sp<IBinder>& handle) REQUIRES(mStateLock);
 
     // Inherit from ClientCache::ErasedRecipient
     void bufferErased(const client_cache_t& clientCacheId) override;
 
+    // If set, disables reusing client composition buffers. This can be set by
+    // debug.sf.disable_client_composition_cache
+    bool mDisableClientCompositionCache = false;
+
 private:
     friend class BufferLayer;
     friend class BufferQueueLayer;
@@ -345,17 +349,19 @@
 
     // For unit tests
     friend class TestableSurfaceFlinger;
+    friend class TransactionApplicationTest;
 
     // This value is specified in number of frames.  Log frame stats at most
     // every half hour.
     enum { LOG_FRAME_STATS_PERIOD =  30*60*60 };
 
-    static const size_t MAX_LAYERS = 4096;
     static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
 
+protected:
     // We're reference counted, never destroy SurfaceFlinger directly
     virtual ~SurfaceFlinger();
 
+private:
     /* ------------------------------------------------------------------------
      * Internal data structures
      */
@@ -372,6 +378,8 @@
             if (colorMatrixChanged) {
                 colorMatrix = other.colorMatrix;
             }
+            globalShadowSettings = other.globalShadowSettings;
+
             return *this;
         }
 
@@ -382,6 +390,9 @@
         bool colorMatrixChanged = true;
         mat4 colorMatrix;
 
+        renderengine::ShadowSettings globalShadowSettings;
+
+        void traverse(const LayerVector::Visitor& visitor) const;
         void traverseInZOrder(const LayerVector::Visitor& visitor) const;
         void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
     };
@@ -391,7 +402,8 @@
      */
     status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
     status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); }
-    bool callingThreadHasUnscopedSurfaceFlingerAccess() EXCLUDES(mStateLock);
+    bool callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermissionCache = true)
+            EXCLUDES(mStateLock);
 
     /* ------------------------------------------------------------------------
      * ISurfaceComposer interface
@@ -406,6 +418,7 @@
                              const sp<IBinder>& applyToken,
                              const InputWindowCommands& inputWindowCommands,
                              int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
+                             bool hasListenerCallbacks,
                              const std::vector<ListenerCallbacks>& listenerCallbacks) override;
     void bootFinished() override;
     bool authenticateSurfaceTexture(
@@ -416,10 +429,10 @@
             ISurfaceComposer::ConfigChanged configChanged =
                     ISurfaceComposer::eConfigChangedSuppress) override;
     status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer,
-            bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace,
-            const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
-            uint32_t reqWidth, uint32_t reqHeight,
-            bool useIdentityTransform, ISurfaceComposer::Rotation rotation, bool captureSecureLayers) override;
+                           bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
+                           ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
+                           uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+                           ui::Rotation rotation, bool captureSecureLayers) override;
     status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
                            sp<GraphicBuffer>* outBuffer) override;
     status_t captureLayers(
@@ -430,36 +443,40 @@
             float frameScale, bool childrenOnly) override;
 
     status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
-    status_t getDisplayConfigs(const sp<IBinder>& displayToken,
-                               Vector<DisplayInfo>* configs) override;
+    status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) override;
+    status_t getDisplayInfo(const sp<IBinder>& displayToken, DisplayInfo*) override;
+    status_t getDisplayConfigs(const sp<IBinder>& displayToken, Vector<DisplayConfig>*) override;
     int getActiveConfig(const sp<IBinder>& displayToken) override;
-    status_t getDisplayColorModes(const sp<IBinder>& displayToken,
-                                  Vector<ui::ColorMode>* configs) override;
+    status_t getDisplayColorModes(const sp<IBinder>& displayToken, Vector<ui::ColorMode>*) override;
     status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
-                                       ui::DisplayPrimaries &primaries);
+                                       ui::DisplayPrimaries&) override;
     ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken) override;
     status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
+    status_t getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
+                                          bool* outSupported) const override;
+    void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) override;
+    status_t getGameContentTypeSupport(const sp<IBinder>& displayToken,
+                                       bool* outSupported) const override;
+    void setGameContentType(const sp<IBinder>& displayToken, bool on) override;
     void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
-    status_t setActiveConfig(const sp<IBinder>& displayToken, int id) override;
     status_t clearAnimationFrameStats() override;
     status_t getAnimationFrameStats(FrameStats* outStats) const override;
     status_t getHdrCapabilities(const sp<IBinder>& displayToken,
                                 HdrCapabilities* outCapabilities) const override;
     status_t enableVSyncInjections(bool enable) override;
     status_t injectVSync(nsecs_t when) override;
-    status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const override;
+    status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override;
     status_t getColorManagement(bool* outGetColorManagement) const override;
     status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
                                       ui::Dataspace* outWideColorGamutDataspace,
                                       ui::PixelFormat* outWideColorGamutPixelFormat) const override;
-    status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& display,
+    status_t getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken,
                                                    ui::PixelFormat* outFormat,
                                                    ui::Dataspace* outDataspace,
                                                    uint8_t* outComponentMask) const override;
-    status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable,
-                                              uint8_t componentMask,
-                                              uint64_t maxFrames) const override;
-    status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
+    status_t setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken, bool enable,
+                                              uint8_t componentMask, uint64_t maxFrames) override;
+    status_t getDisplayedContentSample(const sp<IBinder>& displayToken, uint64_t maxFrames,
                                        uint64_t timestamp,
                                        DisplayedFrameStats* outStats) const override;
     status_t getProtectedContentSupport(bool* outSupported) const override;
@@ -468,15 +485,25 @@
     status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
                                        const sp<IRegionSamplingListener>& listener) override;
     status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
-    status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                      const std::vector<int32_t>& allowedConfigs) override;
-    status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                      std::vector<int32_t>* outAllowedConfigs) override;
+    status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t displayModeId,
+                                          float primaryRefreshRateMin, float primaryRefreshRateMax,
+                                          float appRequestRefreshRateMin,
+                                          float appRequestRefreshRateMax) override;
+    status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                          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) const override;
+    status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override;
     status_t notifyPowerHint(int32_t hintId) 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,
+                          int8_t compatibility) override;
+    status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
     /* ------------------------------------------------------------------------
      * DeathRecipient interface
      */
@@ -490,31 +517,48 @@
     /* ------------------------------------------------------------------------
      * HWC2::ComposerCallback / HWComposer::EventHandler interface
      */
-    void onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
-                         int64_t timestamp) override;
-    void onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
-                           HWC2::Connection connection) override;
-    void onRefreshReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId) override;
+    void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId, int64_t timestamp,
+                         std::optional<hal::VsyncPeriodNanos> vsyncPeriod) override;
+    void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId,
+                           hal::Connection connection) override;
+    void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId) override;
+    void onVsyncPeriodTimingChangedReceived(
+            int32_t sequenceId, hal::HWDisplayId display,
+            const hal::VsyncPeriodChangeTimeline& updatedTimeline) override;
+    void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId display) override;
 
     /* ------------------------------------------------------------------------
+     * ISchedulerCallback
+     */
+    void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
+    // force full composition on all displays without resetting the scheduler idle timer.
+    void repaintEverythingForHWC() override;
+    // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
+    void kernelTimerChanged(bool expired) override;
+    // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
+    void toggleKernelIdleTimer();
+    // Keeps track of whether the kernel idle timer is currently enabled, so we don't have to
+    // make calls to sys prop each time.
+    bool mKernelIdleTimerEnabled = false;
+    // Keeps track of whether the kernel timer is supported on the SF side.
+    bool mSupportKernelIdleTimer = false;
+    /* ------------------------------------------------------------------------
      * 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
     void signalLayerUpdate();
     void signalRefresh();
 
-    using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+    using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
 
     struct ActiveConfigInfo {
-        RefreshRateType type = RefreshRateType::DEFAULT;
-        int configId = 0;
+        HwcConfigIndexType configId;
         Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
 
         bool operator!=(const ActiveConfigInfo& other) const {
-            return type != other.type || configId != other.configId || event != other.event;
+            return configId != other.configId || event != other.event;
         }
     };
 
@@ -522,32 +566,39 @@
     void onInitializeDisplays() REQUIRES(mStateLock);
     // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
     void setDesiredActiveConfig(const ActiveConfigInfo& info) REQUIRES(mStateLock);
+    status_t setActiveConfig(const sp<IBinder>& displayToken, int id);
     // Once HWC has returned the present fence, this sets the active config and a new refresh
     // rate in SF.
     void setActiveConfigInternal() REQUIRES(mStateLock);
-    // Active config is updated on INVALIDATE call in a state machine-like manner. When the
-    // desired config was set, HWC needs to update the panel on the next refresh, and when
-    // we receive the fence back, we know that the process was complete. It returns whether
-    // we need to wait for the next invalidate
-    bool performSetActiveConfig() REQUIRES(mStateLock);
+    // Calls to setActiveConfig on the main thread if there is a pending config
+    // that needs to be applied.
+    void performSetActiveConfig() REQUIRES(mStateLock);
     // Called when active config is no longer is progress
     void desiredActiveConfigChangeDone() REQUIRES(mStateLock);
     // called on the main thread in response to setPowerMode()
-    void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
-
-    // called on the main thread in response to setAllowedDisplayConfigs()
-    void setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
-                                          const std::vector<int32_t>& allowedConfigs)
+    void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
             REQUIRES(mStateLock);
 
+    // Sets the desired display configs.
+    status_t setDesiredDisplayConfigSpecsInternal(
+            const sp<DisplayDevice>& display,
+            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);
 
@@ -571,10 +622,10 @@
                                const Vector<DisplayState>& displays, uint32_t flags,
                                const InputWindowCommands& inputWindowCommands,
                                const int64_t desiredPresentTime,
-                               const client_cache_t& uncacheBuffer,
+                               const client_cache_t& uncacheBuffer, const int64_t postTime,
+                               bool privileged, bool hasListenerCallbacks,
                                const std::vector<ListenerCallbacks>& listenerCallbacks,
-                               const int64_t postTime, bool privileged, bool isMainThread = false)
-            REQUIRES(mStateLock);
+                               bool isMainThread = false) REQUIRES(mStateLock);
     // Returns true if at least one transaction was flushed
     bool flushTransactionQueues();
     // Returns true if there is at least one transaction that needs to be flushed
@@ -583,46 +634,67 @@
     uint32_t peekTransactionFlags();
     // Can only be called from the main thread or with mStateLock held
     uint32_t setTransactionFlags(uint32_t flags);
+    // Indicate SF should call doTraversal on layers, but don't trigger a wakeup! We use this cases
+    // where there are still pending transactions but we know they won't be ready until a frame
+    // arrives from a different layer. So we need to ensure we performTransaction from invalidate
+    // but there is no need to try and wake up immediately to do it. Rather we rely on
+    // onFrameAvailable or another layer update to wake us up.
+    void setTraversalNeeded();
     uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
-    void latchAndReleaseBuffer(const sp<Layer>& layer);
     void commitTransaction() REQUIRES(mStateLock);
     void commitOffscreenLayers();
-    bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
     bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
                                        const Vector<ComposerState>& states);
-    uint32_t setClientStateLocked(const ComposerState& composerState, int64_t desiredPresentTime,
-                                  const std::vector<ListenerCallbacks>& listenerCallbacks,
-                                  int64_t postTime, bool privileged) REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
 
+protected:
+    virtual uint32_t setClientStateLocked(
+            const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
+            bool privileged,
+            std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
+            REQUIRES(mStateLock);
+    virtual void commitTransactionLocked();
+
+    // Used internally by computeLayerBounds() to gets the clip rectangle to use for the
+    // root layers on a particular display in layer-coordinate space. The
+    // layers (and effectively their children) will be clipped against this
+    // rectangle. The base behavior is to clip to the visible region of the
+    // display.
+    virtual FloatRect getLayerClipBoundsForDisplay(const DisplayDevice&) const;
+
+private:
     /* ------------------------------------------------------------------------
      * Layer management
      */
     status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
                          PixelFormat format, uint32_t flags, LayerMetadata metadata,
                          sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
-                         const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr);
+                         const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr,
+                         uint32_t* outTransformHint = nullptr);
 
-    status_t createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w,
+    status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w,
                                     uint32_t h, uint32_t flags, LayerMetadata metadata,
                                     PixelFormat& format, sp<IBinder>* outHandle,
                                     sp<IGraphicBufferProducer>* outGbp, sp<Layer>* outLayer);
 
-    status_t createBufferStateLayer(const sp<Client>& client, const String8& name, uint32_t w,
+    status_t createBufferStateLayer(const sp<Client>& client, std::string name, uint32_t w,
                                     uint32_t h, uint32_t flags, LayerMetadata metadata,
                                     sp<IBinder>* outHandle, sp<Layer>* outLayer);
 
-    status_t createColorLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h,
-                              uint32_t flags, LayerMetadata metadata, 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,
+                               sp<Layer>* outLayer);
 
-    status_t createContainerLayer(const sp<Client>& client, const String8& name, uint32_t w,
+    status_t createContainerLayer(const sp<Client>& client, std::string name, uint32_t w,
                                   uint32_t h, uint32_t flags, LayerMetadata metadata,
                                   sp<IBinder>* outHandle, sp<Layer>* outLayer);
 
-    String8 getUniqueLayerName(const String8& name);
+    status_t mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
+                         sp<IBinder>* outHandle);
+
+    std::string getUniqueLayerName(const char* name);
 
     // called when all clients have released all their references to
     // this layer meaning it is entirely safe to destroy all
@@ -634,7 +706,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();
@@ -649,18 +721,20 @@
 
     void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers,
                                 ANativeWindowBuffer* buffer, bool useIdentityTransform,
-                                int* outSyncFd);
+                                bool regionSampling, int* outSyncFd);
     status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
                                  sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat,
                                  bool useIdentityTransform, bool& outCapturedSecureLayers);
     status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers,
                                  const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
-                                 bool& outCapturedSecureLayers);
-    const sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack);
+                                 bool regionSampling, bool& outCapturedSecureLayers);
+    sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
+    sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
     status_t captureScreenImplLocked(const RenderArea& renderArea,
                                      TraverseLayersFunction traverseLayers,
                                      ANativeWindowBuffer* buffer, bool useIdentityTransform,
-                                     bool forSystem, int* outSyncFd, bool& outCapturedSecureLayers);
+                                     bool forSystem, int* outSyncFd, bool regionSampling,
+                                     bool& outCapturedSecureLayers);
     void traverseLayersInDisplay(const sp<const DisplayDevice>& display,
                                  const LayerVector::Visitor& visitor);
 
@@ -683,38 +757,34 @@
     // called when starting, or restarting after system_server death
     void initializeDisplays();
 
-    sp<const DisplayDevice> getDisplayDevice(const wp<IBinder>& displayToken) const {
-        Mutex::Autolock _l(mStateLock);
-        return getDisplayDeviceLocked(displayToken);
-    }
-
-    sp<DisplayDevice> getDisplayDevice(const wp<IBinder>& displayToken) {
-        Mutex::Autolock _l(mStateLock);
-        return getDisplayDeviceLocked(displayToken);
-    }
-
-    // NOTE: can only be called from the main thread or with mStateLock held
-    sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const {
+    sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const
+            REQUIRES(mStateLock) {
         return const_cast<SurfaceFlinger*>(this)->getDisplayDeviceLocked(displayToken);
     }
 
-    // NOTE: can only be called from the main thread or with mStateLock held
-    sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) {
+    sp<DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) REQUIRES(mStateLock) {
         const auto it = mDisplays.find(displayToken);
         return it == mDisplays.end() ? nullptr : it->second;
     }
 
-    sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const {
+    sp<const DisplayDevice> getDefaultDisplayDeviceLocked() const REQUIRES(mStateLock) {
         return const_cast<SurfaceFlinger*>(this)->getDefaultDisplayDeviceLocked();
     }
 
-    sp<DisplayDevice> getDefaultDisplayDeviceLocked() {
+    sp<DisplayDevice> getDefaultDisplayDeviceLocked() REQUIRES(mStateLock) {
         if (const auto token = getInternalDisplayTokenLocked()) {
             return getDisplayDeviceLocked(token);
         }
         return nullptr;
     }
 
+    sp<const DisplayDevice> getDefaultDisplayDevice() EXCLUDES(mStateLock) {
+        Mutex::Autolock lock(mStateLock);
+        return getDefaultDisplayDeviceLocked();
+    }
+
+    std::optional<DeviceProductInfo> getDeviceProductInfoLocked(const DisplayDevice&) const;
+
     // mark a region of a layer stack dirty. this updates the dirty
     // region of all screens presenting this layer stack.
     void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
@@ -749,64 +819,33 @@
      * Compositing
      */
     void invalidateHwcGeometry();
-    void computeVisibleRegions(const sp<const DisplayDevice>& display, Region& dirtyRegion,
-                               Region& opaqueRegion);
 
-    void preComposition();
     void postComposition();
     void getCompositorTiming(CompositorTiming* compositorTiming);
     void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
                                 std::shared_ptr<FenceTime>& presentFenceTime);
     void setCompositorTimingSnapped(const DisplayStatInfo& stats,
                                     nsecs_t compositeToPresentLatency);
-    void rebuildLayerStacks();
 
-    ui::Dataspace getBestDataspace(const sp<DisplayDevice>& display, ui::Dataspace* outHdrDataSpace,
-                                   bool* outIsHdrClientComposition) const;
-
-    // Returns the appropriate ColorMode, Dataspace and RenderIntent for the
-    // DisplayDevice. The function only returns the supported ColorMode,
-    // Dataspace and RenderIntent.
-    void pickColorMode(const sp<DisplayDevice>& display, ui::ColorMode* outMode,
-                       ui::Dataspace* outDataSpace, ui::RenderIntent* outRenderIntent) const;
-
-    void calculateWorkingSet();
-    /*
-     * beginFrame - This function handles any pre-frame processing that needs to be
-     * prior to any CompositionInfo handling and is not dependent on data in
-     * CompositionInfo
-     */
-    void beginFrame(const sp<DisplayDevice>& display);
-    /* prepareFrame - This function will call into the DisplayDevice to prepare a
-     * frame after CompositionInfo has been programmed.   This provides a mechanism
-     * to prepare the hardware composer
-     */
-    void prepareFrame(const sp<DisplayDevice>& display);
-    void doComposition(const sp<DisplayDevice>& display, bool repainEverything);
-    void doDebugFlashRegions(const sp<DisplayDevice>& display, bool repaintEverything);
-    void logLayerStats();
-    void doDisplayComposition(const sp<DisplayDevice>& display, const Region& dirtyRegion);
-
-    // This fails if using GL and the surface has been destroyed. readyFence
-    // will be populated if using GL and native fence sync is supported, to
-    // signal when drawing has completed.
-    bool doComposeSurfaces(const sp<DisplayDevice>& display, const Region& debugRegionm,
-                           base::unique_fd* readyFence);
-
-    void postFramebuffer(const sp<DisplayDevice>& display);
     void postFrame();
-    void drawWormhole(const Region& region) const;
 
     /* ------------------------------------------------------------------------
      * Display management
      */
     sp<DisplayDevice> setupNewDisplayDeviceInternal(
-            const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
+            const wp<IBinder>& displayToken,
+            std::shared_ptr<compositionengine::Display> compositionDisplay,
             const DisplayDeviceState& state,
-            const sp<compositionengine::DisplaySurface>& dispSurface,
-            const sp<IGraphicBufferProducer>& producer);
-    void processDisplayChangesLocked();
-    void processDisplayHotplugEventsLocked();
+            const sp<compositionengine::DisplaySurface>& displaySurface,
+            const sp<IGraphicBufferProducer>& producer) REQUIRES(mStateLock);
+    void processDisplayChangesLocked() REQUIRES(mStateLock);
+    void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState&)
+            REQUIRES(mStateLock);
+    void processDisplayRemoved(const wp<IBinder>& displayToken) REQUIRES(mStateLock);
+    void processDisplayChanged(const wp<IBinder>& displayToken,
+                               const DisplayDeviceState& currentState,
+                               const DisplayDeviceState& drawingState) REQUIRES(mStateLock);
+    void processDisplayHotplugEventsLocked() REQUIRES(mStateLock);
 
     void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
 
@@ -817,19 +856,41 @@
 
     // Sets the refresh rate by switching active configs, if they are available for
     // the desired refresh rate.
-    void setRefreshRateTo(RefreshRateType, Scheduler::ConfigEvent event) REQUIRES(mStateLock);
+    void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent event)
+            REQUIRES(mStateLock);
 
-    bool isDisplayConfigAllowed(int32_t configId) REQUIRES(mStateLock);
+    bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
+
+    // Gets the fence for the previous frame.
+    // Must be called on the main thread.
+    sp<Fence> previousFrameFence();
+
+    // Whether the previous frame has not yet been presented to the display.
+    // If graceTimeMs is positive, this method waits for at most the provided
+    // grace period before reporting if the frame missed.
+    // Must be called on the main thread.
+    bool previousFramePending(int graceTimeMs = 0);
+
+    // Returns the previous time that the frame was presented. If the frame has
+    // not been presented yet, then returns Fence::SIGNAL_TIME_PENDING. If there
+    // is no pending frame, then returns Fence::SIGNAL_TIME_INVALID.
+    // Must be called on the main thread.
+    nsecs_t previousFramePresentTime();
+
+    // Calculates the expected present time for this frame. For negative offsets, performs a
+    // correction using the predicted vsync for the next frame instead.
+    nsecs_t calculateExpectedPresentTime(nsecs_t now) const;
 
     /*
      * Display identification
      */
-    sp<IBinder> getPhysicalDisplayTokenLocked(DisplayId displayId) const {
+    sp<IBinder> getPhysicalDisplayTokenLocked(DisplayId displayId) const REQUIRES(mStateLock) {
         const auto it = mPhysicalDisplayTokens.find(displayId);
         return it != mPhysicalDisplayTokens.end() ? it->second : nullptr;
     }
 
-    std::optional<DisplayId> getPhysicalDisplayIdLocked(const sp<IBinder>& displayToken) const {
+    std::optional<DisplayId> getPhysicalDisplayIdLocked(const sp<IBinder>& displayToken) const
+            REQUIRES(mStateLock) {
         for (const auto& [id, token] : mPhysicalDisplayTokens) {
             if (token == displayToken) {
                 return id;
@@ -839,19 +900,16 @@
     }
 
     // TODO(b/74619554): Remove special cases for primary display.
-    sp<IBinder> getInternalDisplayTokenLocked() const {
+    sp<IBinder> getInternalDisplayTokenLocked() const REQUIRES(mStateLock) {
         const auto displayId = getInternalDisplayIdLocked();
         return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr;
     }
 
-    std::optional<DisplayId> getInternalDisplayIdLocked() const {
+    std::optional<DisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) {
         const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId();
         return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
     }
 
-    bool previousFrameMissed(int graceTimeMs = 0);
-    void setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled);
-
     /*
      * Debugging & dumpsys
      */
@@ -896,16 +954,20 @@
     // Not const because each Layer needs to query Fences and cache timestamps.
     void dumpFrameEventsLocked(std::string& result);
 
-    void recordBufferingStats(const char* layerName,
-            std::vector<OccupancyTracker::Segment>&& history);
+    void recordBufferingStats(const std::string& layerName,
+                              std::vector<OccupancyTracker::Segment>&& history);
     void dumpBufferingStats(std::string& result) const;
-    void dumpDisplayIdentificationData(std::string& result) const;
-    void dumpWideColorInfo(std::string& result) const;
-    LayersProto dumpDrawingStateProto(uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+    void dumpDisplayIdentificationData(std::string& result) const REQUIRES(mStateLock);
+    void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
+    void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock);
+    LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
+    void dumpOffscreenLayersProto(LayersProto& layersProto,
+                                  uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+    // Dumps state from HW Composer
+    void dumpHwc(std::string& result) const;
     LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL)
             EXCLUDES(mStateLock);
-    void withTracingLock(std::function<void()> operation) REQUIRES(mStateLock);
-    LayersProto dumpVisibleLayersProtoInfo(const sp<DisplayDevice>& display) const;
+    void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock);
 
     bool isLayerTripleBufferingDisabled() const {
         return this->mLayerTripleBufferingDisabled;
@@ -919,10 +981,12 @@
         return doDump(fd, args, asProto);
     }
 
+    void onFrameRateFlexibilityTokenReleased();
+
     /* ------------------------------------------------------------------------
      * VrFlinger
      */
-    void resetDisplayState();
+    void resetDisplayState() REQUIRES(mStateLock);
 
     // Check to see if we should handoff to vr flinger.
     void updateVrFlinger();
@@ -943,10 +1007,7 @@
     bool mTransactionPending = false;
     bool mAnimTransactionPending = false;
     SortedVector<sp<Layer>> mLayersPendingRemoval;
-    bool mTraversalNeededMainThread = false;
-
-    // guards access to the mDrawing state if tracing is enabled.
-    mutable std::mutex mDrawingStateLock;
+    bool mForceTraversal = false;
 
     // global color transform states
     Daltonizer mDaltonizer;
@@ -955,7 +1016,9 @@
 
     // Can't be unordered_set because wp<> isn't hashable
     std::set<wp<IBinder>> mGraphicBufferProducerList;
-    size_t mMaxGraphicBufferProducerListSize = MAX_LAYERS;
+    size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS;
+
+    void removeGraphicBufferProducerAsync(const wp<IBinder>&);
 
     // protected by mStateLock (but we could use another lock)
     bool mLayersRemoved = false;
@@ -966,13 +1029,7 @@
     // constant members (no synchronization needed for access)
     const nsecs_t mBootTime = systemTime();
     bool mGpuToCpuSupported = false;
-    std::unique_ptr<EventThread> mInjectorEventThread;
-    std::unique_ptr<InjectVSyncSource> mVSyncInjector;
-
-    // Calculates correct offsets.
-    VSyncModulator mVsyncModulator;
-    // Keeps track of all available phase offsets for different refresh types.
-    const std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
+    bool mIsUserBuild = true;
 
     // Can only accessed from the main thread, these members
     // don't need synchronization
@@ -992,6 +1049,10 @@
     // Note that it is possible for a frame to be composed via both client and device
     // composition, for example in the case of overlays.
     bool mHadDeviceComposition = false;
+    // True if in the previous frame, the client composition was skipped by reusing the buffer
+    // used in a previous composition. This can happed if the client composition requests
+    // did not change.
+    bool mReusedClientComposition = false;
 
     enum class BootStage {
         BOOTLOADER,
@@ -1001,19 +1062,17 @@
     BootStage mBootStage = BootStage::BOOTLOADER;
 
     struct HotplugEvent {
-        hwc2_display_t hwcDisplayId;
-        HWC2::Connection connection = HWC2::Connection::Invalid;
+        hal::HWDisplayId hwcDisplayId;
+        hal::Connection connection = hal::Connection::INVALID;
     };
-    // protected by mStateLock
-    std::vector<HotplugEvent> mPendingHotplugEvents;
+    std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mStateLock);
 
     // this may only be written from the main thread with mStateLock held
     // it may be read from other threads with mStateLock held
-    std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays;
-    std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens;
+    std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays GUARDED_BY(mStateLock);
+    std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens GUARDED_BY(mStateLock);
 
-    // protected by mStateLock
-    std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken;
+    std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock);
 
     // don't use a lock for these, we don't care
     int mDebugRegion = 0;
@@ -1024,12 +1083,22 @@
     bool mPropagateBackpressure = true;
     bool mPropagateBackpressureClientComposition = false;
     std::unique_ptr<SurfaceInterceptor> mInterceptor;
+
     SurfaceTracing mTracing{*this};
+    std::mutex mTracingLock;
     bool mTracingEnabled = false;
-    bool mTracingEnabledChanged GUARDED_BY(mStateLock) = false;
-    LayerStats mLayerStats;
+    bool mAddCompositionStateToTrace = false;
+    std::atomic<bool> mTracingEnabledChanged = false;
+
     const std::shared_ptr<TimeStats> mTimeStats;
+    const std::unique_ptr<FrameTracer> mFrameTracer;
     bool mUseHwcVirtualDisplays = false;
+    // If blurs should be enabled on this device.
+    bool mSupportsBlur = false;
+    // Disable blurs, for debugging
+    std::atomic<bool> mDisableBlurs = false;
+    // If blurs are considered expensive and should require high GPU frequency.
+    bool mBlursAreExpensive = false;
     std::atomic<uint32_t> mFrameMissedCount = 0;
     std::atomic<uint32_t> mHwcFrameMissedCount = 0;
     std::atomic<uint32_t> mGpuFrameMissedCount = 0;
@@ -1058,47 +1127,42 @@
     uint32_t mTexturePoolSize = 0;
     std::vector<uint32_t> mTexturePool;
 
-    struct IBinderHash {
-        std::size_t operator()(const sp<IBinder>& strongPointer) const {
-            return std::hash<IBinder*>{}(strongPointer.get());
-        }
-    };
     struct TransactionState {
         TransactionState(const Vector<ComposerState>& composerStates,
                          const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
                          int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
-                         const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
-                         bool privileged)
+                         int64_t postTime, bool privileged, bool hasListenerCallbacks,
+                         std::vector<ListenerCallbacks> listenerCallbacks)
               : states(composerStates),
                 displays(displayStates),
                 flags(transactionFlags),
                 desiredPresentTime(desiredPresentTime),
                 buffer(uncacheBuffer),
-                callback(listenerCallbacks),
                 postTime(postTime),
-                privileged(privileged) {}
+                privileged(privileged),
+                hasListenerCallbacks(hasListenerCallbacks),
+                listenerCallbacks(listenerCallbacks) {}
 
         Vector<ComposerState> states;
         Vector<DisplayState> displays;
         uint32_t flags;
         const int64_t desiredPresentTime;
         client_cache_t buffer;
-        std::vector<ListenerCallbacks> callback;
         const int64_t postTime;
         bool privileged;
+        bool hasListenerCallbacks;
+        std::vector<ListenerCallbacks> listenerCallbacks;
     };
-    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IBinderHash> mTransactionQueues;
+    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues;
 
     /* ------------------------------------------------------------------------
      * Feature prototyping
      */
 
-    bool mInjectVSyncs = false;
-
     // Static screen stats
     bool mHasPoweredOff = false;
 
-    size_t mNumLayers = 0;
+    std::atomic<size_t> mNumLayers = 0;
 
     // Verify that transaction is being called by an approved process:
     // either AID_GRAPHICS or AID_SYSTEM.
@@ -1106,13 +1170,17 @@
 
     // to linkToDeath
     sp<IBinder> mWindowManager;
+    // We want to avoid multiple calls to BOOT_FINISHED as they come in on
+    // different threads without a lock and could trigger unsynchronized writes to
+    // to mWindowManager or mInputFlinger
+    std::atomic<bool> mBootFinished = false;
 
     std::unique_ptr<dvr::VrFlinger> mVrFlinger;
     std::atomic<bool> mVrFlingerRequestsDisplay = false;
     static bool useVrFlinger;
     std::thread::id mMainThreadId = std::this_thread::get_id();
 
-    DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::ENHANCED;
+    DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::kEnhanced;
 
     // Color mode forced by setting persist.sys.sf.color_mode, it must:
     //     1. not be NATIVE color mode, NATIVE color mode means no forced color mode;
@@ -1133,17 +1201,36 @@
     /* ------------------------------------------------------------------------
      * Scheduler
      */
-    bool mUseSmart90ForVideo = false;
     std::unique_ptr<Scheduler> mScheduler;
-    sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
-    sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
+    scheduler::ConnectionHandle mAppConnectionHandle;
+    scheduler::ConnectionHandle mSfConnectionHandle;
+
+    // Stores phase offsets configured per refresh rate.
+    std::unique_ptr<scheduler::PhaseConfiguration> mPhaseConfiguration;
+
+    // Optional to defer construction until scheduler connections are created.
+    std::optional<scheduler::VSyncModulator> mVSyncModulator;
 
     std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
     std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
 
-    // All configs are allowed if the set is empty.
-    using DisplayConfigs = std::set<int32_t>;
-    DisplayConfigs mAllowedDisplayConfigs GUARDED_BY(mStateLock);
+    std::atomic<nsecs_t> mExpectedPresentTime = 0;
+    hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE;
+
+    /* ------------------------------------------------------------------------
+     * Generic Layer Metadata
+     */
+    const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const;
+
+    /* ------------------------------------------------------------------------
+     * 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
@@ -1154,13 +1241,17 @@
     ActiveConfigInfo mDesiredActiveConfig GUARDED_BY(mActiveConfigLock);
 
     // below flags are set by main thread only
-    bool mDesiredActiveConfigChanged GUARDED_BY(mActiveConfigLock) = false;
-    bool mCheckPendingFence = false;
+    TracedOrdinal<bool> mDesiredActiveConfigChanged
+            GUARDED_BY(mActiveConfigLock) = {"DesiredActiveConfigChanged", false};
+    bool mSetActiveConfigPending = false;
 
     bool mLumaSampling = true;
     sp<RegionSamplingThread> mRegionSamplingThread;
     ui::DisplayPrimaries mInternalDisplayPrimaries;
 
+    const float mInternalDisplayDensity;
+    const float mEmulatedDisplayDensity;
+
     sp<IInputFlinger> mInputFlinger;
     InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
     // Should only be accessed by the main thread.
@@ -1177,10 +1268,14 @@
 
     const sp<SetInputWindowsListener> mSetInputWindowsListener = new SetInputWindowsListener(this);
 
-    bool mPendingSyncInputWindows GUARDED_BY(mStateLock);
+    bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false;
     Hwc2::impl::PowerAdvisor mPowerAdvisor;
 
-    std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
+    // This should only be accessed on the main thread.
+    nsecs_t mFrameStartTime = 0;
+
+    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;
@@ -1191,11 +1286,16 @@
     // be any issues with a raw pointer referencing an invalid object.
     std::unordered_set<Layer*> mOffscreenLayers;
 
-    // Flags to capture the state of Vsync in HWC
-    HWC2::Vsync mHWCVsyncState = HWC2::Vsync::Disable;
-    HWC2::Vsync mHWCVsyncPendingState = HWC2::Vsync::Disable;
+    // Fields tracking the current jank event: when it started and how many
+    // janky frames there are.
+    nsecs_t mMissedFrameJankStart = 0;
+    int32_t mMissedFrameJankCount = 0;
+    // Positive if jank should be uploaded in postComposition
+    nsecs_t mLastJankDuration = -1;
 
-    nsecs_t mExpectedPresentTime;
+    int mFrameRateFlexibilityTokenCount = 0;
+
+    sp<IBinder> mDebugFrameRateFlexibilityToken;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
new file mode 100644
index 0000000..ddd20a5
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <compositionengine/impl/CompositionEngine.h>
+#include <cutils/properties.h>
+#include <ui/GraphicBuffer.h>
+
+#include "BufferLayerConsumer.h"
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "ContainerLayer.h"
+#include "DisplayDevice.h"
+#include "EffectLayer.h"
+#include "Layer.h"
+#include "MonitoredProducer.h"
+#include "NativeWindowSurface.h"
+#include "StartPropertySetThread.h"
+#include "SurfaceFlingerDefaultFactory.h"
+#include "SurfaceFlingerProperties.h"
+#include "SurfaceInterceptor.h"
+
+#include "DisplayHardware/ComposerHal.h"
+#include "Scheduler/DispSync.h"
+#include "Scheduler/EventControlThread.h"
+#include "Scheduler/MessageQueue.h"
+#include "Scheduler/PhaseOffsets.h"
+#include "Scheduler/Scheduler.h"
+
+namespace android::surfaceflinger {
+
+DefaultFactory::~DefaultFactory() = default;
+
+std::unique_ptr<DispSync> DefaultFactory::createDispSync(const char* name, bool hasSyncFramework) {
+    return std::make_unique<android::impl::DispSync>(name, hasSyncFramework);
+}
+
+std::unique_ptr<EventControlThread> DefaultFactory::createEventControlThread(
+        SetVSyncEnabled setVSyncEnabled) {
+    return std::make_unique<android::impl::EventControlThread>(std::move(setVSyncEnabled));
+}
+
+std::unique_ptr<HWComposer> DefaultFactory::createHWComposer(const std::string& serviceName) {
+    return std::make_unique<android::impl::HWComposer>(serviceName);
+}
+
+std::unique_ptr<MessageQueue> DefaultFactory::createMessageQueue() {
+    return std::make_unique<android::impl::MessageQueue>();
+}
+
+std::unique_ptr<scheduler::PhaseConfiguration> DefaultFactory::createPhaseConfiguration(
+        const scheduler::RefreshRateConfigs& refreshRateConfigs) {
+    if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
+        return std::make_unique<scheduler::impl::PhaseDurations>(refreshRateConfigs);
+    } else {
+        return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs);
+    }
+}
+
+std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
+        SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs,
+        ISchedulerCallback& schedulerCallback) {
+    return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback,
+                                       property_get_bool("debug.sf.use_content_detection_v2", true),
+                                       sysprop::use_content_detection_for_refresh_rate(false));
+}
+
+std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
+        SurfaceFlinger* flinger) {
+    return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
+}
+
+sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread(
+        bool timestampPropertyValue) {
+    return new StartPropertySetThread(timestampPropertyValue);
+}
+
+sp<DisplayDevice> DefaultFactory::createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) {
+    return new DisplayDevice(creationArgs);
+}
+
+sp<GraphicBuffer> DefaultFactory::createGraphicBuffer(uint32_t width, uint32_t height,
+                                                      PixelFormat format, uint32_t layerCount,
+                                                      uint64_t usage, std::string requestorName) {
+    return new GraphicBuffer(width, height, format, layerCount, usage, requestorName);
+}
+
+void DefaultFactory::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+                                       sp<IGraphicBufferConsumer>* outConsumer,
+                                       bool consumerIsSurfaceFlinger) {
+    BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
+}
+
+sp<IGraphicBufferProducer> DefaultFactory::createMonitoredProducer(
+        const sp<IGraphicBufferProducer>& producer, const sp<SurfaceFlinger>& flinger,
+        const wp<Layer>& layer) {
+    return new MonitoredProducer(producer, flinger, layer);
+}
+
+sp<BufferLayerConsumer> DefaultFactory::createBufferLayerConsumer(
+        const sp<IGraphicBufferConsumer>& consumer, renderengine::RenderEngine& renderEngine,
+        uint32_t textureName, Layer* layer) {
+    return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
+}
+
+std::unique_ptr<surfaceflinger::NativeWindowSurface> DefaultFactory::createNativeWindowSurface(
+        const sp<IGraphicBufferProducer>& producer) {
+    return surfaceflinger::impl::createNativeWindowSurface(producer);
+}
+
+std::unique_ptr<compositionengine::CompositionEngine> DefaultFactory::createCompositionEngine() {
+    return compositionengine::impl::createCompositionEngine();
+}
+
+sp<ContainerLayer> DefaultFactory::createContainerLayer(const LayerCreationArgs& args) {
+    return new ContainerLayer(args);
+}
+
+sp<BufferQueueLayer> DefaultFactory::createBufferQueueLayer(const LayerCreationArgs& args) {
+    return new BufferQueueLayer(args);
+}
+
+sp<BufferStateLayer> DefaultFactory::createBufferStateLayer(const LayerCreationArgs& args) {
+    return new BufferStateLayer(args);
+}
+
+sp<EffectLayer> DefaultFactory::createEffectLayer(const LayerCreationArgs& args) {
+    return new EffectLayer(args);
+}
+
+} // namespace android::surfaceflinger
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
new file mode 100644
index 0000000..bd40cfb
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "SurfaceFlingerFactory.h"
+
+namespace android::surfaceflinger {
+
+// A default implementation of the factory which creates the standard
+// implementation types for each interface.
+class DefaultFactory : public surfaceflinger::Factory {
+public:
+    virtual ~DefaultFactory();
+
+    std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) override;
+    std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) override;
+    std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
+    std::unique_ptr<MessageQueue> createMessageQueue() override;
+    std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+            const scheduler::RefreshRateConfigs&) override;
+    std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
+                                               const scheduler::RefreshRateConfigs&,
+                                               ISchedulerCallback&) override;
+    std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) override;
+    sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
+    sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
+    sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
+                                          uint32_t layerCount, uint64_t usage,
+                                          std::string requestorName) override;
+    void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+                           sp<IGraphicBufferConsumer>* outConsumer,
+                           bool consumerIsSurfaceFlinger) override;
+    sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&,
+                                                       const sp<SurfaceFlinger>&,
+                                                       const wp<Layer>&) override;
+    sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&,
+                                                      renderengine::RenderEngine&, uint32_t tex,
+                                                      Layer*) override;
+    std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
+            const sp<IGraphicBufferProducer>&) override;
+    std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override;
+    sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) override;
+    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override;
+    sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) override;
+    sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override;
+};
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
index e425b2a..3997b04 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -14,129 +14,22 @@
  * limitations under the License.
  */
 
-#include <compositionengine/impl/CompositionEngine.h>
-#include <ui/GraphicBuffer.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 
-#include "BufferQueueLayer.h"
-#include "BufferStateLayer.h"
-#include "ColorLayer.h"
-#include "ContainerLayer.h"
-#include "DisplayDevice.h"
-#include "Layer.h"
-#include "NativeWindowSurface.h"
-#include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
-#include "SurfaceFlingerFactory.h"
-#include "SurfaceInterceptor.h"
-
-#include "DisplayHardware/ComposerHal.h"
-#include "Scheduler/DispSync.h"
-#include "Scheduler/EventControlThread.h"
-#include "Scheduler/MessageQueue.h"
-#include "Scheduler/PhaseOffsets.h"
-#include "Scheduler/Scheduler.h"
-#include "TimeStats/TimeStats.h"
+#include "SurfaceFlingerDefaultFactory.h"
 
 namespace android::surfaceflinger {
 
 sp<SurfaceFlinger> createSurfaceFlinger() {
-    class Factory final : public surfaceflinger::Factory {
-    public:
-        Factory() = default;
-        ~Factory() = default;
-
-        std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework,
-                                                 int64_t dispSyncPresentTimeOffset) override {
-            // Note: We create a local temporary with the real DispSync implementation
-            // type temporarily so we can initialize it with the configured values,
-            // before storing it for more generic use using the interface type.
-            auto primaryDispSync = std::make_unique<android::impl::DispSync>(name);
-            primaryDispSync->init(hasSyncFramework, dispSyncPresentTimeOffset);
-            return primaryDispSync;
-        }
-
-        std::unique_ptr<EventControlThread> createEventControlThread(
-                std::function<void(bool)> setVSyncEnabled) override {
-            return std::make_unique<android::impl::EventControlThread>(setVSyncEnabled);
-        }
-
-        std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override {
-            return std::make_unique<android::impl::HWComposer>(
-                    std::make_unique<Hwc2::impl::Composer>(serviceName));
-        }
-
-        std::unique_ptr<MessageQueue> createMessageQueue() override {
-            return std::make_unique<android::impl::MessageQueue>();
-        }
-
-        std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
-            return std::make_unique<scheduler::impl::PhaseOffsets>();
-        }
-
-        std::unique_ptr<Scheduler> createScheduler(
-                std::function<void(bool)> callback,
-                const scheduler::RefreshRateConfigs& refreshRateConfig) override {
-            return std::make_unique<Scheduler>(callback, refreshRateConfig);
-        }
-
-        std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(
-                SurfaceFlinger* flinger) override {
-            return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
-        }
-
-        sp<StartPropertySetThread> createStartPropertySetThread(
-                bool timestampPropertyValue) override {
-            return new StartPropertySetThread(timestampPropertyValue);
-        }
-
-        sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) override {
-            return new DisplayDevice(std::move(creationArgs));
-        }
-
-        sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
-                                              uint32_t layerCount, uint64_t usage,
-                                              std::string requestorName) override {
-            return new GraphicBuffer(width, height, format, layerCount, usage, requestorName);
-        }
-
-        void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
-                               sp<IGraphicBufferConsumer>* outConsumer,
-                               bool consumerIsSurfaceFlinger) override {
-            BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
-        }
-
-        std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
-                const sp<IGraphicBufferProducer>& producer) override {
-            return surfaceflinger::impl::createNativeWindowSurface(producer);
-        }
-
-        std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override {
-            return compositionengine::impl::createCompositionEngine();
-        }
-
-        sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override {
-            return new ContainerLayer(args);
-        }
-
-        sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) override {
-            return new BufferQueueLayer(args);
-        }
-
-        sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override {
-            return new BufferStateLayer(args);
-        }
-
-        sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) override {
-            return new ColorLayer(args);
-        }
-
-        std::shared_ptr<TimeStats> createTimeStats() override {
-            return std::make_shared<android::impl::TimeStats>();
-        }
-    };
-    static Factory factory;
+    static DefaultFactory factory;
 
     return new SurfaceFlinger(factory);
 }
 
 } // namespace android::surfaceflinger
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index c2bc808..6f4fcc6 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -16,21 +16,22 @@
 
 #pragma once
 
+#include <cutils/compiler.h>
+#include <utils/StrongPointer.h>
+
 #include <cinttypes>
 #include <functional>
 #include <memory>
 #include <string>
 
-#include <cutils/compiler.h>
-#include <utils/StrongPointer.h>
-
 namespace android {
 
 typedef int32_t PixelFormat;
 
 class BufferQueueLayer;
 class BufferStateLayer;
-class ColorLayer;
+class BufferLayerConsumer;
+class EffectLayer;
 class ContainerLayer;
 class DisplayDevice;
 class DispSync;
@@ -39,12 +40,13 @@
 class HWComposer;
 class IGraphicBufferConsumer;
 class IGraphicBufferProducer;
+class ISchedulerCallback;
+class Layer;
 class MessageQueue;
 class Scheduler;
 class StartPropertySetThread;
 class SurfaceFlinger;
 class SurfaceInterceptor;
-class TimeStats;
 
 struct DisplayDeviceCreationArgs;
 struct LayerCreationArgs;
@@ -54,8 +56,10 @@
 } // namespace compositionengine
 
 namespace scheduler {
-class PhaseOffsets;
+class PhaseConfiguration;
+class RefreshRateConfigs;
 } // namespace scheduler
+
 namespace surfaceflinger {
 
 class NativeWindowSurface;
@@ -64,27 +68,35 @@
 // of each interface.
 class Factory {
 public:
-    virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework,
-                                                     int64_t dispSyncPresentTimeOffset) = 0;
-    virtual std::unique_ptr<EventControlThread> createEventControlThread(
-            std::function<void(bool)> setVSyncEnabled) = 0;
+    using SetVSyncEnabled = std::function<void(bool)>;
+
+    virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) = 0;
+    virtual std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) = 0;
     virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
     virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
-    virtual std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() = 0;
-    virtual std::unique_ptr<Scheduler> createScheduler(
-            std::function<void(bool)> callback,
-            const scheduler::RefreshRateConfigs& refreshRateConfig) = 0;
+    virtual std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+            const scheduler::RefreshRateConfigs&) = 0;
+    virtual std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
+                                                       const scheduler::RefreshRateConfigs&,
+                                                       ISchedulerCallback&) = 0;
     virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
 
     virtual sp<StartPropertySetThread> createStartPropertySetThread(
             bool timestampPropertyValue) = 0;
-    virtual sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) = 0;
+    virtual sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) = 0;
     virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height,
                                                   PixelFormat format, uint32_t layerCount,
                                                   uint64_t usage, std::string requestorName) = 0;
     virtual void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
                                    sp<IGraphicBufferConsumer>* outConsumer,
                                    bool consumerIsSurfaceFlinger) = 0;
+    virtual sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&,
+                                                               const sp<SurfaceFlinger>&,
+                                                               const wp<Layer>&) = 0;
+    virtual sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&,
+                                                              renderengine::RenderEngine&,
+                                                              uint32_t tex, Layer*) = 0;
+
     virtual std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer>&) = 0;
 
@@ -92,11 +104,9 @@
 
     virtual sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) = 0;
     virtual sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
-    virtual sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) = 0;
+    virtual sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) = 0;
     virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
 
-    virtual std::shared_ptr<TimeStats> createTimeStats() = 0;
-
 protected:
     ~Factory() = default;
 };
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index b4716eb..9d78702 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -18,7 +18,9 @@
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/types.h>
 #include <configstore/Utils.h>
+#include <utils/Log.h>
 
+#include <log/log.h>
 #include <cstdlib>
 #include <tuple>
 
@@ -68,6 +70,22 @@
             defaultValue);
 }
 
+int32_t max_graphics_width(int32_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::max_graphics_width();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return defaultValue;
+}
+
+int32_t max_graphics_height(int32_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::max_graphics_height();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return defaultValue;
+}
+
 bool has_wide_color_display(bool defaultValue) {
     auto temp = SurfaceFlingerProperties::has_wide_color_display();
     if (temp.has_value()) {
@@ -227,8 +245,12 @@
 }
 
 bool refresh_rate_switching(bool defaultValue) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
     auto temp = SurfaceFlingerProperties::refresh_rate_switching();
+#pragma clang diagnostic pop
     if (temp.has_value()) {
+        ALOGW("Using deprecated refresh_rate_switching sysprop. Value: %d", *temp);
         return *temp;
     }
     return defaultValue;
@@ -258,8 +280,17 @@
     return defaultValue;
 }
 
-bool use_smart_90_for_video(bool defaultValue) {
-    auto temp = SurfaceFlingerProperties::use_smart_90_for_video();
+bool use_content_detection_for_refresh_rate(bool defaultValue) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    auto smart_90_deprecated = SurfaceFlingerProperties::use_smart_90_for_video();
+#pragma clang diagnostic pop
+    if (smart_90_deprecated.has_value()) {
+        ALOGW("Using deprecated use_smart_90_for_video sysprop. Value: %d", *smart_90_deprecated);
+        return *smart_90_deprecated;
+    }
+
+    auto temp = SurfaceFlingerProperties::use_content_detection_for_refresh_rate();
     if (temp.has_value()) {
         return *temp;
     }
@@ -282,6 +313,22 @@
     return defaultValue;
 }
 
+bool use_frame_rate_api(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::use_frame_rate_api();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return defaultValue;
+}
+
+int32_t display_update_imminent_timeout_ms(int32_t defaultValue) {
+    auto temp = SurfaceFlingerProperties::display_update_imminent_timeout_ms();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return defaultValue;
+}
+
 #define DISPLAY_PRIMARY_SIZE 3
 
 constexpr float kSrgbRedX = 0.4123f;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index e394cca..c63adfe 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -37,6 +37,9 @@
 
 int64_t max_frame_buffer_acquired_buffers(int64_t defaultValue);
 
+int32_t max_graphics_width(int32_t defaultValue);
+int32_t max_graphics_height(int32_t defaultValue);
+
 bool has_wide_color_display(bool defaultValue);
 
 bool running_without_sync_framework(bool defaultValue);
@@ -81,12 +84,16 @@
 
 int32_t set_display_power_timer_ms(int32_t defaultValue);
 
-bool use_smart_90_for_video(bool defaultValue);
+bool use_content_detection_for_refresh_rate(bool defaultValue);
 
 bool enable_protected_contents(bool defaultValue);
 
 bool support_kernel_idle_timer(bool defaultValue);
 
+bool use_frame_rate_api(bool defaultValue);
+
+int32_t display_update_imminent_timeout_ms(int32_t defaultValue);
+
 android::ui::DisplayPrimaries getDisplayNativePrimaries();
 } // namespace sysprop
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 7bfe033..80102bd 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -13,6 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 #undef LOG_TAG
 #define LOG_TAG "SurfaceInterceptor"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -110,13 +114,22 @@
     addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
     addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
     addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
+    addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius);
     if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
         addDeferTransactionLocked(transaction, layerId,
                                   layer->mCurrentState.barrierLayer_legacy.promote(),
                                   layer->mCurrentState.frameNumber_legacy);
     }
     addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode());
-    addFlagsLocked(transaction, layerId, layer->mCurrentState.flags);
+    addFlagsLocked(transaction, layerId, layer->mCurrentState.flags,
+                   layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque |
+                           layer_state_t::eLayerSecure);
+    addReparentLocked(transaction, layerId, getLayerIdFromWeakRef(layer->mCurrentParent));
+    addDetachChildrenLocked(transaction, layerId, layer->isLayerDetached());
+    addRelativeParentLocked(transaction, layerId,
+                            getLayerIdFromWeakRef(layer->mCurrentState.zOrderRelativeOf),
+                            layer->mCurrentState.z);
+    addShadowRadiusLocked(transaction, layerId, layer->mCurrentState.shadowRadius);
 }
 
 void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment,
@@ -129,8 +142,8 @@
     addDisplaySurfaceLocked(transaction, display.sequenceId, display.surface);
     addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack);
     addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height);
-    addDisplayProjectionLocked(transaction, display.sequenceId, display.orientation,
-            display.viewport, display.frame);
+    addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation),
+                               display.viewport, display.frame);
 }
 
 status_t SurfaceInterceptor::writeProtoFileLocked() {
@@ -150,7 +163,7 @@
     return NO_ERROR;
 }
 
-const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) {
+const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) const {
     const sp<const IBinder>& handle(weakHandle.promote());
     const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
     const sp<const Layer> layer(layerHandle->owner.promote());
@@ -158,17 +171,30 @@
     return layer;
 }
 
-const std::string SurfaceInterceptor::getLayerName(const sp<const Layer>& layer) {
-    return layer->getName().string();
+int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) const {
+    return layer->sequence;
 }
 
-int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) {
-    return layer->sequence;
+int32_t SurfaceInterceptor::getLayerIdFromWeakRef(const wp<const Layer>& layer) const {
+    if (layer == nullptr) {
+        return -1;
+    }
+    auto strongLayer = layer.promote();
+    return strongLayer == nullptr ? -1 : getLayerId(strongLayer);
+}
+
+int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<const IBinder>& handle) const {
+    if (handle == nullptr) {
+        return -1;
+    }
+    const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
+    const sp<const Layer> layer(layerHandle->owner.promote());
+    return layer == nullptr ? -1 : getLayerId(layer);
 }
 
 Increment* SurfaceInterceptor::createTraceIncrementLocked() {
     Increment* increment(mTrace.add_increment());
-    increment->set_time_stamp(systemTime());
+    increment->set_time_stamp(elapsedRealtimeNano());
     return increment;
 }
 
@@ -252,24 +278,23 @@
     }
 }
 
-void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId,
-        uint8_t flags)
-{
+void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags,
+                                        uint8_t mask) {
     // There can be multiple flags changed
-    if (flags & layer_state_t::eLayerHidden) {
+    if (mask & layer_state_t::eLayerHidden) {
         SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
         HiddenFlagChange* flagChange(change->mutable_hidden_flag());
-        flagChange->set_hidden_flag(true);
+        flagChange->set_hidden_flag(flags & layer_state_t::eLayerHidden);
     }
-    if (flags & layer_state_t::eLayerOpaque) {
+    if (mask & layer_state_t::eLayerOpaque) {
         SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
         OpaqueFlagChange* flagChange(change->mutable_opaque_flag());
-        flagChange->set_opaque_flag(true);
+        flagChange->set_opaque_flag(flags & layer_state_t::eLayerOpaque);
     }
-    if (flags & layer_state_t::eLayerSecure) {
+    if (mask & layer_state_t::eLayerSecure) {
         SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
         SecureFlagChange* flagChange(change->mutable_secure_flag());
-        flagChange->set_secure_flag(true);
+        flagChange->set_secure_flag(flags & layer_state_t::eLayerSecure);
     }
 }
 
@@ -298,6 +323,13 @@
     cornerRadiusChange->set_corner_radius(cornerRadius);
 }
 
+void SurfaceInterceptor::addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
+                                                       int32_t backgroundBlurRadius) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    BackgroundBlurRadiusChange* blurRadiusChange(change->mutable_background_blur_radius());
+    blurRadiusChange->set_background_blur_radius(backgroundBlurRadius);
+}
+
 void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
         const sp<const Layer>& layer, uint64_t frameNumber)
 {
@@ -320,6 +352,42 @@
     overrideChange->set_override_scaling_mode(overrideScalingMode);
 }
 
+void SurfaceInterceptor::addReparentLocked(Transaction* transaction, int32_t layerId,
+                                           int32_t parentId) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    ReparentChange* overrideChange(change->mutable_reparent());
+    overrideChange->set_parent_id(parentId);
+}
+
+void SurfaceInterceptor::addReparentChildrenLocked(Transaction* transaction, int32_t layerId,
+                                                   int32_t parentId) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    ReparentChildrenChange* overrideChange(change->mutable_reparent_children());
+    overrideChange->set_parent_id(parentId);
+}
+
+void SurfaceInterceptor::addDetachChildrenLocked(Transaction* transaction, int32_t layerId,
+                                                 bool detached) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    DetachChildrenChange* overrideChange(change->mutable_detach_children());
+    overrideChange->set_detach_children(detached);
+}
+
+void SurfaceInterceptor::addRelativeParentLocked(Transaction* transaction, int32_t layerId,
+                                                 int32_t parentId, int z) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    RelativeParentChange* overrideChange(change->mutable_relative_parent());
+    overrideChange->set_relative_parent_id(parentId);
+    overrideChange->set_z(z);
+}
+
+void SurfaceInterceptor::addShadowRadiusLocked(Transaction* transaction, int32_t layerId,
+                                               float shadowRadius) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    ShadowRadiusChange* overrideChange(change->mutable_shadow_radius());
+    overrideChange->set_radius(shadowRadius);
+}
+
 void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction,
         const layer_state_t& state)
 {
@@ -351,7 +419,7 @@
         addTransparentRegionLocked(transaction, layerId, state.transparentRegion);
     }
     if (state.what & layer_state_t::eFlagsChanged) {
-        addFlagsLocked(transaction, layerId, state.flags);
+        addFlagsLocked(transaction, layerId, state.flags, state.mask);
     }
     if (state.what & layer_state_t::eLayerStackChanged) {
         addLayerStackLocked(transaction, layerId, state.layerStack);
@@ -362,6 +430,9 @@
     if (state.what & layer_state_t::eCornerRadiusChanged) {
         addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
     }
+    if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) {
+        addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius);
+    }
     if (state.what & layer_state_t::eDeferTransaction_legacy) {
         sp<Layer> otherLayer = nullptr;
         if (state.barrierHandle_legacy != nullptr) {
@@ -380,6 +451,22 @@
     if (state.what & layer_state_t::eOverrideScalingModeChanged) {
         addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode);
     }
+    if (state.what & layer_state_t::eReparent) {
+        addReparentLocked(transaction, layerId, getLayerIdFromHandle(state.parentHandleForChild));
+    }
+    if (state.what & layer_state_t::eReparentChildren) {
+        addReparentChildrenLocked(transaction, layerId, getLayerIdFromHandle(state.reparentHandle));
+    }
+    if (state.what & layer_state_t::eDetachChildren) {
+        addDetachChildrenLocked(transaction, layerId, true);
+    }
+    if (state.what & layer_state_t::eRelativeLayerChanged) {
+        addRelativeParentLocked(transaction, layerId,
+                                getLayerIdFromHandle(state.relativeLayerHandle), state.z);
+    }
+    if (state.what & layer_state_t::eShadowRadiusChanged) {
+        addShadowRadiusLocked(transaction, layerId, state.shadowRadius);
+    }
 }
 
 void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
@@ -395,8 +482,8 @@
         addDisplaySizeLocked(transaction, sequenceId, state.width, state.height);
     }
     if (state.what & DisplayState::eDisplayProjectionChanged) {
-        addDisplayProjectionLocked(transaction, sequenceId, state.orientation, state.viewport,
-                state.frame);
+        addDisplayProjectionLocked(transaction, sequenceId, toRotationInt(state.orientation),
+                                   state.viewport, state.frame);
     }
 }
 
@@ -425,7 +512,7 @@
 {
     SurfaceCreation* creation(increment->mutable_surface_creation());
     creation->set_id(getLayerId(layer));
-    creation->set_name(getLayerName(layer));
+    creation->set_name(layer->getName());
     creation->set_w(layer->mCurrentState.active_legacy.w);
     creation->set_h(layer->mCurrentState.active_legacy.h);
 }
@@ -437,11 +524,11 @@
     deletion->set_id(getLayerId(layer));
 }
 
-void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, const sp<const Layer>& layer,
+void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, int32_t layerId,
         uint32_t width, uint32_t height, uint64_t frameNumber)
 {
     BufferUpdate* update(increment->mutable_buffer_update());
-    update->set_id(getLayerId(layer));
+    update->set_id(layerId);
     update->set_w(width);
     update->set_h(height);
     update->set_frame_number(frameNumber);
@@ -508,8 +595,8 @@
     creation->set_id(info.sequenceId);
     creation->set_name(info.displayName);
     creation->set_is_secure(info.isSecure);
-    if (info.displayId) {
-        creation->set_display_id(info.displayId->value);
+    if (info.physical) {
+        creation->set_display_id(info.physical->id.value);
     }
 }
 
@@ -557,15 +644,22 @@
     addSurfaceDeletionLocked(createTraceIncrementLocked(), layer);
 }
 
-void SurfaceInterceptor::saveBufferUpdate(const sp<const Layer>& layer, uint32_t width,
+/**
+ * Here we pass the layer by ID instead of by sp<> since this is called without
+ * holding the state-lock from a Binder thread. If we required the caller
+ * to pass 'this' by sp<> the temporary sp<> constructed could end up
+ * being the last reference and we might accidentally destroy the Layer
+ * from this binder thread.
+ */
+void SurfaceInterceptor::saveBufferUpdate(int32_t layerId, uint32_t width,
         uint32_t height, uint64_t frameNumber)
 {
-    if (!mEnabled || layer == nullptr) {
+    if (!mEnabled) {
         return;
     }
     ATRACE_CALL();
     std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-    addBufferUpdateLocked(createTraceIncrementLocked(), layer, width, height, frameNumber);
+    addBufferUpdateLocked(createTraceIncrementLocked(), layerId, width, height, frameNumber);
 }
 
 void SurfaceInterceptor::saveVSyncEvent(nsecs_t timestamp) {
@@ -605,3 +699,6 @@
 
 } // namespace impl
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 563a44c..896bdcc 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -39,8 +39,14 @@
 struct DisplayDeviceState;
 struct DisplayState;
 struct layer_state_t;
+using Transaction = surfaceflinger::Transaction;
+using Trace = surfaceflinger::Trace;
+using Rectangle = surfaceflinger::Rectangle;
+using SurfaceChange = surfaceflinger::SurfaceChange;
+using Increment = surfaceflinger::Increment;
+using DisplayChange = surfaceflinger::DisplayChange;
 
-constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
+constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb";
 
 class SurfaceInterceptor {
 public:
@@ -61,7 +67,7 @@
     // Intercept surface data
     virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0;
     virtual void saveSurfaceDeletion(const sp<const Layer>& layer) = 0;
-    virtual void saveBufferUpdate(const sp<const Layer>& layer, uint32_t width, uint32_t height,
+    virtual void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
                                   uint64_t frameNumber) = 0;
 
     // Intercept display data
@@ -96,7 +102,7 @@
     // Intercept surface data
     void saveSurfaceCreation(const sp<const Layer>& layer) override;
     void saveSurfaceDeletion(const sp<const Layer>& layer) override;
-    void saveBufferUpdate(const sp<const Layer>& layer, uint32_t width, uint32_t height,
+    void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
                           uint64_t frameNumber) override;
 
     // Intercept display data
@@ -116,14 +122,15 @@
     void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display);
 
     status_t writeProtoFileLocked();
-    const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle);
-    const std::string getLayerName(const sp<const Layer>& layer);
-    int32_t getLayerId(const sp<const Layer>& layer);
+    const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle) const;
+    int32_t getLayerId(const sp<const Layer>& layer) const;
+    int32_t getLayerIdFromWeakRef(const wp<const Layer>& layer) const;
+    int32_t getLayerIdFromHandle(const sp<const IBinder>& weakHandle) const;
 
     Increment* createTraceIncrementLocked();
     void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
     void addSurfaceDeletionLocked(Increment* increment, const sp<const Layer>& layer);
-    void addBufferUpdateLocked(Increment* increment, const sp<const Layer>& layer, uint32_t width,
+    void addBufferUpdateLocked(Increment* increment, int32_t layerId, uint32_t width,
             uint32_t height, uint64_t frameNumber);
     void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp);
     void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info);
@@ -141,10 +148,12 @@
             const layer_state_t::matrix22_t& matrix);
     void addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
             const Region& transRegion);
-    void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags);
+    void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags, uint8_t mask);
     void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack);
     void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect);
     void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius);
+    void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
+                                       int32_t backgroundBlurRadius);
     void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
             const sp<const Layer>& layer, uint64_t frameNumber);
     void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
@@ -153,6 +162,12 @@
     void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
             const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
             const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags);
+    void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
+    void addReparentChildrenLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
+    void addDetachChildrenLocked(Transaction* transaction, int32_t layerId, bool detached);
+    void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId,
+                                 int z);
+    void addShadowRadiusLocked(Transaction* transaction, int32_t layerId, float shadowRadius);
 
     // Add display transactions to the trace
     DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId);
@@ -176,6 +191,7 @@
 };
 
 } // namespace impl
+
 } // namespace android
 
 #endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index 5d9be0b..d84ce69 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -13,6 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 #undef LOG_TAG
 #define LOG_TAG "SurfaceTracing"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -29,24 +33,23 @@
 namespace android {
 
 SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger)
-      : mFlinger(flinger), mSfLock(flinger.mDrawingStateLock) {}
+      : mFlinger(flinger), mSfLock(flinger.mTracingLock) {}
 
 void SurfaceTracing::mainLoop() {
-    addFirstEntry();
-    bool enabled = true;
+    bool enabled = addFirstEntry();
     while (enabled) {
         LayersTraceProto entry = traceWhenNotified();
         enabled = addTraceToBuffer(entry);
     }
 }
 
-void SurfaceTracing::addFirstEntry() {
+bool SurfaceTracing::addFirstEntry() {
     LayersTraceProto entry;
     {
         std::scoped_lock lock(mSfLock);
         entry = traceLayersLocked("tracing.enable");
     }
-    addTraceToBuffer(entry);
+    return addTraceToBuffer(entry);
 }
 
 LayersTraceProto SurfaceTracing::traceWhenNotified() {
@@ -54,6 +57,8 @@
     mCanStartTrace.wait(lock);
     android::base::ScopedLockAssertion assumeLock(mSfLock);
     LayersTraceProto entry = traceLayersLocked(mWhere);
+    mTracingInProgress = false;
+    mMissedTraceEntries = 0;
     lock.unlock();
     return entry;
 }
@@ -68,10 +73,17 @@
     return mEnabled;
 }
 
-void SurfaceTracing::notify(long compositionTime, const char* where) {
+void SurfaceTracing::notify(const char* where) {
     std::scoped_lock lock(mSfLock);
-    mCompositionTime = compositionTime;
+    notifyLocked(where);
+}
+
+void SurfaceTracing::notifyLocked(const char* where) {
     mWhere = where;
+    if (mTracingInProgress) {
+        mMissedTraceEntries++;
+    }
+    mTracingInProgress = true;
     mCanStartTrace.notify_one();
 }
 
@@ -112,19 +124,26 @@
     }
 }
 
-void SurfaceTracing::enable() {
+bool SurfaceTracing::enable() {
     std::scoped_lock lock(mTraceLock);
 
     if (mEnabled) {
-        return;
+        return false;
     }
+
     mBuffer.reset(mBufferSize);
     mEnabled = true;
     mThread = std::thread(&SurfaceTracing::mainLoop, this);
+    return true;
 }
 
 status_t SurfaceTracing::writeToFile() {
-    mThread.join();
+    std::thread thread;
+    {
+        std::scoped_lock lock(mTraceLock);
+        thread = std::move(mThread);
+    }
+    thread.join();
     return mLastErr;
 }
 
@@ -161,11 +180,25 @@
     ATRACE_CALL();
 
     LayersTraceProto entry;
-    entry.set_elapsed_realtime_nanos(mCompositionTime);
+    entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
     entry.set_where(where);
     LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags));
+
+    if (flagIsSetLocked(SurfaceTracing::TRACE_EXTRA)) {
+        mFlinger.dumpOffscreenLayersProto(layers);
+    }
     entry.mutable_layers()->Swap(&layers);
 
+    if (mTraceFlags & SurfaceTracing::TRACE_HWC) {
+        std::string hwcDump;
+        mFlinger.dumpHwc(hwcDump);
+        entry.set_hwc_blob(hwcDump);
+    }
+    if (!flagIsSetLocked(SurfaceTracing::TRACE_COMPOSITION)) {
+        entry.set_excludes_composition_state(true);
+    }
+    entry.set_missed_entries(mMissedTraceEntries);
+
     return entry;
 }
 
@@ -184,8 +217,11 @@
         ALOGE("Could not save the proto file! Permission denied");
         mLastErr = PERMISSION_DENIED;
     }
-    if (!android::base::WriteStringToFile(output, kDefaultFileName, S_IRWXU | S_IRGRP, getuid(),
-                                          getgid(), true)) {
+
+    // -rw-r--r--
+    const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+    if (!android::base::WriteStringToFile(output, kDefaultFileName, mode, getuid(), getgid(),
+                                          true)) {
         ALOGE("Could not save the proto file! There are missing fields");
         mLastErr = PERMISSION_DENIED;
     }
@@ -202,3 +238,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index 395d562..f208eb8 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -16,11 +16,11 @@
 
 #pragma once
 
+#include <android-base/thread_annotations.h>
 #include <layerproto/LayerProtoHeader.h>
 #include <utils/Errors.h>
 #include <utils/StrongPointer.h>
 
-#include <android-base/thread_annotations.h>
 #include <condition_variable>
 #include <memory>
 #include <mutex>
@@ -42,11 +42,12 @@
 class SurfaceTracing {
 public:
     explicit SurfaceTracing(SurfaceFlinger& flinger);
-    void enable();
+    bool enable();
     bool disable();
     status_t writeToFile();
     bool isEnabled() const;
-    void notify(long compositionTime, const char* where);
+    void notify(const char* where);
+    void notifyLocked(const char* where) NO_THREAD_SAFETY_ANALYSIS /* REQUIRES(mSfLock) */;
 
     void setBufferSize(size_t bufferSizeInByte);
     void writeToFileAsync();
@@ -55,13 +56,18 @@
     enum : uint32_t {
         TRACE_CRITICAL = 1 << 0,
         TRACE_INPUT = 1 << 1,
-        TRACE_EXTRA = 1 << 2,
+        TRACE_COMPOSITION = 1 << 2,
+        TRACE_EXTRA = 1 << 3,
+        TRACE_HWC = 1 << 4,
         TRACE_ALL = 0xffffffff
     };
     void setTraceFlags(uint32_t flags);
+    bool flagIsSetLocked(uint32_t flags) NO_THREAD_SAFETY_ANALYSIS /* REQUIRES(mSfLock) */ {
+        return (mTraceFlags & flags) == flags;
+    }
 
 private:
-    static constexpr auto kDefaultBufferCapInByte = 100_MB;
+    static constexpr auto kDefaultBufferCapInByte = 5_MB;
     static constexpr auto kDefaultFileName = "/data/misc/wmtrace/layers_trace.pb";
 
     class LayersTraceBuffer { // ring buffer
@@ -81,10 +87,8 @@
         std::queue<LayersTraceProto> mStorage;
     };
 
-    long mCompositionTime;
-
     void mainLoop();
-    void addFirstEntry();
+    bool addFirstEntry();
     LayersTraceProto traceWhenNotified();
     LayersTraceProto traceLayersLocked(const char* where) REQUIRES(mSfLock);
 
@@ -92,14 +96,16 @@
     bool addTraceToBuffer(LayersTraceProto& entry);
     void writeProtoFileLocked() REQUIRES(mTraceLock);
 
-    const SurfaceFlinger& mFlinger;
+    SurfaceFlinger& mFlinger;
     status_t mLastErr = NO_ERROR;
     std::thread mThread;
     std::condition_variable mCanStartTrace;
 
     std::mutex& mSfLock;
-    uint32_t mTraceFlags GUARDED_BY(mSfLock) = TRACE_ALL;
+    uint32_t mTraceFlags GUARDED_BY(mSfLock) = TRACE_CRITICAL | TRACE_INPUT;
     const char* mWhere GUARDED_BY(mSfLock) = "";
+    uint32_t mMissedTraceEntries GUARDED_BY(mSfLock) = 0;
+    bool mTracingInProgress GUARDED_BY(mSfLock) = false;
 
     mutable std::mutex mTraceLock;
     LayersTraceBuffer mBuffer GUARDED_BY(mTraceLock);
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
new file mode 100644
index 0000000..3901757
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -0,0 +1,36 @@
+cc_library_shared {
+    name: "libtimestats",
+    srcs: [
+        "TimeStats.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.graphics.composer@2.4",
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libprotobuf-cpp-lite",
+        "libprotoutil",
+        "libstatslog",
+        "libstatspull",
+        "libstatssocket",
+        "libtimestats_proto",
+        "libui",
+        "libutils",
+    ],
+    export_include_dirs: ["."],
+    export_shared_lib_headers: [
+        "libprotoutil",
+        "libstatslog",
+        "libstatspull",
+        "libstatssocket",
+        "libtimestats_proto",
+    ],
+    cppflags: [
+        "-Wall",
+        "-Werror",
+        "-Wformat",
+        "-Wthread-safety",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index c97a19b..37194c6 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -13,6 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 #undef LOG_TAG
 #define LOG_TAG "TimeStats"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -20,20 +24,182 @@
 #include "TimeStats.h"
 
 #include <android-base/stringprintf.h>
-
+#include <android/util/ProtoOutputStream.h>
 #include <log/log.h>
-
 #include <utils/String8.h>
 #include <utils/Timers.h>
 #include <utils/Trace.h>
 
 #include <algorithm>
-#include <regex>
+#include <chrono>
 
 namespace android {
 
 namespace impl {
 
+AStatsManager_PullAtomCallbackReturn TimeStats::pullAtomCallback(int32_t atom_tag,
+                                                                 AStatsEventList* data,
+                                                                 void* cookie) {
+    impl::TimeStats* timeStats = reinterpret_cast<impl::TimeStats*>(cookie);
+    AStatsManager_PullAtomCallbackReturn result = AStatsManager_PULL_SKIP;
+    if (atom_tag == android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) {
+        result = timeStats->populateGlobalAtom(data);
+    } else if (atom_tag == android::util::SURFACEFLINGER_STATS_LAYER_INFO) {
+        result = timeStats->populateLayerAtom(data);
+    }
+
+    // Enable timestats now. The first full pull for a given build is expected to
+    // have empty or very little stats, as stats are first enabled after the
+    // first pull is completed for either the global or layer stats.
+    timeStats->enable();
+    return result;
+}
+
+namespace {
+// Histograms align with the order of fields in SurfaceflingerStatsLayerInfo.
+const std::array<std::string, 6> kHistogramNames = {
+        "present2present", "post2present",    "acquire2present",
+        "latch2present",   "desired2present", "post2acquire",
+};
+
+std::string histogramToProtoByteString(const std::unordered_map<int32_t, int32_t>& histogram,
+                                       size_t maxPulledHistogramBuckets) {
+    auto buckets = std::vector<std::pair<int32_t, int32_t>>(histogram.begin(), histogram.end());
+    std::sort(buckets.begin(), buckets.end(),
+              [](std::pair<int32_t, int32_t>& left, std::pair<int32_t, int32_t>& right) {
+                  return left.second > right.second;
+              });
+
+    util::ProtoOutputStream proto;
+    int histogramSize = 0;
+    for (const auto& bucket : buckets) {
+        if (++histogramSize > maxPulledHistogramBuckets) {
+            break;
+        }
+        proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
+                            1 /* field id */,
+                    (int32_t)bucket.first);
+        proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+                            2 /* field id */,
+                    (int64_t)bucket.second);
+    }
+
+    std::string byteString;
+    proto.serializeToString(&byteString);
+    return byteString;
+}
+} // namespace
+
+AStatsManager_PullAtomCallbackReturn TimeStats::populateGlobalAtom(AStatsEventList* data) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    if (mTimeStats.statsStart == 0) {
+        return AStatsManager_PULL_SKIP;
+    }
+    flushPowerTimeLocked();
+
+    AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
+    mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+    mStatsDelegate->statsEventWriteInt64(event, mTimeStats.totalFrames);
+    mStatsDelegate->statsEventWriteInt64(event, mTimeStats.missedFrames);
+    mStatsDelegate->statsEventWriteInt64(event, mTimeStats.clientCompositionFrames);
+    mStatsDelegate->statsEventWriteInt64(event, mTimeStats.displayOnTime);
+    mStatsDelegate->statsEventWriteInt64(event, mTimeStats.presentToPresent.totalTime());
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.displayEventConnectionsCount);
+    std::string frameDurationBytes =
+            histogramToProtoByteString(mTimeStats.frameDuration.hist, mMaxPulledHistogramBuckets);
+    mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameDurationBytes.c_str(),
+                                             frameDurationBytes.size());
+    std::string renderEngineTimingBytes =
+            histogramToProtoByteString(mTimeStats.renderEngineTiming.hist,
+                                       mMaxPulledHistogramBuckets);
+    mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)renderEngineTimingBytes.c_str(),
+                                             renderEngineTimingBytes.size());
+    mStatsDelegate->statsEventBuild(event);
+    clearGlobalLocked();
+
+    return AStatsManager_PULL_SUCCESS;
+}
+
+AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventList* data) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    std::vector<TimeStatsHelper::TimeStatsLayer const*> dumpStats;
+    for (const auto& ele : mTimeStats.stats) {
+        dumpStats.push_back(&ele.second);
+    }
+
+    std::sort(dumpStats.begin(), dumpStats.end(),
+              [](TimeStatsHelper::TimeStatsLayer const* l,
+                 TimeStatsHelper::TimeStatsLayer const* r) {
+                  return l->totalFrames > r->totalFrames;
+              });
+
+    if (mMaxPulledLayers < dumpStats.size()) {
+        dumpStats.resize(mMaxPulledLayers);
+    }
+
+    for (const auto& layer : dumpStats) {
+        AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
+        mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_LAYER_INFO);
+        mStatsDelegate->statsEventWriteString8(event, layer->layerName.c_str());
+        mStatsDelegate->statsEventWriteInt64(event, layer->totalFrames);
+        mStatsDelegate->statsEventWriteInt64(event, layer->droppedFrames);
+
+        for (const auto& name : kHistogramNames) {
+            const auto& histogram = layer->deltas.find(name);
+            if (histogram == layer->deltas.cend()) {
+                mStatsDelegate->statsEventWriteByteArray(event, nullptr, 0);
+            } else {
+                std::string bytes = histogramToProtoByteString(histogram->second.hist,
+                                                               mMaxPulledHistogramBuckets);
+                mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)bytes.c_str(),
+                                                         bytes.size());
+            }
+        }
+
+        mStatsDelegate->statsEventWriteInt64(event, layer->lateAcquireFrames);
+        mStatsDelegate->statsEventWriteInt64(event, layer->badDesiredPresentFrames);
+
+        mStatsDelegate->statsEventBuild(event);
+    }
+    clearLayersLocked();
+
+    return AStatsManager_PULL_SUCCESS;
+}
+
+TimeStats::TimeStats() : TimeStats(nullptr, std::nullopt, std::nullopt) {}
+
+TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate,
+                     std::optional<size_t> maxPulledLayers,
+                     std::optional<size_t> maxPulledHistogramBuckets) {
+    if (statsDelegate != nullptr) {
+        mStatsDelegate = std::move(statsDelegate);
+    }
+
+    if (maxPulledLayers) {
+        mMaxPulledLayers = *maxPulledLayers;
+    }
+
+    if (maxPulledHistogramBuckets) {
+        mMaxPulledHistogramBuckets = *maxPulledHistogramBuckets;
+    }
+}
+
+TimeStats::~TimeStats() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+    mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
+}
+
+void TimeStats::onBootFinished() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                             nullptr, TimeStats::pullAtomCallback, this);
+    mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                             nullptr, TimeStats::pullAtomCallback, this);
+}
+
 void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
     ATRACE_CALL();
 
@@ -59,7 +225,7 @@
     }
 
     if (argsMap.count("-clear")) {
-        clear();
+        clearAll();
     }
 
     if (argsMap.count("-enable")) {
@@ -72,8 +238,10 @@
 
     std::string result = "TimeStats miniDump:\n";
     std::lock_guard<std::mutex> lock(mMutex);
-    android::base::StringAppendF(&result, "Number of tracked layers is %zu\n",
+    android::base::StringAppendF(&result, "Number of layers currently being tracked is %zu\n",
                                  mTimeStatsTracker.size());
+    android::base::StringAppendF(&result, "Number of layers in the stats pool is %zu\n",
+                                 mTimeStats.stats.size());
     return result;
 }
 
@@ -104,9 +272,86 @@
     mTimeStats.clientCompositionFrames++;
 }
 
-bool TimeStats::recordReadyLocked(int32_t layerID, TimeRecord* timeRecord) {
+void TimeStats::incrementClientCompositionReusedFrames() {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    mTimeStats.clientCompositionReusedFrames++;
+}
+
+void TimeStats::incrementRefreshRateSwitches() {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    mTimeStats.refreshRateSwitches++;
+}
+
+void TimeStats::incrementCompositionStrategyChanges() {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    mTimeStats.compositionStrategyChanges++;
+}
+
+void TimeStats::recordDisplayEventConnectionCount(int32_t count) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    mTimeStats.displayEventConnectionsCount =
+            std::max(mTimeStats.displayEventConnectionsCount, count);
+}
+
+static int32_t msBetween(nsecs_t start, nsecs_t end) {
+    int64_t delta = std::chrono::duration_cast<std::chrono::milliseconds>(
+                            std::chrono::nanoseconds(end - start))
+                            .count();
+    delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
+    return static_cast<int32_t>(delta);
+}
+
+void TimeStats::recordFrameDuration(nsecs_t startTime, nsecs_t endTime) {
+    if (!mEnabled.load()) return;
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mPowerTime.powerMode == PowerMode::ON) {
+        mTimeStats.frameDuration.insert(msBetween(startTime, endTime));
+    }
+}
+
+void TimeStats::recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) {
+    if (!mEnabled.load()) return;
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mGlobalRecord.renderEngineDurations.size() == MAX_NUM_TIME_RECORDS) {
+        ALOGE("RenderEngineTimes are already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+        mGlobalRecord.renderEngineDurations.pop_front();
+    }
+    mGlobalRecord.renderEngineDurations.push_back({startTime, endTime});
+}
+
+void TimeStats::recordRenderEngineDuration(nsecs_t startTime,
+                                           const std::shared_ptr<FenceTime>& endTime) {
+    if (!mEnabled.load()) return;
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mGlobalRecord.renderEngineDurations.size() == MAX_NUM_TIME_RECORDS) {
+        ALOGE("RenderEngineTimes are already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+        mGlobalRecord.renderEngineDurations.pop_front();
+    }
+    mGlobalRecord.renderEngineDurations.push_back({startTime, endTime});
+}
+
+bool TimeStats::recordReadyLocked(int32_t layerId, TimeRecord* timeRecord) {
     if (!timeRecord->ready) {
-        ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerID,
+        ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerId,
               timeRecord->frameTime.frameNumber);
         return false;
     }
@@ -119,7 +364,7 @@
             timeRecord->frameTime.acquireTime = timeRecord->acquireFence->getSignalTime();
             timeRecord->acquireFence = nullptr;
         } else {
-            ALOGV("[%d]-[%" PRIu64 "]-acquireFence signal time is invalid", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-acquireFence signal time is invalid", layerId,
                   timeRecord->frameTime.frameNumber);
         }
     }
@@ -132,7 +377,7 @@
             timeRecord->frameTime.presentTime = timeRecord->presentFence->getSignalTime();
             timeRecord->presentFence = nullptr;
         } else {
-            ALOGV("[%d]-[%" PRIu64 "]-presentFence signal time invalid", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-presentFence signal time invalid", layerId,
                   timeRecord->frameTime.frameNumber);
         }
     }
@@ -140,138 +385,105 @@
     return true;
 }
 
-static int32_t msBetween(nsecs_t start, nsecs_t end) {
-    int64_t delta = (end - start) / 1000000;
-    delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
-    return static_cast<int32_t>(delta);
-}
-
-// This regular expression captures the following for instance:
-// StatusBar in StatusBar#0
-// com.appname in com.appname/com.appname.activity#0
-// com.appname in SurfaceView - com.appname/com.appname.activity#0
-static const std::regex packageNameRegex("(?:SurfaceView[-\\s\\t]+)?([^/]+).*#\\d+");
-
-static std::string getPackageName(const std::string& layerName) {
-    std::smatch match;
-    if (std::regex_match(layerName.begin(), layerName.end(), match, packageNameRegex)) {
-        // There must be a match for group 1 otherwise the whole string is not
-        // matched and the above will return false
-        return match[1];
-    }
-    return "";
-}
-
-void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerID) {
+void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId) {
     ATRACE_CALL();
 
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
     std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
     while (!timeRecords.empty()) {
-        if (!recordReadyLocked(layerID, &timeRecords[0])) break;
-        ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerID,
+        if (!recordReadyLocked(layerId, &timeRecords[0])) break;
+        ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerId,
               timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
 
-        const std::string& layerName = layerRecord.layerName;
         if (prevTimeRecord.ready) {
+            const std::string& layerName = layerRecord.layerName;
             if (!mTimeStats.stats.count(layerName)) {
                 mTimeStats.stats[layerName].layerName = layerName;
-                mTimeStats.stats[layerName].packageName = getPackageName(layerName);
             }
             TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName];
             timeStatsLayer.totalFrames++;
             timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
+            timeStatsLayer.lateAcquireFrames += layerRecord.lateAcquireFrames;
+            timeStatsLayer.badDesiredPresentFrames += layerRecord.badDesiredPresentFrames;
+
             layerRecord.droppedFrames = 0;
+            layerRecord.lateAcquireFrames = 0;
+            layerRecord.badDesiredPresentFrames = 0;
 
             const int32_t postToAcquireMs = msBetween(timeRecords[0].frameTime.postTime,
                                                       timeRecords[0].frameTime.acquireTime);
-            ALOGV("[%d]-[%" PRIu64 "]-post2acquire[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-post2acquire[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, postToAcquireMs);
             timeStatsLayer.deltas["post2acquire"].insert(postToAcquireMs);
 
             const int32_t postToPresentMs = msBetween(timeRecords[0].frameTime.postTime,
                                                       timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-post2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-post2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, postToPresentMs);
             timeStatsLayer.deltas["post2present"].insert(postToPresentMs);
 
             const int32_t acquireToPresentMs = msBetween(timeRecords[0].frameTime.acquireTime,
                                                          timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, acquireToPresentMs);
             timeStatsLayer.deltas["acquire2present"].insert(acquireToPresentMs);
 
             const int32_t latchToPresentMs = msBetween(timeRecords[0].frameTime.latchTime,
                                                        timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, latchToPresentMs);
             timeStatsLayer.deltas["latch2present"].insert(latchToPresentMs);
 
             const int32_t desiredToPresentMs = msBetween(timeRecords[0].frameTime.desiredTime,
                                                          timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, desiredToPresentMs);
             timeStatsLayer.deltas["desired2present"].insert(desiredToPresentMs);
 
             const int32_t presentToPresentMs = msBetween(prevTimeRecord.frameTime.presentTime,
                                                          timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, presentToPresentMs);
             timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
         }
-
-        // Output additional trace points to track frame time.
-        ATRACE_INT64(("TimeStats-Post - " + layerName).c_str(), timeRecords[0].frameTime.postTime);
-        ATRACE_INT64(("TimeStats-Acquire - " + layerName).c_str(),
-                     timeRecords[0].frameTime.acquireTime);
-        ATRACE_INT64(("TimeStats-Latch - " + layerName).c_str(),
-                     timeRecords[0].frameTime.latchTime);
-        ATRACE_INT64(("TimeStats-Desired - " + layerName).c_str(),
-                     timeRecords[0].frameTime.desiredTime);
-        ATRACE_INT64(("TimeStats-Present - " + layerName).c_str(),
-                     timeRecords[0].frameTime.presentTime);
-
         prevTimeRecord = timeRecords[0];
         timeRecords.pop_front();
         layerRecord.waitData--;
     }
 }
 
-// This regular expression captures the following layer names for instance:
-// 1) StatusBat#0
-// 2) NavigationBar#1
-// 3) co(m).*#0
-// 4) SurfaceView - co(m).*#0
-// Using [-\\s\t]+ for the conjunction part between SurfaceView and co(m).*
-// is a bit more robust in case there's a slight change.
-// The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases.
-static const std::regex layerNameRegex(
-        "(((SurfaceView[-\\s\\t]+)?com?\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+");
+static constexpr const char* kPopupWindowPrefix = "PopupWindow";
+static const size_t kMinLenLayerName = std::strlen(kPopupWindowPrefix);
 
+// Avoid tracking the "PopupWindow:<random hash>#<number>" layers
 static bool layerNameIsValid(const std::string& layerName) {
-    return std::regex_match(layerName.begin(), layerName.end(), layerNameRegex);
+    return layerName.length() >= kMinLenLayerName &&
+            layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0;
 }
 
-void TimeStats::setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                             nsecs_t postTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerID, frameNumber, layerName.c_str(),
+    ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerId, frameNumber, layerName.c_str(),
           postTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
-        layerNameIsValid(layerName)) {
-        mTimeStatsTracker[layerID].layerName = layerName;
+    if (!mTimeStats.stats.count(layerName) && mTimeStats.stats.size() >= MAX_NUM_LAYER_STATS) {
+        return;
     }
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
+        layerNameIsValid(layerName)) {
+        mTimeStatsTracker[layerId].layerName = layerName;
+    }
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) {
         ALOGE("[%d]-[%s]-timeRecords is at its maximum size[%zu]. Ignore this when unittesting.",
-              layerID, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS);
-        mTimeStatsTracker.erase(layerID);
+              layerId, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS);
+        mTimeStatsTracker.erase(layerId);
         return;
     }
     // For most media content, the acquireFence is invalid because the buffer is
@@ -293,15 +505,15 @@
         layerRecord.waitData = layerRecord.timeRecords.size() - 1;
 }
 
-void TimeStats::setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) {
+void TimeStats::setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerID, frameNumber, latchTime);
+    ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerId, frameNumber, latchTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -311,15 +523,45 @@
     }
 }
 
-void TimeStats::setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) {
+void TimeStats::incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerID, frameNumber, desiredTime);
+    ALOGV("[%d]-LatchSkipped-Reason[%d]", layerId,
+          static_cast<std::underlying_type<LatchSkipReason>::type>(reason));
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
+
+    switch (reason) {
+        case LatchSkipReason::LateAcquire:
+            layerRecord.lateAcquireFrames++;
+            break;
+    }
+}
+
+void TimeStats::incrementBadDesiredPresent(int32_t layerId) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    ALOGV("[%d]-BadDesiredPresent", layerId);
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
+    layerRecord.badDesiredPresentFrames++;
+}
+
+void TimeStats::setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerId, frameNumber, desiredTime);
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -329,15 +571,15 @@
     }
 }
 
-void TimeStats::setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) {
+void TimeStats::setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerID, frameNumber, acquireTime);
+    ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerId, frameNumber, acquireTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -347,17 +589,17 @@
     }
 }
 
-void TimeStats::setAcquireFence(int32_t layerID, uint64_t frameNumber,
+void TimeStats::setAcquireFence(int32_t layerId, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& acquireFence) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerID, frameNumber,
+    ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerId, frameNumber,
           acquireFence->getSignalTime());
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -367,15 +609,15 @@
     }
 }
 
-void TimeStats::setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) {
+void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerID, frameNumber, presentTime);
+    ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerId, frameNumber, presentTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -386,20 +628,20 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerID);
+    flushAvailableRecordsToStatsLocked(layerId);
 }
 
-void TimeStats::setPresentFence(int32_t layerID, uint64_t frameNumber,
+void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& presentFence) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerID, frameNumber,
+    ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerId, frameNumber,
           presentFence->getSignalTime());
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -410,29 +652,25 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerID);
+    flushAvailableRecordsToStatsLocked(layerId);
 }
 
-void TimeStats::onDestroy(int32_t layerID) {
+void TimeStats::onDestroy(int32_t layerId) {
+    ATRACE_CALL();
+    ALOGV("[%d]-onDestroy", layerId);
+    std::lock_guard<std::mutex> lock(mMutex);
+    mTimeStatsTracker.erase(layerId);
+}
+
+void TimeStats::removeTimeRecord(int32_t layerId, uint64_t frameNumber) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-onDestroy", layerID);
+    ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerId, frameNumber);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    mTimeStatsTracker.erase(layerID);
-}
-
-void TimeStats::removeTimeRecord(int32_t layerID, uint64_t frameNumber) {
-    if (!mEnabled.load()) return;
-
-    ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerID, frameNumber);
-
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     size_t removeAt = 0;
     for (const TimeRecord& record : layerRecord.timeRecords) {
         if (record.frameTime.frameNumber == frameNumber) break;
@@ -454,12 +692,13 @@
     int64_t elapsedTime = (curTime - mPowerTime.prevTime) / 1000000;
 
     switch (mPowerTime.powerMode) {
-        case HWC_POWER_MODE_NORMAL:
+        case PowerMode::ON:
             mTimeStats.displayOnTime += elapsedTime;
             break;
-        case HWC_POWER_MODE_OFF:
-        case HWC_POWER_MODE_DOZE:
-        case HWC_POWER_MODE_DOZE_SUSPEND:
+        case PowerMode::OFF:
+        case PowerMode::DOZE:
+        case PowerMode::DOZE_SUSPEND:
+        case PowerMode::ON_SUSPEND:
         default:
             break;
     }
@@ -467,7 +706,7 @@
     mPowerTime.prevTime = curTime;
 }
 
-void TimeStats::setPowerMode(int32_t powerMode) {
+void TimeStats::setPowerMode(PowerMode powerMode) {
     if (!mEnabled.load()) {
         std::lock_guard<std::mutex> lock(mMutex);
         mPowerTime.powerMode = powerMode;
@@ -518,6 +757,31 @@
         mGlobalRecord.prevPresentTime = curPresentTime;
         mGlobalRecord.presentFences.pop_front();
     }
+    while (!mGlobalRecord.renderEngineDurations.empty()) {
+        const auto duration = mGlobalRecord.renderEngineDurations.front();
+        const auto& endTime = duration.endTime;
+
+        nsecs_t endNs = -1;
+
+        if (auto val = std::get_if<nsecs_t>(&endTime)) {
+            endNs = *val;
+        } else {
+            endNs = std::get<std::shared_ptr<FenceTime>>(endTime)->getSignalTime();
+        }
+
+        if (endNs == Fence::SIGNAL_TIME_PENDING) break;
+
+        if (endNs < 0) {
+            ALOGE("RenderEngineTiming is invalid!");
+            mGlobalRecord.renderEngineDurations.pop_front();
+            continue;
+        }
+
+        const int32_t renderEngineMs = msBetween(duration.startTime, endNs);
+        mTimeStats.renderEngineTiming.insert(renderEngineMs);
+
+        mGlobalRecord.renderEngineDurations.pop_front();
+    }
 }
 
 void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) {
@@ -530,8 +794,8 @@
         return;
     }
 
-    if (mPowerTime.powerMode != HWC_POWER_MODE_NORMAL) {
-        // Try flushing the last present fence on HWC_POWER_MODE_NORMAL.
+    if (mPowerTime.powerMode != PowerMode::ON) {
+        // Try flushing the last present fence on PowerMode::ON.
         flushAvailableGlobalRecordsToStatsLocked();
         mGlobalRecord.presentFences.clear();
         mGlobalRecord.prevPresentTime = 0;
@@ -574,24 +838,41 @@
     ALOGD("Disabled");
 }
 
-void TimeStats::clear() {
+void TimeStats::clearAll() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    clearGlobalLocked();
+    clearLayersLocked();
+}
+
+void TimeStats::clearGlobalLocked() {
     ATRACE_CALL();
 
-    std::lock_guard<std::mutex> lock(mMutex);
-    mTimeStatsTracker.clear();
-    mTimeStats.stats.clear();
     mTimeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
     mTimeStats.statsEnd = 0;
     mTimeStats.totalFrames = 0;
     mTimeStats.missedFrames = 0;
     mTimeStats.clientCompositionFrames = 0;
+    mTimeStats.clientCompositionReusedFrames = 0;
+    mTimeStats.refreshRateSwitches = 0;
+    mTimeStats.compositionStrategyChanges = 0;
+    mTimeStats.displayEventConnectionsCount = 0;
     mTimeStats.displayOnTime = 0;
     mTimeStats.presentToPresent.hist.clear();
+    mTimeStats.frameDuration.hist.clear();
+    mTimeStats.renderEngineTiming.hist.clear();
     mTimeStats.refreshRateStats.clear();
     mPowerTime.prevTime = systemTime();
     mGlobalRecord.prevPresentTime = 0;
     mGlobalRecord.presentFences.clear();
-    ALOGD("Cleared");
+    ALOGD("Cleared global stats");
+}
+
+void TimeStats::clearLayersLocked() {
+    ATRACE_CALL();
+
+    mTimeStatsTracker.clear();
+    mTimeStats.stats.clear();
+    ALOGD("Cleared layer stats");
 }
 
 bool TimeStats::isEnabled() {
@@ -613,7 +894,7 @@
     if (asProto) {
         ALOGD("Dumping TimeStats as proto");
         SFTimeStatsGlobalProto timeStatsProto = mTimeStats.toProto(maxLayers);
-        result.append(timeStatsProto.SerializeAsString().c_str(), timeStatsProto.ByteSize());
+        result.append(timeStatsProto.SerializeAsString());
     } else {
         ALOGD("Dumping TimeStats as text");
         result.append(mTimeStats.toString(maxLayers));
@@ -624,3 +905,6 @@
 } // namespace impl
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 2bcb568..8de5d0c 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -16,13 +16,21 @@
 
 #pragma once
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
 #include <timestatsproto/TimeStatsHelper.h>
 #include <timestatsproto/TimeStatsProtoHeader.h>
-
-#include <hardware/hwcomposer_defs.h>
-
 #include <ui/FenceTime.h>
-
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
@@ -30,6 +38,7 @@
 #include <mutex>
 #include <optional>
 #include <unordered_map>
+#include <variant>
 
 using namespace android::surfaceflinger;
 
@@ -39,6 +48,10 @@
 public:
     virtual ~TimeStats() = default;
 
+    // Called once boot has been finished to perform additional capabilities,
+    // e.g. registration to statsd.
+    virtual void onBootFinished() = 0;
+
     virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
     virtual bool isEnabled() = 0;
     virtual std::string miniDump() = 0;
@@ -46,23 +59,62 @@
     virtual void incrementTotalFrames() = 0;
     virtual void incrementMissedFrames() = 0;
     virtual void incrementClientCompositionFrames() = 0;
+    virtual void incrementClientCompositionReusedFrames() = 0;
+    // Increments the number of times the display refresh rate changed.
+    virtual void incrementRefreshRateSwitches() = 0;
+    // Increments the number of changes in composition strategy
+    // The intention is to reflect the number of changes between hwc and gpu
+    // composition, where "gpu composition" may also include mixed composition.
+    virtual void incrementCompositionStrategyChanges() = 0;
+    // Records the most up-to-date count of display event connections.
+    // The stored count will be the maximum ever recoded.
+    virtual void recordDisplayEventConnectionCount(int32_t count) = 0;
 
-    virtual void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+    // Records the start and end times for a frame.
+    // The start time is the same as the beginning of a SurfaceFlinger
+    // invalidate message.
+    // The end time corresponds to when SurfaceFlinger finishes submitting the
+    // request to HWC to present a frame.
+    virtual void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) = 0;
+    // Records the start time and end times for when RenderEngine begins work.
+    // The start time corresponds to the beginning of RenderEngine::drawLayers.
+    // The end time corresponds to when RenderEngine finishes rendering.
+    virtual void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) = 0;
+    // Same as above, but passes in a fence representing the end time.
+    virtual void recordRenderEngineDuration(nsecs_t startTime,
+                                            const std::shared_ptr<FenceTime>& readyFence) = 0;
+
+    virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                              nsecs_t postTime) = 0;
-    virtual void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) = 0;
-    virtual void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) = 0;
-    virtual void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) = 0;
-    virtual void setAcquireFence(int32_t layerID, uint64_t frameNumber,
+    virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
+    // Reasons why latching a particular buffer may be skipped
+    enum class LatchSkipReason {
+        // If the acquire fence did not fire on some devices we skip latching
+        // the buffer until the fence fires.
+        LateAcquire,
+    };
+    // Increments the counter of skipped latch buffers.
+    virtual void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) = 0;
+    // Increments the counter of bad desired present times for this layer.
+    // Bad desired present times are "implausible" and cause SurfaceFlinger to
+    // latch a buffer immediately to avoid stalling.
+    virtual void incrementBadDesiredPresent(int32_t layerId) = 0;
+    virtual void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) = 0;
+    virtual void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) = 0;
+    virtual void setAcquireFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& acquireFence) = 0;
-    virtual void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) = 0;
-    virtual void setPresentFence(int32_t layerID, uint64_t frameNumber,
+    // SetPresent{Time, Fence} are not expected to be called in the critical
+    // rendering path, as they flush prior fences if those fences have fired.
+    virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0;
+    virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& presentFence) = 0;
     // Clean up the layer record
-    virtual void onDestroy(int32_t layerID) = 0;
+    virtual void onDestroy(int32_t layerId) = 0;
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
-    virtual void removeTimeRecord(int32_t layerID, uint64_t frameNumber) = 0;
+    virtual void removeTimeRecord(int32_t layerId, uint64_t frameNumber) = 0;
 
-    virtual void setPowerMode(int32_t powerMode) = 0;
+    virtual void setPowerMode(
+            hardware::graphics::composer::V2_4::IComposerClient::PowerMode powerMode) = 0;
     // Source of truth is RefrehRateStats.
     virtual void recordRefreshRate(uint32_t fps, nsecs_t duration) = 0;
     virtual void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) = 0;
@@ -71,6 +123,8 @@
 namespace impl {
 
 class TimeStats : public android::TimeStats {
+    using PowerMode = android::hardware::graphics::composer::V2_4::IComposerClient::PowerMode;
+
     struct FrameTime {
         uint64_t frameNumber = 0;
         nsecs_t postTime = 0;
@@ -94,23 +148,82 @@
         // fences to signal, but rather waiting to receive those fences/timestamps.
         int32_t waitData = -1;
         uint32_t droppedFrames = 0;
+        uint32_t lateAcquireFrames = 0;
+        uint32_t badDesiredPresentFrames = 0;
         TimeRecord prevTimeRecord;
         std::deque<TimeRecord> timeRecords;
     };
 
     struct PowerTime {
-        int32_t powerMode = HWC_POWER_MODE_OFF;
+        PowerMode powerMode = PowerMode::OFF;
         nsecs_t prevTime = 0;
     };
 
+    struct RenderEngineDuration {
+        nsecs_t startTime;
+        std::variant<nsecs_t, std::shared_ptr<FenceTime>> endTime;
+    };
+
     struct GlobalRecord {
         nsecs_t prevPresentTime = 0;
         std::deque<std::shared_ptr<FenceTime>> presentFences;
+        std::deque<RenderEngineDuration> renderEngineDurations;
     };
 
 public:
-    TimeStats() = default;
+    TimeStats();
 
+    // Delegate to the statsd service and associated APIs.
+    // Production code may use this class directly, whereas unit test may define
+    // a subclass for ease of testing.
+    class StatsEventDelegate {
+    public:
+        virtual ~StatsEventDelegate() = default;
+        virtual AStatsEvent* addStatsEventToPullData(AStatsEventList* data) {
+            return AStatsEventList_addStatsEvent(data);
+        }
+        virtual void setStatsPullAtomCallback(int32_t atom_tag,
+                                              AStatsManager_PullAtomMetadata* metadata,
+                                              AStatsManager_PullAtomCallback callback,
+                                              void* cookie) {
+            return AStatsManager_setPullAtomCallback(atom_tag, metadata, callback, cookie);
+        }
+
+        virtual void clearStatsPullAtomCallback(int32_t atom_tag) {
+            return AStatsManager_clearPullAtomCallback(atom_tag);
+        }
+
+        virtual void statsEventSetAtomId(AStatsEvent* event, uint32_t atom_id) {
+            return AStatsEvent_setAtomId(event, atom_id);
+        }
+
+        virtual void statsEventWriteInt32(AStatsEvent* event, int32_t field) {
+            return AStatsEvent_writeInt32(event, field);
+        }
+
+        virtual void statsEventWriteInt64(AStatsEvent* event, int64_t field) {
+            return AStatsEvent_writeInt64(event, field);
+        }
+
+        virtual void statsEventWriteString8(AStatsEvent* event, const char* field) {
+            return AStatsEvent_writeString(event, field);
+        }
+
+        virtual void statsEventWriteByteArray(AStatsEvent* event, const uint8_t* buf,
+                                              size_t numBytes) {
+            return AStatsEvent_writeByteArray(event, buf, numBytes);
+        }
+
+        virtual void statsEventBuild(AStatsEvent* event) { return AStatsEvent_build(event); }
+    };
+    // For testing only for injecting custom dependencies.
+    TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate,
+              std::optional<size_t> maxPulledLayers,
+              std::optional<size_t> maxPulledHistogramBuckets);
+
+    ~TimeStats() override;
+
+    void onBootFinished() override;
     void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
     bool isEnabled() override;
     std::string miniDump() override;
@@ -118,23 +231,35 @@
     void incrementTotalFrames() override;
     void incrementMissedFrames() override;
     void incrementClientCompositionFrames() override;
+    void incrementClientCompositionReusedFrames() override;
+    void incrementRefreshRateSwitches() override;
+    void incrementCompositionStrategyChanges() override;
+    void recordDisplayEventConnectionCount(int32_t count) override;
 
-    void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+    void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) override;
+    void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) override;
+    void recordRenderEngineDuration(nsecs_t startTime,
+                                    const std::shared_ptr<FenceTime>& readyFence) override;
+
+    void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                      nsecs_t postTime) override;
-    void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) override;
-    void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) override;
-    void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) override;
-    void setAcquireFence(int32_t layerID, uint64_t frameNumber,
+    void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
+    void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
+    void incrementBadDesiredPresent(int32_t layerId) override;
+    void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) override;
+    void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) override;
+    void setAcquireFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& acquireFence) override;
-    void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) override;
-    void setPresentFence(int32_t layerID, uint64_t frameNumber,
+    void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) override;
+    void setPresentFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& presentFence) override;
     // Clean up the layer record
-    void onDestroy(int32_t layerID) override;
+    void onDestroy(int32_t layerId) override;
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
-    void removeTimeRecord(int32_t layerID, uint64_t frameNumber) override;
+    void removeTimeRecord(int32_t layerId, uint64_t frameNumber) override;
 
-    void setPowerMode(int32_t powerMode) override;
+    void setPowerMode(
+            hardware::graphics::composer::V2_4::IComposerClient::PowerMode powerMode) override;
     // Source of truth is RefrehRateStats.
     void recordRefreshRate(uint32_t fps, nsecs_t duration) override;
     void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) override;
@@ -142,25 +267,36 @@
     static const size_t MAX_NUM_TIME_RECORDS = 64;
 
 private:
-    bool recordReadyLocked(int32_t layerID, TimeRecord* timeRecord);
-    void flushAvailableRecordsToStatsLocked(int32_t layerID);
+    static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atom_tag,
+                                                                 AStatsEventList* data,
+                                                                 void* cookie);
+    AStatsManager_PullAtomCallbackReturn populateGlobalAtom(AStatsEventList* data);
+    AStatsManager_PullAtomCallbackReturn populateLayerAtom(AStatsEventList* data);
+    bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
+    void flushAvailableRecordsToStatsLocked(int32_t layerId);
     void flushPowerTimeLocked();
     void flushAvailableGlobalRecordsToStatsLocked();
 
     void enable();
     void disable();
-    void clear();
+    void clearAll();
+    void clearGlobalLocked();
+    void clearLayersLocked();
     void dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result);
 
     std::atomic<bool> mEnabled = false;
     std::mutex mMutex;
     TimeStatsHelper::TimeStatsGlobal mTimeStats;
-    // Hashmap for LayerRecord with layerID as the hash key
+    // Hashmap for LayerRecord with layerId as the hash key
     std::unordered_map<int32_t, LayerRecord> mTimeStatsTracker;
     PowerTime mPowerTime;
     GlobalRecord mGlobalRecord;
 
     static const size_t MAX_NUM_LAYER_RECORDS = 200;
+    static const size_t MAX_NUM_LAYER_STATS = 200;
+    std::unique_ptr<StatsEventDelegate> mStatsDelegate = std::make_unique<StatsEventDelegate>();
+    size_t mMaxPulledLayers = 8;
+    size_t mMaxPulledHistogramBuckets = 6;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 16d2da0..894ee6d 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -83,9 +83,13 @@
     StringAppendF(&result, "packageName = %s\n", packageName.c_str());
     StringAppendF(&result, "totalFrames = %d\n", totalFrames);
     StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
+    StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
+    StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
     const auto iter = deltas.find("present2present");
     if (iter != deltas.end()) {
-        StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime());
+        const float averageTime = iter->second.averageTime();
+        const float averageFPS = averageTime < 1.0f ? 0.0f : 1000.0f / averageTime;
+        StringAppendF(&result, "averageFPS = %.3f\n", averageFPS);
     }
     for (const auto& ele : deltas) {
         StringAppendF(&result, "%s histogram is as below:\n", ele.first.c_str());
@@ -102,6 +106,9 @@
     StringAppendF(&result, "totalFrames = %d\n", totalFrames);
     StringAppendF(&result, "missedFrames = %d\n", missedFrames);
     StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
+    StringAppendF(&result, "clientCompositionReusedFrames = %d\n", clientCompositionReusedFrames);
+    StringAppendF(&result, "refreshRateSwitches = %d\n", refreshRateSwitches);
+    StringAppendF(&result, "compositionStrategyChanges = %d\n", compositionStrategyChanges);
     StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
     StringAppendF(&result, "displayConfigStats is as below:\n");
     for (const auto& [fps, duration] : refreshRateStats) {
@@ -111,6 +118,16 @@
     StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
     StringAppendF(&result, "presentToPresent histogram is as below:\n");
     result.append(presentToPresent.toString());
+    const float averageFrameDuration = frameDuration.averageTime();
+    StringAppendF(&result, "averageFrameDuration = %.3f ms\n",
+                  std::isnan(averageFrameDuration) ? 0.0f : averageFrameDuration);
+    StringAppendF(&result, "frameDuration histogram is as below:\n");
+    result.append(frameDuration.toString());
+    const float averageRenderEngineTiming = renderEngineTiming.averageTime();
+    StringAppendF(&result, "averageRenderEngineTiming = %.3f ms\n",
+                  std::isnan(averageRenderEngineTiming) ? 0.0f : averageRenderEngineTiming);
+    StringAppendF(&result, "renderEngineTiming histogram is as below:\n");
+    result.append(renderEngineTiming.toString());
     const auto dumpStats = generateDumpStats(maxLayers);
     for (const auto& ele : dumpStats) {
         result.append(ele->toString());
@@ -158,6 +175,16 @@
         histProto->set_time_millis(histEle.first);
         histProto->set_frame_count(histEle.second);
     }
+    for (const auto& histEle : frameDuration.hist) {
+        SFTimeStatsHistogramBucketProto* histProto = globalProto.add_frame_duration();
+        histProto->set_time_millis(histEle.first);
+        histProto->set_frame_count(histEle.second);
+    }
+    for (const auto& histEle : renderEngineTiming.hist) {
+        SFTimeStatsHistogramBucketProto* histProto = globalProto.add_render_engine_timing();
+        histProto->set_time_millis(histEle.first);
+        histProto->set_frame_count(histEle.second);
+    }
     const auto dumpStats = generateDumpStats(maxLayers);
     for (const auto& ele : dumpStats) {
         SFTimeStatsLayerProto* layerProto = globalProto.add_stats();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index f2ac7ff..0c75f96 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -46,6 +46,8 @@
         std::string packageName;
         int32_t totalFrames = 0;
         int32_t droppedFrames = 0;
+        int32_t lateAcquireFrames = 0;
+        int32_t badDesiredPresentFrames = 0;
         std::unordered_map<std::string, Histogram> deltas;
 
         std::string toString() const;
@@ -59,8 +61,14 @@
         int32_t totalFrames = 0;
         int32_t missedFrames = 0;
         int32_t clientCompositionFrames = 0;
+        int32_t clientCompositionReusedFrames = 0;
+        int32_t refreshRateSwitches = 0;
+        int32_t compositionStrategyChanges = 0;
+        int32_t displayEventConnectionsCount = 0;
         int64_t displayOnTime = 0;
         Histogram presentToPresent;
+        Histogram frameDuration;
+        Histogram renderEngineTiming;
         std::unordered_map<std::string, TimeStatsLayer> stats;
         std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
 
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
index 0dacbeb..5fd4a39 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -25,7 +25,7 @@
 // changes to these messages, and keep google3 side proto messages in sync if
 // the end to end pipeline needs to be updated.
 
-// Next tag: 10
+// Next tag: 12
 message SFTimeStatsGlobalProto {
   // The stats start time in UTC as seconds since January 1, 1970
   optional int64 stats_start = 1;
@@ -43,6 +43,10 @@
   repeated SFTimeStatsDisplayConfigBucketProto display_config_stats = 9;
   // Present to present histogram.
   repeated SFTimeStatsHistogramBucketProto present_to_present = 8;
+  // Frame CPU duration histogram.
+  repeated SFTimeStatsHistogramBucketProto frame_duration = 10;
+  // Frame GPU duration histogram.
+  repeated SFTimeStatsHistogramBucketProto render_engine_timing = 11;
   // Stats per layer. Apps could have multiple layers.
   repeated SFTimeStatsLayerProto stats = 6;
 }
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
new file mode 100644
index 0000000..4e7f67d
--- /dev/null
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <android-base/stringprintf.h>
+#include <cutils/compiler.h>
+#include <utils/Trace.h>
+#include <cmath>
+#include <string>
+
+namespace android {
+
+template <typename T>
+class TracedOrdinal {
+public:
+    static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()),
+                  "Type is not supported. Please test it with systrace before adding "
+                  "it to the list.");
+
+    TracedOrdinal(std::string name, T initialValue)
+          : mName(std::move(name)),
+            mHasGoneNegative(std::signbit(initialValue)),
+            mData(initialValue) {
+        trace();
+    }
+
+    operator T() const { return mData; }
+
+    TracedOrdinal& operator=(T other) {
+        mData = other;
+        mHasGoneNegative = mHasGoneNegative || std::signbit(mData);
+        trace();
+        return *this;
+    }
+
+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) {
+                ATRACE_INT64(mNameNegative.c_str(), 0);
+            }
+        } else {
+            ATRACE_INT64(mNameNegative.c_str(), -int64_t(mData));
+            ATRACE_INT64(mName.c_str(), 0);
+        }
+    }
+
+    const std::string mName;
+    std::string mNameNegative;
+    bool mHasGoneNegative;
+    T mData;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index fd466de..ca24493 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "TransactionCompletedThread"
@@ -24,7 +28,6 @@
 #include <cinttypes>
 
 #include <binder/IInterface.h>
-#include <gui/ITransactionCompletedListener.h>
 #include <utils/RefBase.h>
 
 namespace android {
@@ -58,7 +61,7 @@
     {
         std::lock_guard lock(mMutex);
         for (const auto& [listener, transactionStats] : mCompletedTransactions) {
-            IInterface::asBinder(listener)->unlinkToDeath(mDeathRecipient);
+            listener->unlinkToDeath(mDeathRecipient);
         }
     }
 }
@@ -75,27 +78,59 @@
     mThread = std::thread(&TransactionCompletedThread::threadMain, this);
 }
 
-status_t TransactionCompletedThread::addCallback(const sp<ITransactionCompletedListener>& listener,
-                                                 const std::vector<CallbackId>& callbackIds) {
+status_t TransactionCompletedThread::startRegistration(const ListenerCallbacks& listenerCallbacks) {
+    // begin running if not already running
+    run();
     std::lock_guard lock(mMutex);
     if (!mRunning) {
         ALOGE("cannot add callback because the callback thread isn't running");
         return BAD_VALUE;
     }
 
-    if (mCompletedTransactions.count(listener) == 0) {
-        status_t err = IInterface::asBinder(listener)->linkToDeath(mDeathRecipient);
-        if (err != NO_ERROR) {
-            ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
-            return err;
+    auto [itr, inserted] = mRegisteringTransactions.insert(listenerCallbacks);
+    auto& [listener, callbackIds] = listenerCallbacks;
+
+    if (inserted) {
+        if (mCompletedTransactions.count(listener) == 0) {
+            status_t err = listener->linkToDeath(mDeathRecipient);
+            if (err != NO_ERROR) {
+                ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
+                return err;
+            }
         }
+        auto& transactionStatsDeque = mCompletedTransactions[listener];
+        transactionStatsDeque.emplace_back(callbackIds);
     }
 
-    auto& transactionStatsDeque = mCompletedTransactions[listener];
-    transactionStatsDeque.emplace_back(callbackIds);
     return NO_ERROR;
 }
 
+status_t TransactionCompletedThread::endRegistration(const ListenerCallbacks& listenerCallbacks) {
+    std::lock_guard lock(mMutex);
+    if (!mRunning) {
+        ALOGE("cannot add callback because the callback thread isn't running");
+        return BAD_VALUE;
+    }
+
+    auto itr = mRegisteringTransactions.find(listenerCallbacks);
+    if (itr == mRegisteringTransactions.end()) {
+        ALOGE("cannot end a registration that does not exist");
+        return BAD_VALUE;
+    }
+
+    mRegisteringTransactions.erase(itr);
+
+    return NO_ERROR;
+}
+
+bool TransactionCompletedThread::isRegisteringTransaction(
+        const sp<IBinder>& transactionListener, const std::vector<CallbackId>& callbackIds) {
+    ListenerCallbacks listenerCallbacks(transactionListener, callbackIds);
+
+    auto itr = mRegisteringTransactions.find(listenerCallbacks);
+    return itr != mRegisteringTransactions.end();
+}
+
 status_t TransactionCompletedThread::registerPendingCallbackHandle(
         const sp<CallbackHandle>& handle) {
     std::lock_guard lock(mMutex);
@@ -105,7 +140,7 @@
     }
 
     // If we can't find the transaction stats something has gone wrong. The client should call
-    // addCallback before trying to register a pending callback handle.
+    // startRegistration before trying to register a pending callback handle.
     TransactionStats* transactionStats;
     status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
     if (err != NO_ERROR) {
@@ -117,8 +152,11 @@
     return NO_ERROR;
 }
 
-status_t TransactionCompletedThread::addPresentedCallbackHandles(
+status_t TransactionCompletedThread::finalizePendingCallbackHandles(
         const std::deque<sp<CallbackHandle>>& handles) {
+    if (handles.empty()) {
+        return NO_ERROR;
+    }
     std::lock_guard lock(mMutex);
     if (!mRunning) {
         ALOGE("cannot add presented callback handle because the callback thread isn't running");
@@ -158,7 +196,7 @@
     return NO_ERROR;
 }
 
-status_t TransactionCompletedThread::addUnpresentedCallbackHandle(
+status_t TransactionCompletedThread::registerUnpresentedCallbackHandle(
         const sp<CallbackHandle>& handle) {
     std::lock_guard lock(mMutex);
     if (!mRunning) {
@@ -170,8 +208,8 @@
 }
 
 status_t TransactionCompletedThread::findTransactionStats(
-        const sp<ITransactionCompletedListener>& listener,
-        const std::vector<CallbackId>& callbackIds, TransactionStats** outTransactionStats) {
+        const sp<IBinder>& listener, const std::vector<CallbackId>& callbackIds,
+        TransactionStats** outTransactionStats) {
     auto& transactionStatsDeque = mCompletedTransactions[listener];
 
     // Search back to front because the most recent transactions are at the back of the deque
@@ -189,7 +227,7 @@
 
 status_t TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
     // If we can't find the transaction stats something has gone wrong. The client should call
-    // addCallback before trying to add a presnted callback handle.
+    // startRegistration before trying to add a callback handle.
     TransactionStats* transactionStats;
     status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
     if (err != NO_ERROR) {
@@ -202,8 +240,13 @@
     // destroyed the client side is dead and there won't be anyone to send the callback to.
     sp<IBinder> surfaceControl = handle->surfaceControl.promote();
     if (surfaceControl) {
+        FrameEventHistoryStats eventStats(handle->frameNumber,
+                                          handle->gpuCompositionDoneFence->getSnapshot().fence,
+                                          handle->compositorTiming, handle->refreshStartTime,
+                                          handle->dequeueReadyTime);
         transactionStats->surfaceStats.emplace_back(surfaceControl, handle->acquireTime,
-                                                    handle->previousReleaseFence);
+                                                    handle->previousReleaseFence,
+                                                    handle->transformHint, eventStats);
     }
     return NO_ERROR;
 }
@@ -239,6 +282,13 @@
             while (transactionStatsItr != transactionStatsDeque.end()) {
                 auto& transactionStats = *transactionStatsItr;
 
+                // If this transaction is still registering, it is not safe to send a callback
+                // because there could be surface controls that haven't been added to
+                // transaction stats or mPendingTransactions.
+                if (isRegisteringTransaction(listener, transactionStats.callbackIds)) {
+                    break;
+                }
+
                 // If we are still waiting on the callback handles for this transaction, stop
                 // here because all transaction callbacks for the same listener must come in order
                 auto pendingTransactions = mPendingTransactions.find(listener);
@@ -262,12 +312,26 @@
             // If the listener has completed transactions
             if (!listenerStats.transactionStats.empty()) {
                 // If the listener is still alive
-                if (IInterface::asBinder(listener)->isBinderAlive()) {
-                    // Send callback
-                    listenerStats.listener->onTransactionCompleted(listenerStats);
-                    IInterface::asBinder(listener)->unlinkToDeath(mDeathRecipient);
+                if (listener->isBinderAlive()) {
+                    // Send callback.  The listener stored in listenerStats
+                    // comes from the cross-process setTransactionState call to
+                    // SF.  This MUST be an ITransactionCompletedListener.  We
+                    // keep it as an IBinder due to consistency reasons: if we
+                    // interface_cast at the IPC boundary when reading a Parcel,
+                    // we get pointers that compare unequal in the SF process.
+                    interface_cast<ITransactionCompletedListener>(listenerStats.listener)
+                            ->onTransactionCompleted(listenerStats);
+                    if (transactionStatsDeque.empty()) {
+                        listener->unlinkToDeath(mDeathRecipient);
+                        completedTransactionsItr =
+                                mCompletedTransactions.erase(completedTransactionsItr);
+                    } else {
+                        completedTransactionsItr++;
+                    }
+                } else {
+                    completedTransactionsItr =
+                            mCompletedTransactions.erase(completedTransactionsItr);
                 }
-                completedTransactionsItr = mCompletedTransactions.erase(completedTransactionsItr);
             } else {
                 completedTransactionsItr++;
             }
@@ -297,8 +361,11 @@
 
 // -----------------------------------------------------------------------
 
-CallbackHandle::CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
+CallbackHandle::CallbackHandle(const sp<IBinder>& transactionListener,
                                const std::vector<CallbackId>& ids, const sp<IBinder>& sc)
       : listener(transactionListener), callbackIds(ids), surfaceControl(sc) {}
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index e849f71..f50147a 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -21,6 +21,7 @@
 #include <mutex>
 #include <thread>
 #include <unordered_map>
+#include <unordered_set>
 
 #include <android-base/thread_annotations.h>
 
@@ -30,24 +31,12 @@
 
 namespace android {
 
-struct CallbackIdsHash {
-    // CallbackId vectors have several properties that let us get away with this simple hash.
-    // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is
-    // empty we can still hash 0.
-    // 2) CallbackId vectors for the same listener either are identical or contain none of the
-    // same members. It is sufficient to just check the first CallbackId in the vectors. If
-    // they match, they are the same. If they do not match, they are not the same.
-    std::size_t operator()(const std::vector<CallbackId>& callbackIds) const {
-        return std::hash<CallbackId>{}((callbackIds.empty()) ? 0 : callbackIds.front());
-    }
-};
-
 class CallbackHandle : public RefBase {
 public:
-    CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
-                   const std::vector<CallbackId>& ids, const sp<IBinder>& sc);
+    CallbackHandle(const sp<IBinder>& transactionListener, const std::vector<CallbackId>& ids,
+                   const sp<IBinder>& sc);
 
-    sp<ITransactionCompletedListener> listener;
+    sp<IBinder> listener;
     std::vector<CallbackId> callbackIds;
     wp<IBinder> surfaceControl;
 
@@ -55,6 +44,12 @@
     sp<Fence> previousReleaseFence;
     nsecs_t acquireTime = -1;
     nsecs_t latchTime = -1;
+    uint32_t transformHint = 0;
+    std::shared_ptr<FenceTime> gpuCompositionDoneFence{FenceTime::NO_FENCE};
+    CompositorTiming compositorTiming;
+    nsecs_t refreshStartTime = 0;
+    nsecs_t dequeueReadyTime = 0;
+    uint64_t frameNumber = 0;
 };
 
 class TransactionCompletedThread {
@@ -64,10 +59,12 @@
     void run();
 
     // Adds listener and callbackIds in case there are no SurfaceControls that are supposed
-    // to be included in the callback. This functions should be call before attempting to add any
-    // callback handles.
-    status_t addCallback(const sp<ITransactionCompletedListener>& transactionListener,
-                         const std::vector<CallbackId>& callbackIds);
+    // to be included in the callback. This functions should be call before attempting to register
+    // any callback handles.
+    status_t startRegistration(const ListenerCallbacks& listenerCallbacks);
+    // Ends the registration. After this is called, no more CallbackHandles will be registered.
+    // It is safe to send a callback if the Transaction doesn't have any Pending callback handles.
+    status_t endRegistration(const ListenerCallbacks& listenerCallbacks);
 
     // Informs the TransactionCompletedThread that there is a Transaction with a CallbackHandle
     // that needs to be latched and presented this frame. This function should be called once the
@@ -76,11 +73,11 @@
     // presented.
     status_t registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
     // Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
-    status_t addPresentedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
+    status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
 
     // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
     // presented this frame.
-    status_t addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
+    status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
 
     void addPresentFence(const sp<Fence>& presentFence);
 
@@ -89,7 +86,10 @@
 private:
     void threadMain();
 
-    status_t findTransactionStats(const sp<ITransactionCompletedListener>& listener,
+    bool isRegisteringTransaction(const sp<IBinder>& transactionListener,
+                                  const std::vector<CallbackId>& callbackIds) REQUIRES(mMutex);
+
+    status_t findTransactionStats(const sp<IBinder>& listener,
                                   const std::vector<CallbackId>& callbackIds,
                                   TransactionStats** outTransactionStats) REQUIRES(mMutex);
 
@@ -106,13 +106,6 @@
     };
     sp<ThreadDeathRecipient> mDeathRecipient;
 
-    struct ITransactionCompletedListenerHash {
-        std::size_t operator()(const sp<ITransactionCompletedListener>& listener) const {
-            return std::hash<IBinder*>{}((listener) ? IInterface::asBinder(listener).get()
-                                                    : nullptr);
-        }
-    };
-
     // Protects the creation and destruction of mThread
     std::mutex mThreadMutex;
 
@@ -121,13 +114,16 @@
     std::mutex mMutex;
     std::condition_variable_any mConditionVariable;
 
+    std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> mRegisteringTransactions
+            GUARDED_BY(mMutex);
+
     std::unordered_map<
-            sp<ITransactionCompletedListener>,
+            sp<IBinder>,
             std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>,
-            ITransactionCompletedListenerHash>
+            IListenerHash>
             mPendingTransactions GUARDED_BY(mMutex);
-    std::unordered_map<sp<ITransactionCompletedListener>, std::deque<TransactionStats>,
-                       ITransactionCompletedListenerHash>
+
+    std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash>
             mCompletedTransactions GUARDED_BY(mMutex);
 
     bool mRunning GUARDED_BY(mMutex) = false;
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index 973d20c..8fce0c9 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -37,16 +37,6 @@
     return lhs->id < rhs->id;
 }
 
-const LayerProtoParser::LayerGlobal LayerProtoParser::generateLayerGlobalInfo(
-        const LayersProto& layersProto) {
-    LayerGlobal layerGlobal;
-    layerGlobal.resolution = {layersProto.resolution().w(), layersProto.resolution().h()};
-    layerGlobal.colorMode = layersProto.color_mode();
-    layerGlobal.colorTransform = layersProto.color_transform();
-    layerGlobal.globalTransform = layersProto.global_transform();
-    return layerGlobal;
-}
-
 LayerProtoParser::LayerTree LayerProtoParser::generateLayerTree(const LayersProto& layersProto) {
     LayerTree layerTree;
     layerTree.allLayers = generateLayerList(layersProto);
@@ -114,19 +104,17 @@
     layer.bufferTransform = generateTransform(layerProto.buffer_transform());
     layer.queuedFrames = layerProto.queued_frames();
     layer.refreshPending = layerProto.refresh_pending();
-    layer.hwcFrame = generateRect(layerProto.hwc_frame());
-    layer.hwcCrop = generateFloatRect(layerProto.hwc_crop());
-    layer.hwcTransform = layerProto.hwc_transform();
-    layer.hwcCompositionType = layerProto.hwc_composition_type();
     layer.isProtected = layerProto.is_protected();
     layer.cornerRadius = layerProto.corner_radius();
+    layer.backgroundBlurRadius = layerProto.background_blur_radius();
     for (const auto& entry : layerProto.metadata()) {
         const std::string& dataStr = entry.second;
         std::vector<uint8_t>& outData = layer.metadata.mMap[entry.first];
         outData.resize(dataStr.size());
         memcpy(outData.data(), dataStr.data(), dataStr.size());
     }
-
+    layer.cornerRadiusCrop = generateFloatRect(layerProto.corner_radius_crop());
+    layer.shadowRadius = layerProto.shadow_radius();
     return layer;
 }
 
@@ -302,7 +290,8 @@
     StringAppendF(&result, "isProtected=%1d, ", isProtected);
     StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate);
     StringAppendF(&result, "dataspace=%s, ", dataspace.c_str());
-    StringAppendF(&result, "pixelFormat=%s, ", pixelFormat.c_str());
+    StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str());
+    StringAppendF(&result, "backgroundBlurRadius=%1d, ", backgroundBlurRadius);
     StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
                   static_cast<double>(color.r), static_cast<double>(color.g),
                   static_cast<double>(color.b), static_cast<double>(color.a), flags);
@@ -321,8 +310,9 @@
         first = false;
         result.append(metadata.itemToString(entry.first, ":"));
     }
-    result.append("}");
-
+    result.append("},");
+    StringAppendF(&result, " cornerRadiusCrop=%s, ", cornerRadiusCrop.to_string().c_str());
+    StringAppendF(&result, " shadowRadius=%.3f, ", shadowRadius);
     return result;
 }
 
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index d1b2b1f..52b9165 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -108,25 +108,16 @@
         Transform bufferTransform;
         int32_t queuedFrames;
         bool refreshPending;
-        LayerProtoParser::Rect hwcFrame;
-        LayerProtoParser::FloatRect hwcCrop;
-        int32_t hwcTransform;
-        int32_t hwcCompositionType;
         bool isProtected;
         float cornerRadius;
+        int backgroundBlurRadius;
         LayerMetadata metadata;
+        LayerProtoParser::FloatRect cornerRadiusCrop;
+        float shadowRadius;
 
         std::string to_string() const;
     };
 
-    class LayerGlobal {
-    public:
-        int2 resolution;
-        std::string colorMode;
-        std::string colorTransform;
-        int32_t globalTransform;
-    };
-
     class LayerTree {
     public:
         // all layers in LayersProto and in the original order
@@ -136,7 +127,6 @@
         std::vector<Layer*> topLevelLayers;
     };
 
-    static const LayerGlobal generateLayerGlobalInfo(const LayersProto& layersProto);
     static LayerTree generateLayerTree(const LayersProto& layersProto);
     static std::string layerTreeToString(const LayerTree& layerTree);
 
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index b097505..7f1f542 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -7,10 +7,23 @@
 // Contains a list of all layers.
 message LayersProto {
   repeated LayerProto layers = 1;
-  SizeProto resolution = 2;
-  string color_mode = 3;
-  string color_transform = 4;
-  int32 global_transform = 5;
+}
+
+// Must match definition in the IComposerClient HAL
+enum HwcCompositionType {
+    // Invalid composition type
+    INVALID = 0;
+    // Layer was composited by the client into the client target buffer
+    CLIENT = 1;
+    // Layer was composited by the device through hardware overlays
+    DEVICE = 2;
+    // Layer was composited by the device using a color
+    SOLID_COLOR = 3;
+    // Similar to DEVICE, but the layer position may have been asynchronously set
+    // through setCursorPosition
+    CURSOR = 4;
+    // Layer was composited by the device via a sideband stream.
+    SIDEBAND = 5;
 }
 
 // Information about each layer.
@@ -77,7 +90,7 @@
   int32 window_type = 33 [deprecated=true];
   int32 app_id = 34 [deprecated=true];
   // The layer's composition type
-  int32 hwc_composition_type = 35;
+  HwcCompositionType hwc_composition_type = 35;
   // If it's a buffer layer, indicate if the content is protected
   bool is_protected = 36;
   // Current frame number being rendered.
@@ -98,6 +111,18 @@
   FloatRectProto screen_bounds = 46;
 
   InputWindowInfoProto input_window_info = 47;
+
+  // Crop used to draw the rounded corner.
+  FloatRectProto corner_radius_crop = 48;
+
+  // length of the shadow to draw around the layer, it may be set on the
+  // layer or set by a parent layer.
+  float shadow_radius = 49;
+  ColorTransformProto color_transform = 50;
+
+  bool is_relative_of = 51;
+  // Layer's background blur radius in pixels.
+  int32 background_blur_radius = 52;
 }
 
 message PositionProto {
@@ -178,3 +203,8 @@
     bool replace_touchable_region_with_crop = 14;
     RectProto touchable_region_crop = 15;
 }
+
+message ColorTransformProto {
+  // This will be a 4x4 matrix of float values
+  repeated float val = 1;
+}
diff --git a/services/surfaceflinger/layerproto/layerstrace.proto b/services/surfaceflinger/layerproto/layerstrace.proto
index bee17d2..acf621e 100644
--- a/services/surfaceflinger/layerproto/layerstrace.proto
+++ b/services/surfaceflinger/layerproto/layerstrace.proto
@@ -48,4 +48,13 @@
     optional string where = 2;
 
     optional LayersProto layers = 3;
+
+    // Blob for the current HWC information for all layers, reported by dumpsys.
+    optional string hwc_blob = 4;
+
+    /* Includes state sent during composition like visible region and composition type. */
+    optional bool excludes_composition_state = 5;
+
+    /* Number of missed entries since the last entry was recorded. */
+    optional int32 missed_entries = 6;
 }
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index e7986d3..2b8424c 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <sys/resource.h>
 
 #include <sched.h>
@@ -27,6 +31,7 @@
 #include <binder/ProcessState.h>
 #include <configstore/Utils.h>
 #include <displayservice/DisplayService.h>
+#include <errno.h>
 #include <hidl/LegacySupport.h>
 #include <processgroup/sched_policy.h>
 #include "SurfaceFlinger.h"
@@ -110,10 +115,8 @@
 
     startDisplayService(); // dependency on SF getting registered above
 
-    struct sched_param param = {0};
-    param.sched_priority = 2;
-    if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
-        ALOGE("Couldn't set SCHED_FIFO");
+    if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
+        ALOGW("Couldn't set to SCHED_FIFO: %s", strerror(errno));
     }
 
     // run surface flinger in this thread
@@ -121,3 +124,6 @@
 
     return 0;
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 8ed454f..7b1f0fb 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -67,6 +67,26 @@
     prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
 }
 
+# Controls the maximum width in pixels that the graphics pipeline can support for GPU fallback
+# composition. For example, 8k displays with 4k GPUs, or 4k displays with 2k GPUs.
+prop {
+    api_name: "max_graphics_width"
+    type: Integer
+    scope: System
+    access: Readonly
+    prop_name: "ro.surface_flinger.max_graphics_width"
+}
+
+# Controls the maximum height in pixels that the graphics pipeline can support for GPU fallback
+# composition. For example, 8k displays with 4k GPUs, or 4k displays with 2k GPUs.
+prop {
+    api_name: "max_graphics_height"
+    type: Integer
+    scope: System
+    access: Readonly
+    prop_name: "ro.surface_flinger.max_graphics_height"
+}
+
 # hasWideColorDisplay indicates that the device has
 # or can support a wide-color display, e.g. color space
 # greater than sRGB. Typical display may have same
@@ -311,11 +331,9 @@
     scope: Public
     access: Readonly
     prop_name: "ro.surface_flinger.refresh_rate_switching"
+    deprecated: true
 }
 
-# setIdleTimerMs indicates what is considered a timeout in milliseconds for Scheduler. This value is
-# used by the Scheduler to trigger inactivity callbacks that will switch the display to a lower
-# refresh rate. Setting this property to 0 means there is no timer.
 prop {
     api_name: "set_idle_timer_ms"
     type: Integer
@@ -347,14 +365,26 @@
     prop_name: "ro.surface_flinger.set_display_power_timer_ms"
 }
 
+# useContentDetectionForRefreshRate indicates whether Scheduler should detect content FPS, and try
+# to adjust the screen refresh rate based on that.
+prop {
+    api_name: "use_content_detection_for_refresh_rate"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.use_content_detection_for_refresh_rate"
+}
+
 # useSmart90ForVideo indicates whether Scheduler should detect content FPS, and try to adjust the
 # screen refresh rate based on that.
+# Replaced by useContentDetectionForRefreshRate
 prop {
     api_name: "use_smart_90_for_video"
     type: Boolean
     scope: Public
     access: Readonly
     prop_name: "ro.surface_flinger.use_smart_90_for_video"
+    deprecated: true
 }
 
 prop {
@@ -374,3 +404,34 @@
     access: Readonly
     prop_name: "ro.surface_flinger.support_kernel_idle_timer"
 }
+
+# Indicates whether background blurs are supported.
+prop {
+    api_name: "supports_background_blur"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.supports_background_blur"
+}
+
+# Indicates whether Scheduler should use frame rate API when adjusting the
+# display refresh rate.
+prop {
+    api_name: "use_frame_rate_api"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.use_frame_rate_api"
+}
+
+# Sets the timeout used to rate limit DISPLAY_UPDATE_IMMINENT Power HAL notifications.
+# SurfaceFlinger wakeups will trigger this boost whenever they are separated by more than this
+# duration (specified in milliseconds). A value of 0 disables the rate limit, and will result in
+# Power HAL notifications every time SF wakes up.
+prop {
+    api_name: "display_update_imminent_timeout_ms"
+    type: Integer
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.display_update_imminent_timeout_ms"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 2d52507..ba60a7d 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -36,6 +36,11 @@
     prop_name: "ro.surface_flinger.display_primary_white"
   }
   prop {
+    api_name: "display_update_imminent_timeout_ms"
+    type: Integer
+    prop_name: "ro.surface_flinger.display_update_imminent_timeout_ms"
+  }
+  prop {
     api_name: "enable_protected_contents"
     prop_name: "ro.surface_flinger.protected_contents"
   }
@@ -57,6 +62,16 @@
     prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
   }
   prop {
+    api_name: "max_graphics_height"
+    type: Integer
+    prop_name: "ro.surface_flinger.max_graphics_height"
+  }
+  prop {
+    api_name: "max_graphics_width"
+    type: Integer
+    prop_name: "ro.surface_flinger.max_graphics_width"
+  }
+  prop {
     api_name: "max_virtual_display_dimension"
     type: Long
     prop_name: "ro.surface_flinger.max_virtual_display_dimension"
@@ -75,6 +90,7 @@
   prop {
     api_name: "refresh_rate_switching"
     prop_name: "ro.surface_flinger.refresh_rate_switching"
+    deprecated: true
   }
   prop {
     api_name: "running_without_sync_framework"
@@ -104,16 +120,29 @@
     prop_name: "ro.surface_flinger.support_kernel_idle_timer"
   }
   prop {
+    api_name: "supports_background_blur"
+    prop_name: "ro.surface_flinger.supports_background_blur"
+  }
+  prop {
     api_name: "use_color_management"
     prop_name: "ro.surface_flinger.use_color_management"
   }
   prop {
+    api_name: "use_content_detection_for_refresh_rate"
+    prop_name: "ro.surface_flinger.use_content_detection_for_refresh_rate"
+  }
+  prop {
     api_name: "use_context_priority"
     prop_name: "ro.surface_flinger.use_context_priority"
   }
   prop {
+    api_name: "use_frame_rate_api"
+    prop_name: "ro.surface_flinger.use_frame_rate_api"
+  }
+  prop {
     api_name: "use_smart_90_for_video"
     prop_name: "ro.surface_flinger.use_smart_90_for_video"
+    deprecated: true
   }
   prop {
     api_name: "use_vr_flinger"
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 53a3611..fe2af80 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -18,11 +18,25 @@
     test_suites: ["device-tests"],
     srcs: [
         "BufferGenerator.cpp",
+        "CommonTypes_test.cpp",
         "Credentials_test.cpp",
+        "DereferenceSurfaceControl_test.cpp",
+        "DisplayConfigs_test.cpp",
+        "EffectLayer_test.cpp",
         "InvalidHandles_test.cpp",
+        "LayerCallback_test.cpp",
+        "LayerRenderTypeTransaction_test.cpp",
+        "LayerTransaction_test.cpp",
+        "LayerTypeAndRenderTypeTransaction_test.cpp",
+        "LayerTypeTransaction_test.cpp",
+        "LayerUpdate_test.cpp",
+        "MirrorLayer_test.cpp",
+        "MultiDisplayLayerBounds_test.cpp",
+        "RelativeZ_test.cpp",
+        "SetFrameRate_test.cpp",
+        "SetGeometry_test.cpp",
         "Stress_test.cpp",
         "SurfaceInterceptor_test.cpp",
-        "Transaction_test.cpp",
         "VirtualDisplay_test.cpp",
     ],
     data: ["SurfaceFlinger_test.filter"],
@@ -30,6 +44,52 @@
         "libtrace_proto",
     ],
     shared_libs: [
+        "android.hardware.graphics.common-ndk_platform",
+        "android.hardware.graphics.common@1.2",
+        "android.hardware.graphics.composer@2.1",
+        "libandroid",
+        "libbinder",
+        "libcutils",
+        "libEGL",
+        "libGLESv2",
+        "libgui",
+        "liblayers_proto",
+        "liblog",
+        "libnativewindow",
+        "libprotobuf-cpp-full",
+        "libui",
+        "libutils",
+    ],
+    header_libs: [
+        "libnativewindow_headers",
+    ],
+}
+
+cc_defaults {
+    name: "ipc_defaults",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
+cc_test {
+    name: "IPC_test",
+    defaults: ["ipc_defaults"],
+    test_suites: ["device-tests"],
+    srcs: [
+        "BufferGenerator.cpp",
+        "IPC_test.cpp",
+    ],
+    cppflags: [
+        "-Wall",
+        "-Werror",
+        "-Wformat",
+        "-Wthread-safety",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+    shared_libs: [
         "libandroid",
         "libbinder",
         "libcutils",
@@ -39,17 +99,18 @@
         "liblayers_proto",
         "liblog",
         "libprotobuf-cpp-full",
-        "libtimestats_proto",
         "libui",
         "libutils",
-    ]
-
+    ],
+    cpp_std: "experimental",
+    gnu_extensions: false,
 }
 
 subdirs = [
     "fakehwc",
     "hwc2",
     "unittests",
+    "utils",
     "vsync",
     "waitforvsync",
 ]
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
index 8ddda60..4868c12 100644
--- a/services/surfaceflinger/tests/BufferGenerator.cpp
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <gui/BufferItemConsumer.h>
 #include <gui/Surface.h>
 
@@ -84,7 +88,7 @@
     sp<Surface> mSurface;
 };
 
-/* Used to generate valid fences. It is not possible to create a dummy sync
+/* Used to generate valid fences. It is not possible to create a placeholder sync
  * fence for testing. Egl can generate buffers along with a valid fence.
  * The buffer cannot be guaranteed to be the same format across all devices so
  * a CPU filled buffer is used instead. The Egl fence is used along with the
@@ -379,3 +383,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/CommonTypes_test.cpp b/services/surfaceflinger/tests/CommonTypes_test.cpp
new file mode 100644
index 0000000..25b4615
--- /dev/null
+++ b/services/surfaceflinger/tests/CommonTypes_test.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+
+#include <android/data_space.h>
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <android/hardware/graphics/composer/2.1/IComposerClient.h>
+
+using AidlBlendMode = aidl::android::hardware::graphics::common::BlendMode;
+using AidlDataspace = aidl::android::hardware::graphics::common::Dataspace;
+
+using HidlBlendMode = android::hardware::graphics::composer::V2_1::IComposerClient::BlendMode;
+using HidlDataspace = android::hardware::graphics::common::V1_2::Dataspace;
+
+static_assert(static_cast<uint32_t>(AidlBlendMode::INVALID) ==
+              static_cast<uint32_t>(HidlBlendMode::INVALID));
+static_assert(static_cast<uint32_t>(AidlBlendMode::NONE) ==
+              static_cast<uint32_t>(HidlBlendMode::NONE));
+static_assert(static_cast<uint32_t>(AidlBlendMode::PREMULTIPLIED) ==
+              static_cast<uint32_t>(HidlBlendMode::PREMULTIPLIED));
+static_assert(static_cast<uint32_t>(AidlBlendMode::COVERAGE) ==
+              static_cast<uint32_t>(HidlBlendMode::COVERAGE));
+
+static_assert(static_cast<uint32_t>(ADATASPACE_UNKNOWN) ==
+              static_cast<uint32_t>(AidlDataspace::UNKNOWN));
+static_assert(static_cast<uint32_t>(ADATASPACE_SCRGB_LINEAR) ==
+              static_cast<uint32_t>(AidlDataspace::SCRGB_LINEAR));
+static_assert(static_cast<uint32_t>(ADATASPACE_SRGB) == static_cast<uint32_t>(AidlDataspace::SRGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_SCRGB) ==
+              static_cast<uint32_t>(AidlDataspace::SCRGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_DISPLAY_P3) ==
+              static_cast<uint32_t>(AidlDataspace::DISPLAY_P3));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT2020_PQ) ==
+              static_cast<uint32_t>(AidlDataspace::BT2020_PQ));
+static_assert(static_cast<uint32_t>(ADATASPACE_ADOBE_RGB) ==
+              static_cast<uint32_t>(AidlDataspace::ADOBE_RGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT2020) ==
+              static_cast<uint32_t>(AidlDataspace::BT2020));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT709) ==
+              static_cast<uint32_t>(AidlDataspace::BT709));
+static_assert(static_cast<uint32_t>(ADATASPACE_DCI_P3) ==
+              static_cast<uint32_t>(AidlDataspace::DCI_P3));
+static_assert(static_cast<uint32_t>(ADATASPACE_SRGB_LINEAR) ==
+              static_cast<uint32_t>(AidlDataspace::SRGB_LINEAR));
+
+static_assert(static_cast<uint32_t>(ADATASPACE_UNKNOWN) ==
+              static_cast<uint32_t>(HidlDataspace::UNKNOWN));
+static_assert(static_cast<uint32_t>(ADATASPACE_SCRGB_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SCRGB_LINEAR));
+static_assert(static_cast<uint32_t>(ADATASPACE_SRGB) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SRGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_SCRGB) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SCRGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_DISPLAY_P3) ==
+              static_cast<uint32_t>(HidlDataspace::DISPLAY_P3));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT2020_PQ) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_PQ));
+static_assert(static_cast<uint32_t>(ADATASPACE_ADOBE_RGB) ==
+              static_cast<uint32_t>(HidlDataspace::ADOBE_RGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT2020) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT709) ==
+              static_cast<uint32_t>(HidlDataspace::V0_BT709));
+static_assert(static_cast<uint32_t>(ADATASPACE_DCI_P3) ==
+              static_cast<uint32_t>(HidlDataspace::DCI_P3));
+static_assert(static_cast<uint32_t>(ADATASPACE_SRGB_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SRGB_LINEAR));
+
+static_assert(static_cast<uint32_t>(AidlDataspace::UNKNOWN) ==
+              static_cast<uint32_t>(HidlDataspace::UNKNOWN));
+static_assert(static_cast<uint32_t>(AidlDataspace::ARBITRARY) ==
+              static_cast<uint32_t>(HidlDataspace::ARBITRARY));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_SHIFT) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_SHIFT));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_MASK) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_MASK));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_UNSPECIFIED) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_UNSPECIFIED));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT709) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT709));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_625) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_625));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_625_UNADJUSTED) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_625_UNADJUSTED));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_525) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_525));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_525_UNADJUSTED) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_525_UNADJUSTED));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT2020) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT2020));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT2020_CONSTANT_LUMINANCE) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT2020_CONSTANT_LUMINANCE));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT470M) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT470M));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_FILM) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_FILM));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_DCI_P3) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_DCI_P3));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_ADOBE_RGB) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_ADOBE_RGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_SHIFT) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_SHIFT));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_MASK) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_MASK));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_UNSPECIFIED) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_UNSPECIFIED));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_SRGB) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_SRGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_SMPTE_170M) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_SMPTE_170M));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_GAMMA2_2) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_GAMMA2_2));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_GAMMA2_6) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_GAMMA2_6));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_GAMMA2_8) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_GAMMA2_8));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_ST2084) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_ST2084));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_HLG) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_HLG));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_SHIFT) ==
+              static_cast<uint32_t>(HidlDataspace::RANGE_SHIFT));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_MASK) ==
+              static_cast<uint32_t>(HidlDataspace::RANGE_MASK));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_UNSPECIFIED) ==
+              static_cast<uint32_t>(HidlDataspace::RANGE_UNSPECIFIED));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_FULL) ==
+              static_cast<uint32_t>(HidlDataspace::RANGE_FULL));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_LIMITED) ==
+              static_cast<uint32_t>(HidlDataspace::RANGE_LIMITED));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_EXTENDED) ==
+              static_cast<uint32_t>(HidlDataspace::RANGE_EXTENDED));
+static_assert(static_cast<uint32_t>(AidlDataspace::SRGB_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SRGB_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::SCRGB_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SCRGB_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::SRGB) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SRGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::SCRGB) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SCRGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::JFIF) ==
+              static_cast<uint32_t>(HidlDataspace::V0_JFIF));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT601_625) ==
+              static_cast<uint32_t>(HidlDataspace::V0_BT601_625));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT601_525) ==
+              static_cast<uint32_t>(HidlDataspace::V0_BT601_525));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT709) ==
+              static_cast<uint32_t>(HidlDataspace::V0_BT709));
+static_assert(static_cast<uint32_t>(AidlDataspace::DCI_P3_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::DCI_P3_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::DCI_P3) ==
+              static_cast<uint32_t>(HidlDataspace::DCI_P3));
+static_assert(static_cast<uint32_t>(AidlDataspace::DISPLAY_P3_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::DISPLAY_P3_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::DISPLAY_P3) ==
+              static_cast<uint32_t>(HidlDataspace::DISPLAY_P3));
+static_assert(static_cast<uint32_t>(AidlDataspace::ADOBE_RGB) ==
+              static_cast<uint32_t>(HidlDataspace::ADOBE_RGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_PQ) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_PQ));
+static_assert(static_cast<uint32_t>(AidlDataspace::DEPTH) ==
+              static_cast<uint32_t>(HidlDataspace::DEPTH));
+static_assert(static_cast<uint32_t>(AidlDataspace::SENSOR) ==
+              static_cast<uint32_t>(HidlDataspace::SENSOR));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_ITU) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_ITU));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_ITU_PQ) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_ITU_PQ));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_ITU_HLG) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_ITU_HLG));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_HLG) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_HLG));
+static_assert(static_cast<uint32_t>(AidlDataspace::DISPLAY_BT2020) ==
+              static_cast<uint32_t>(HidlDataspace::DISPLAY_BT2020));
+static_assert(static_cast<uint32_t>(AidlDataspace::DYNAMIC_DEPTH) ==
+              static_cast<uint32_t>(HidlDataspace::DYNAMIC_DEPTH));
+static_assert(static_cast<uint32_t>(AidlDataspace::JPEG_APP_SEGMENTS) ==
+              static_cast<uint32_t>(HidlDataspace::JPEG_APP_SEGMENTS));
+static_assert(static_cast<uint32_t>(AidlDataspace::HEIF) ==
+              static_cast<uint32_t>(HidlDataspace::HEIF));
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index b667a74..c136708 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -1,20 +1,15 @@
-#include <algorithm>
-#include <functional>
-#include <limits>
-#include <ostream>
-
 #include <gtest/gtest.h>
-
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
-
 #include <private/android_filesystem_config.h>
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
 #include <utils/String8.h>
 
+#include <functional>
+
 namespace android {
 
 using Transaction = SurfaceComposerClient::Transaction;
@@ -23,7 +18,6 @@
 namespace {
 const String8 DISPLAY_NAME("Credentials Display Test");
 const String8 SURFACE_NAME("Test Surface Name");
-const uint32_t ROTATION = 0;
 const float FRAME_SCALE = 1.0f;
 } // namespace
 
@@ -32,6 +26,10 @@
  * Methods like EnableVsyncInjections and InjectVsync are not tested since they do not
  * return anything meaningful.
  */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 class CredentialsTest : public ::testing::Test {
 protected:
     void SetUp() override {
@@ -64,14 +62,13 @@
         mDisplay = SurfaceComposerClient::getInternalDisplayToken();
         ASSERT_FALSE(mDisplay == nullptr);
 
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
-        const ssize_t displayWidth = info.w;
-        const ssize_t displayHeight = info.h;
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplay, &config));
 
         // Background surface
         mBGSurfaceControl =
-                mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight,
+                mComposerClient->createSurface(SURFACE_NAME, config.resolution.getWidth(),
+                                               config.resolution.getHeight(),
                                                PIXEL_FORMAT_RGBA_8888, 0);
         ASSERT_TRUE(mBGSurfaceControl != nullptr);
         ASSERT_TRUE(mBGSurfaceControl->isValid());
@@ -185,10 +182,10 @@
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     ASSERT_TRUE(display != nullptr);
 
-    DisplayInfo info;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+    DisplayConfig config;
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
 
-    Vector<DisplayInfo> configs;
+    Vector<DisplayConfig> configs;
     ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
 
     ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveConfig(display));
@@ -215,10 +212,24 @@
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
 }
 
-TEST_F(CredentialsTest, SetActiveConfigTest) {
+TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) {
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    int32_t defaultConfig;
+    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::setActiveConfig(display, 0);
+        return SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, defaultConfig,
+                                                                   primaryFpsMin, primaryFpsMax,
+                                                                   appRequestFpsMin,
+                                                                   appRequestFpsMax);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
@@ -245,24 +256,13 @@
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
 }
 
-TEST_F(CredentialsTest, DISABLED_DestroyDisplayTest) {
-    setupVirtualDisplay();
-
-    DisplayInfo info;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info));
-    SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
-    // This test currently fails. TODO(b/112002626): Find a way to properly create
-    // a display in the test environment, so that destroy display can remove it.
-    ASSERT_EQ(NAME_NOT_FOUND, SurfaceComposerClient::getDisplayInfo(mVirtualDisplay, &info));
-}
-
 TEST_F(CredentialsTest, CaptureTest) {
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     std::function<status_t()> condition = [=]() {
         sp<GraphicBuffer> outBuffer;
         return ScreenshotClient::capture(display, ui::Dataspace::V0_SRGB,
                                          ui::PixelFormat::RGBA_8888, Rect(), 0 /*reqWidth*/,
-                                         0 /*reqHeight*/, false, ROTATION, &outBuffer);
+                                         0 /*reqHeight*/, false, ui::ROTATION_0, &outBuffer);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
@@ -274,7 +274,7 @@
         sp<GraphicBuffer> outBuffer;
         return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
                                                ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
-                                               Rect(), FRAME_SCALE, &outBuffer);
+                                               Rect(0, 0, 1, 1), FRAME_SCALE, &outBuffer);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
@@ -363,3 +363,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp b/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp
new file mode 100644
index 0000000..46b98f9
--- /dev/null
+++ b/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class DereferenceSurfaceControlTest : public LayerTransactionTest {
+protected:
+    void SetUp() override {
+        LayerTransactionTest::SetUp();
+        bgLayer = createLayer("BG layer", 20, 20);
+        fillBufferQueueLayerColor(bgLayer, Color::RED, 20, 20);
+        fgLayer = createLayer("FG layer", 20, 20);
+        fillBufferQueueLayerColor(fgLayer, Color::BLUE, 20, 20);
+        Transaction().setLayer(fgLayer, mLayerZBase + 1).apply();
+        {
+            SCOPED_TRACE("before anything");
+            auto shot = screenshot();
+            shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE);
+        }
+    }
+    void TearDown() override {
+        LayerTransactionTest::TearDown();
+        bgLayer = 0;
+        fgLayer = 0;
+    }
+
+    sp<SurfaceControl> bgLayer;
+    sp<SurfaceControl> fgLayer;
+};
+
+TEST_F(DereferenceSurfaceControlTest, LayerNotInTransaction) {
+    fgLayer = nullptr;
+    {
+        SCOPED_TRACE("after setting null");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 20, 20), Color::RED);
+    }
+}
+
+TEST_F(DereferenceSurfaceControlTest, LayerInTransaction) {
+    auto transaction = Transaction().show(fgLayer);
+    fgLayer = nullptr;
+    {
+        SCOPED_TRACE("after setting null");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE);
+    }
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
new file mode 100644
index 0000000..debfe83
--- /dev/null
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <thread>
+#include "LayerTransactionTest.h"
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+/**
+ * Test class for setting display configs and passing around refresh rate ranges.
+ */
+class RefreshRateRangeTest : public ::testing::Test {
+protected:
+    void SetUp() override { mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); }
+
+    sp<IBinder> mDisplayToken;
+};
+
+TEST_F(RefreshRateRangeTest, setAllConfigs) {
+    int32_t initialDefaultConfig;
+    float initialPrimaryMin;
+    float initialPrimaryMax;
+    float initialAppRequestMin;
+    float initialAppRequestMax;
+    status_t res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken,
+                                                                       &initialDefaultConfig,
+                                                                       &initialPrimaryMin,
+                                                                       &initialPrimaryMax,
+                                                                       &initialAppRequestMin,
+                                                                       &initialAppRequestMax);
+    ASSERT_EQ(res, NO_ERROR);
+
+    Vector<DisplayConfig> configs;
+    res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs);
+    ASSERT_EQ(res, NO_ERROR);
+
+    for (size_t i = 0; i < configs.size(); i++) {
+        res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, i,
+                                                                  configs[i].refreshRate,
+                                                                  configs[i].refreshRate,
+                                                                  configs[i].refreshRate,
+                                                                  configs[i].refreshRate);
+        ASSERT_EQ(res, NO_ERROR);
+
+        int defaultConfig;
+        float primaryRefreshRateMin;
+        float primaryRefreshRateMax;
+        float appRequestRefreshRateMin;
+        float appRequestRefreshRateMax;
+        res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
+                                                                  &primaryRefreshRateMin,
+                                                                  &primaryRefreshRateMax,
+                                                                  &appRequestRefreshRateMin,
+                                                                  &appRequestRefreshRateMax);
+        ASSERT_EQ(res, NO_ERROR);
+        ASSERT_EQ(defaultConfig, i);
+        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,
+                                                              initialPrimaryMin, initialPrimaryMax,
+                                                              initialAppRequestMin,
+                                                              initialAppRequestMax);
+    ASSERT_EQ(res, NO_ERROR);
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp
new file mode 100644
index 0000000..3dca391
--- /dev/null
+++ b/services/surfaceflinger/tests/EffectLayer_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class EffectLayerTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        ASSERT_FALSE(display == nullptr);
+
+        mParentLayer = createColorLayer("Parent layer", Color::RED);
+        asTransaction([&](Transaction& t) {
+            t.setDisplayLayerStack(display, 0);
+            t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
+            t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+        });
+    }
+
+    virtual void TearDown() {
+        LayerTransactionTest::TearDown();
+        mParentLayer = 0;
+    }
+
+    sp<SurfaceControl> mParentLayer;
+};
+
+TEST_F(EffectLayerTest, DefaultEffectLayerHasSolidBlackFill) {
+    sp<SurfaceControl> effectLayer =
+            mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+                                   PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect,
+                                   mParentLayer.get());
+
+    EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+    asTransaction([&](Transaction& t) {
+        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.show(effectLayer);
+    });
+
+    {
+        SCOPED_TRACE("Default effect Layer has solid black fill");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 400, 400), Color::BLACK);
+    }
+}
+
+TEST_F(EffectLayerTest, EffectLayerWithNoFill) {
+    sp<SurfaceControl> effectLayer =
+            mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+                                   PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceEffect |
+                                           ISurfaceComposerClient::eNoColorFill,
+                                   mParentLayer.get());
+
+    EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+    asTransaction([&](Transaction& t) {
+        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.show(effectLayer);
+    });
+
+    {
+        SCOPED_TRACE("Effect layer with nofill option is transparent");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 400, 400), Color::RED);
+    }
+}
+
+TEST_F(EffectLayerTest, EffectLayerCanSetColor) {
+    sp<SurfaceControl> effectLayer =
+            mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
+                                   PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceEffect |
+                                           ISurfaceComposerClient::eNoColorFill,
+                                   mParentLayer.get());
+
+    EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl";
+    asTransaction([&](Transaction& t) {
+        t.setCrop_legacy(effectLayer, Rect(0, 0, 400, 400));
+        t.setColor(effectLayer,
+                   half3{Color::GREEN.r / 255.0f, Color::GREEN.g / 255.0f,
+                         Color::GREEN.b / 255.0f});
+        t.show(effectLayer);
+    });
+
+    {
+        SCOPED_TRACE("Effect Layer can set color");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 400, 400), Color::GREEN);
+    }
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
new file mode 100644
index 0000000..4023c66
--- /dev/null
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/LayerState.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+#include <utils/String8.h>
+
+#include <limits>
+
+#include "BufferGenerator.h"
+#include "utils/CallbackUtils.h"
+#include "utils/ColorUtils.h"
+#include "utils/TransactionUtils.h"
+
+namespace android {
+
+namespace test {
+
+using Transaction = SurfaceComposerClient::Transaction;
+using CallbackInfo = SurfaceComposerClient::CallbackInfo;
+using TCLHash = SurfaceComposerClient::TCLHash;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class TransactionHelper : public Transaction {
+public:
+    size_t getNumListeners() { return mListenerCallbacks.size(); }
+
+    std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
+    getListenerCallbacks() {
+        return mListenerCallbacks;
+    }
+};
+
+class IPCTestUtils {
+public:
+    static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+                                bool finalState = false);
+    static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
+};
+
+class IIPCTest : public IInterface {
+public:
+    DECLARE_META_INTERFACE(IPCTest)
+    enum class Tag : uint32_t {
+        SetDeathToken = IBinder::FIRST_CALL_TRANSACTION,
+        InitClient,
+        CreateTransaction,
+        MergeAndApply,
+        VerifyCallbacks,
+        CleanUp,
+        Last,
+    };
+
+    virtual status_t setDeathToken(sp<IBinder>& token) = 0;
+
+    virtual status_t initClient() = 0;
+
+    virtual status_t createTransaction(TransactionHelper* outTransaction, uint32_t width,
+                                       uint32_t height) = 0;
+
+    virtual status_t mergeAndApply(TransactionHelper transaction) = 0;
+
+    virtual status_t verifyCallbacks() = 0;
+
+    virtual status_t cleanUp() = 0;
+};
+
+class BpIPCTest : public SafeBpInterface<IIPCTest> {
+public:
+    explicit BpIPCTest(const sp<IBinder>& impl) : SafeBpInterface<IIPCTest>(impl, "BpIPCTest") {}
+
+    status_t setDeathToken(sp<IBinder>& token) {
+        return callRemote<decltype(&IIPCTest::setDeathToken)>(Tag::SetDeathToken, token);
+    }
+
+    status_t initClient() { return callRemote<decltype(&IIPCTest::initClient)>(Tag::InitClient); }
+
+    status_t createTransaction(TransactionHelper* transaction, uint32_t width, uint32_t height) {
+        return callRemote<decltype(&IIPCTest::createTransaction)>(Tag::CreateTransaction,
+                                                                  transaction, width, height);
+    }
+
+    status_t mergeAndApply(TransactionHelper transaction) {
+        return callRemote<decltype(&IIPCTest::mergeAndApply)>(Tag::MergeAndApply, transaction);
+    }
+
+    status_t verifyCallbacks() {
+        return callRemote<decltype(&IIPCTest::verifyCallbacks)>(Tag::VerifyCallbacks);
+    }
+
+    status_t cleanUp() { return callRemote<decltype(&IIPCTest::cleanUp)>(Tag::CleanUp); }
+};
+
+IMPLEMENT_META_INTERFACE(IPCTest, "android.gfx.tests.IIPCTest")
+
+class onTestDeath : public IBinder::DeathRecipient {
+public:
+    void binderDied(const wp<IBinder>& /*who*/) override {
+        ALOGE("onTestDeath::binderDied, exiting");
+        exit(0);
+    }
+};
+
+sp<onTestDeath> getDeathToken() {
+    static sp<onTestDeath> token = new onTestDeath;
+    return token;
+}
+
+class BnIPCTest : public SafeBnInterface<IIPCTest> {
+public:
+    BnIPCTest() : SafeBnInterface("BnIPCTest") {}
+
+    status_t setDeathToken(sp<IBinder>& token) override {
+        return token->linkToDeath(getDeathToken());
+    }
+
+    status_t initClient() override {
+        mClient = new SurfaceComposerClient;
+        auto err = mClient->initCheck();
+        return err;
+    }
+
+    status_t createTransaction(TransactionHelper* transaction, uint32_t width, uint32_t height) {
+        if (transaction == nullptr) {
+            ALOGE("Error in createTransaction: transaction is nullptr");
+            return BAD_VALUE;
+        }
+        mSurfaceControl = mClient->createSurface(String8("parentProcessSurface"), 0, 0,
+                                                 PIXEL_FORMAT_RGBA_8888,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                 /*parent*/ nullptr);
+        sp<GraphicBuffer> gb;
+        sp<Fence> fence;
+        int err = IPCTestUtils::getBuffer(&gb, &fence);
+        if (err != NO_ERROR) return err;
+
+        TransactionUtils::fillGraphicBufferColor(gb,
+                                                 {0, 0, static_cast<int32_t>(width),
+                                                  static_cast<int32_t>(height)},
+                                                 Color::RED);
+        transaction->setLayerStack(mSurfaceControl, 0)
+                .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
+                .setFrame(mSurfaceControl, Rect(0, 0, width, height))
+                .setBuffer(mSurfaceControl, gb)
+                .setAcquireFence(mSurfaceControl, fence)
+                .show(mSurfaceControl)
+                .addTransactionCompletedCallback(mCallbackHelper.function,
+                                                 mCallbackHelper.getContext());
+        return NO_ERROR;
+    }
+
+    status_t mergeAndApply(TransactionHelper /*transaction*/) {
+        // transaction.apply();
+        return NO_ERROR;
+    }
+
+    status_t verifyCallbacks() {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, mSurfaceControl);
+        EXPECT_NO_FATAL_FAILURE(IPCTestUtils::waitForCallback(mCallbackHelper, expected, true));
+        return NO_ERROR;
+    }
+
+    status_t cleanUp() {
+        if (mClient) mClient->dispose();
+        mSurfaceControl = nullptr;
+        IPCThreadState::self()->stopProcess();
+        return NO_ERROR;
+    }
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t /*flags*/) override {
+        EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION);
+        EXPECT_LT(code, static_cast<uint32_t>(IIPCTest::Tag::Last));
+        switch (static_cast<IIPCTest::Tag>(code)) {
+            case IIPCTest::Tag::SetDeathToken:
+                return callLocal(data, reply, &IIPCTest::setDeathToken);
+            case IIPCTest::Tag::InitClient:
+                return callLocal(data, reply, &IIPCTest::initClient);
+            case IIPCTest::Tag::CreateTransaction:
+                return callLocal(data, reply, &IIPCTest::createTransaction);
+            case IIPCTest::Tag::MergeAndApply:
+                return callLocal(data, reply, &IIPCTest::mergeAndApply);
+            case IIPCTest::Tag::VerifyCallbacks:
+                return callLocal(data, reply, &IIPCTest::verifyCallbacks);
+            case IIPCTest::Tag::CleanUp:
+                return callLocal(data, reply, &IIPCTest::cleanUp);
+            default:
+                return UNKNOWN_ERROR;
+        }
+    }
+
+private:
+    sp<SurfaceComposerClient> mClient;
+    sp<SurfaceControl> mSurfaceControl;
+    CallbackHelper mCallbackHelper;
+};
+
+class IPCTest : public ::testing::Test {
+public:
+    IPCTest() : mDeathRecipient(new BBinder), mRemote(initRemoteService()) {
+        ProcessState::self()->startThreadPool();
+    }
+    void SetUp() {
+        mClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        mPrimaryDisplay = mClient->getInternalDisplayToken();
+        DisplayConfig config;
+        mClient->getActiveDisplayConfig(mPrimaryDisplay, &config);
+        mDisplayWidth = config.resolution.getWidth();
+        mDisplayHeight = config.resolution.getHeight();
+
+        Transaction setupTransaction;
+        setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0);
+        setupTransaction.apply();
+    }
+
+protected:
+    sp<IIPCTest> initRemoteService();
+
+    sp<IBinder> mDeathRecipient;
+    sp<IIPCTest> mRemote;
+    sp<SurfaceComposerClient> mClient;
+    sp<IBinder> mPrimaryDisplay;
+    uint32_t mDisplayWidth;
+    uint32_t mDisplayHeight;
+    sp<SurfaceControl> sc;
+};
+
+status_t IPCTestUtils::getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+    static BufferGenerator bufferGenerator;
+    return bufferGenerator.get(outBuffer, outFence);
+}
+
+void IPCTestUtils::waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+                                   bool finalState) {
+    CallbackData callbackData;
+    ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
+    EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData));
+
+    if (finalState) {
+        ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+    }
+}
+
+sp<IIPCTest> IPCTest::initRemoteService() {
+    static std::mutex mMutex;
+    static sp<IIPCTest> remote;
+    const String16 serviceName("IPCTest");
+
+    std::unique_lock<decltype(mMutex)> lock;
+    if (remote == nullptr) {
+        pid_t forkPid = fork();
+        EXPECT_NE(forkPid, -1);
+
+        if (forkPid == 0) {
+            sp<IIPCTest> nativeService = new BnIPCTest;
+            if (!nativeService) {
+                ALOGE("null service...");
+            }
+            status_t err = defaultServiceManager()->addService(serviceName,
+                                                               IInterface::asBinder(nativeService));
+            if (err != NO_ERROR) {
+                ALOGE("failed to add service: %d", err);
+            }
+            ProcessState::self()->startThreadPool();
+            IPCThreadState::self()->joinThreadPool();
+            [&]() { exit(0); }();
+        }
+        sp<IBinder> binder = defaultServiceManager()->getService(serviceName);
+        remote = interface_cast<IIPCTest>(binder);
+        remote->setDeathToken(mDeathRecipient);
+    }
+    return remote;
+}
+
+TEST_F(IPCTest, MergeBasic) {
+    CallbackHelper helper1;
+    sc = mClient->createSurface(String8("parentProcessSurface"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                ISurfaceComposerClient::eFXSurfaceBufferState,
+                                /*parent*/ nullptr);
+    sp<GraphicBuffer> gb;
+    sp<Fence> fence;
+    int err = IPCTestUtils::getBuffer(&gb, &fence);
+    ASSERT_EQ(NO_ERROR, err);
+    TransactionUtils::fillGraphicBufferColor(gb,
+                                             {0, 0, static_cast<int32_t>(mDisplayWidth),
+                                              static_cast<int32_t>(mDisplayHeight)},
+                                             Color::RED);
+
+    Transaction transaction;
+    transaction.setLayerStack(sc, 0)
+            .setLayer(sc, std::numeric_limits<int32_t>::max() - 1)
+            .setBuffer(sc, gb)
+            .setAcquireFence(sc, fence)
+            .show(sc)
+            .addTransactionCompletedCallback(helper1.function, helper1.getContext());
+
+    TransactionHelper remote;
+    mRemote->initClient();
+    mRemote->createTransaction(&remote, mDisplayWidth / 2, mDisplayHeight / 2);
+    ASSERT_EQ(1, remote.getNumListeners());
+    auto remoteListenerCallbacks = remote.getListenerCallbacks();
+    auto remoteCallback = remoteListenerCallbacks.begin();
+    auto remoteCallbackInfo = remoteCallback->second;
+    auto remoteListenerScs = remoteCallbackInfo.surfaceControls;
+    ASSERT_EQ(1, remoteCallbackInfo.callbackIds.size());
+    ASSERT_EQ(1, remoteListenerScs.size());
+
+    sp<SurfaceControl> remoteSc = *(remoteListenerScs.begin());
+    transaction.merge(std::move(remote));
+    transaction.apply();
+
+    sleep(1);
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, sc);
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, remoteSc);
+    EXPECT_NO_FATAL_FAILURE(IPCTestUtils::waitForCallback(helper1, expected, true));
+
+    mRemote->verifyCallbacks();
+    mRemote->cleanUp();
+}
+
+} // namespace test
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
new file mode 100644
index 0000000..6d28e62
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -0,0 +1,879 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+#include "utils/CallbackUtils.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class LayerCallbackTest : public LayerTransactionTest {
+public:
+    virtual sp<SurfaceControl> createBufferStateLayer() {
+        return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
+    }
+
+    static int fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper,
+                               const sp<SurfaceControl>& layer = nullptr, bool setBuffer = true,
+                               bool setBackgroundColor = false) {
+        if (layer) {
+            sp<GraphicBuffer> buffer;
+            sp<Fence> fence;
+            if (setBuffer) {
+                int err = getBuffer(&buffer, &fence);
+                if (err != NO_ERROR) {
+                    return err;
+                }
+
+                transaction.setBuffer(layer, buffer);
+                transaction.setAcquireFence(layer, fence);
+            }
+
+            if (setBackgroundColor) {
+                transaction.setBackgroundColor(layer, /*color*/ half3(1.0f, 0, 0), /*alpha*/ 1.0f,
+                                               ui::Dataspace::UNKNOWN);
+            }
+        }
+
+        transaction.addTransactionCompletedCallback(callbackHelper->function,
+                                                    callbackHelper->getContext());
+        return NO_ERROR;
+    }
+
+    static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+                                bool finalState = false) {
+        CallbackData callbackData;
+        ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
+        EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData));
+
+        if (finalState) {
+            ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+        }
+    }
+
+    static void waitForCallbacks(CallbackHelper& helper,
+                                 const std::vector<ExpectedResult>& expectedResults,
+                                 bool finalState = false) {
+        for (const auto& expectedResult : expectedResults) {
+            waitForCallback(helper, expectedResult);
+        }
+        if (finalState) {
+            ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+        }
+    }
+};
+
+TEST_F(LayerCallbackTest, BufferColor) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer, true, true);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoBufferNoColor) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer, false, false);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, BufferNoColor) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer, true, false);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoBufferColor) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer, false, true);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoStateChange) {
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.apply();
+
+    ExpectedResult expected;
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, OffScreen) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.setFrame(layer, Rect(-100, -100, 100, 100)).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MergeBufferNoColor) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    int err = fillTransaction(transaction1, &callback1, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MergeNoBufferColor) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    int err = fillTransaction(transaction1, &callback1, layer1, false, true);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2, false, true);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+                         ExpectedResult::Buffer::NOT_ACQUIRED);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MergeOneBufferOneColor) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    int err = fillTransaction(transaction1, &callback1, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2, false, true);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer1);
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer2,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+TEST_F(LayerCallbackTest, Merge_SameCallback) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction1, &callback, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction2.merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_SameLayer) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    int err = fillTransaction(transaction1, &callback1, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction2.merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, Merge_DifferentClients) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    int err = fillTransaction(transaction1, &callback1, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    for (size_t i = 0; i < 10; i++) {
+        int err = fillTransaction(transaction, &callback, layer);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+
+        transaction.apply();
+
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::ACQUIRED,
+                            (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+                                     : ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    for (size_t i = 0; i < 10; i++) {
+        ExpectedResult expected;
+
+        if (i == 0) {
+            int err = fillTransaction(transaction, &callback, layer);
+            if (err) {
+                GTEST_SUCCEED() << "test not supported";
+                return;
+            }
+            expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+        } else {
+            int err = fillTransaction(transaction, &callback);
+            if (err) {
+                GTEST_SUCCEED() << "test not supported";
+                return;
+            }
+        }
+
+        transaction.apply();
+
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    for (size_t i = 0; i < 10; i++) {
+        if (i == 0) {
+            int err = fillTransaction(transaction, &callback, layer);
+            if (err) {
+                GTEST_SUCCEED() << "test not supported";
+                return;
+            }
+        } else {
+            int err = fillTransaction(transaction, &callback);
+            if (err) {
+                GTEST_SUCCEED() << "test not supported";
+                return;
+            }
+        }
+
+        transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+        ExpectedResult expected;
+        expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED
+                                     : ExpectedResult::Transaction::NOT_PRESENTED,
+                            layer,
+                            (i == 0) ? ExpectedResult::Buffer::ACQUIRED
+                                     : ExpectedResult::Buffer::NOT_ACQUIRED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, i == 0));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge) {
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
+    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    for (size_t i = 0; i < 10; i++) {
+        int err = fillTransaction(transaction1, &callback1, layer1);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+        err = fillTransaction(transaction2, &callback2, layer2);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+
+        transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+        transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+        ExpectedResult expected;
+        expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+                             ExpectedResult::Buffer::ACQUIRED,
+                             (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+                                      : ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
+    ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+    for (size_t i = 0; i < 10; i++) {
+        int err = fillTransaction(transaction1, &callback1, layer1);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+        err = fillTransaction(transaction2, &callback2, layer2);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+
+        transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+        transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+        ExpectedResult expected;
+        expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
+                             ExpectedResult::Buffer::ACQUIRED,
+                             (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
+                                      : ExpectedResult::PreviousBuffer::RELEASED);
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
+        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
+    }
+    ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
+    ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_NoStateChange) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+
+    // Normal call to set up test
+    int err = fillTransaction(transaction1, &callback1, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+    expected.reset();
+
+    // Test
+    err = fillTransaction(transaction1, &callback1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction2.merge(std::move(transaction1)).apply();
+
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateChange) {
+    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
+            client2(new SurfaceComposerClient);
+
+    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
+    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
+
+    sp<SurfaceControl> layer1, layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction transaction1, transaction2;
+    CallbackHelper callback1, callback2;
+
+    // Normal call to set up test
+    int err = fillTransaction(transaction1, &callback1, layer1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
+    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+    ExpectedResult expected;
+    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+    expected.reset();
+
+    // Test
+    err = fillTransaction(transaction1, &callback1);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
+
+    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2,
+                        ExpectedResult::Buffer::NOT_ACQUIRED);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    std::vector<ExpectedResult> expectedResults(50);
+    for (auto& expected : expectedResults) {
+        expected.reset();
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                            ExpectedResult::Buffer::ACQUIRED,
+                            ExpectedResult::PreviousBuffer::UNKNOWN);
+
+        int err = fillTransaction(transaction, &callback, layer);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+
+        transaction.apply();
+    }
+    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    // Normal call to set up test
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+
+    // Test
+    std::vector<ExpectedResult> expectedResults(50);
+    for (auto& expected : expectedResults) {
+        expected.reset();
+
+        err = fillTransaction(transaction, &callback);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+
+        transaction.apply();
+    }
+    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    // Normal call to set up test
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+    ExpectedResult expectedResult;
+    expectedResult.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expectedResult, true));
+
+    // Test
+    std::vector<ExpectedResult> expectedResults(50);
+    for (auto& expected : expectedResults) {
+        expected.reset();
+        expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
+                            ExpectedResult::Buffer::NOT_ACQUIRED);
+
+        err = fillTransaction(transaction, &callback);
+        if (err) {
+            GTEST_SUCCEED() << "test not supported";
+            return;
+        }
+
+        transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+    }
+    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected.addExpectedPresentTime(time);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback1;
+    int err = fillTransaction(transaction, &callback1, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected1;
+    expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected1.addExpectedPresentTime(time);
+
+    CallbackHelper callback2;
+    err = fillTransaction(transaction, &callback2, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 33ms after the first frame
+    time += (33.3 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected2;
+    expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                         ExpectedResult::Buffer::ACQUIRED,
+                         ExpectedResult::PreviousBuffer::RELEASED);
+    expected2.addExpectedPresentTime(time);
+
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback1;
+    int err = fillTransaction(transaction, &callback1, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the future
+    nsecs_t time = systemTime() + (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected1;
+    expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected1.addExpectedPresentTime(time);
+
+    CallbackHelper callback2;
+    err = fillTransaction(transaction, &callback2, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 33ms before the previous frame
+    time -= (33.3 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected2;
+    expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
+                         ExpectedResult::Buffer::ACQUIRED,
+                         ExpectedResult::PreviousBuffer::RELEASED);
+
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
+}
+
+TEST_F(LayerCallbackTest, DesiredPresentTime_Past) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    // Try to present 100ms in the past
+    nsecs_t time = systemTime() - (100 * 1e6);
+
+    transaction.setDesiredPresentTime(time);
+    transaction.apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    expected.addExpectedPresentTime(systemTime());
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
new file mode 100644
index 0000000..83e5060
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -0,0 +1,1864 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <gui/BufferItemConsumer.h>
+#include <ui/Transform.h>
+#include <thread>
+#include "TransactionTestHarnesses.h"
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class LayerRenderTypeTransactionTest : public LayerTransactionTest,
+                                       public ::testing::WithParamInterface<RenderPath> {
+public:
+    LayerRenderTypeTransactionTest() : mHarness(LayerRenderPathTestHarness(this, GetParam())) {}
+
+    std::unique_ptr<ScreenCapture> getScreenCapture() { return mHarness.getScreenCapture(); }
+    void setRelativeZBasicHelper(uint32_t layerType);
+    void setRelativeZGroupHelper(uint32_t layerType);
+    void setAlphaBasicHelper(uint32_t layerType);
+    void setBackgroundColorHelper(uint32_t layerType, bool priorColor, bool bufferFill, float alpha,
+                                  Color finalColor);
+
+protected:
+    LayerRenderPathTestHarness mHarness;
+};
+
+INSTANTIATE_TEST_CASE_P(LayerRenderTypeTransactionTests, LayerRenderTypeTransactionTest,
+                        ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT));
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionBasic_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("default position");
+        const Rect rect(0, 0, 32, 32);
+        auto shot = getScreenCapture();
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
+    }
+
+    Transaction().setPosition(layer, 5, 10).apply();
+    {
+        SCOPED_TRACE("new position");
+        const Rect rect(5, 10, 37, 42);
+        auto shot = getScreenCapture();
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionRounding_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    // GPU composition requires only 4 bits of subpixel precision during rasterization
+    // XXX GPU composition does not match HWC composition due to precision
+    // loss (b/69315223)
+    const float epsilon = 1.0f / 16.0f;
+    Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply();
+    {
+        SCOPED_TRACE("rounding down");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+
+    Transaction().setPosition(layer, 0.5f + epsilon, 0.5f + epsilon).apply();
+    {
+        SCOPED_TRACE("rounding up");
+        getScreenCapture()->expectColor(Rect(1, 1, 33, 33), Color::RED);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionOutOfBounds_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction().setPosition(layer, -32, -32).apply();
+    {
+        SCOPED_TRACE("negative coordinates");
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
+    }
+
+    Transaction().setPosition(layer, mDisplayWidth, mDisplayHeight).apply();
+    {
+        SCOPED_TRACE("positive coordinates");
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    // partially out of bounds
+    Transaction().setPosition(layer, -30, -30).apply();
+    {
+        SCOPED_TRACE("negative coordinates");
+        getScreenCapture()->expectColor(Rect(0, 0, 2, 2), Color::RED);
+    }
+
+    Transaction().setPosition(layer, mDisplayWidth - 2, mDisplayHeight - 2).apply();
+    {
+        SCOPED_TRACE("positive coordinates");
+        getScreenCapture()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
+                                             mDisplayHeight),
+                                        Color::RED);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithResize_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    // setPosition is applied immediately by default, with or without resize
+    // pending
+    Transaction().setPosition(layer, 5, 10).setSize(layer, 64, 64).apply();
+    {
+        SCOPED_TRACE("resize pending");
+        auto shot = getScreenCapture();
+        const Rect rect(5, 10, 37, 42);
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
+    {
+        SCOPED_TRACE("resize applied");
+        getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction().setSize(layer, 64, 64).apply();
+    {
+        SCOPED_TRACE("resize pending");
+        auto shot = getScreenCapture();
+        const Rect rect(0, 0, 32, 32);
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
+    {
+        SCOPED_TRACE("resize applied");
+        auto shot = getScreenCapture();
+        const Rect rect(0, 0, 64, 64);
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    // setSize is immediate with SCALE_TO_WINDOW, unlike setPosition
+    Transaction()
+            .setSize(layer, 64, 64)
+            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
+            .apply();
+    getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, CreateLayer_BufferState) {
+    uint32_t transformHint = ui::Transform::ROT_INVALID;
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32,
+                                                ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                /*parent*/ nullptr, &transformHint));
+    ASSERT_NE(ui::Transform::ROT_INVALID, transformHint);
+}
+
+void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
+    sp<SurfaceControl> layerR;
+    sp<SurfaceControl> layerG;
+    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32));
+
+    switch (layerType) {
+        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+            Transaction()
+                    .setPosition(layerG, 16, 16)
+                    .setRelativeLayer(layerG, layerR->getHandle(), 1)
+                    .apply();
+            break;
+        case ISurfaceComposerClient::eFXSurfaceBufferState:
+            Transaction()
+                    .setFrame(layerR, Rect(0, 0, 32, 32))
+                    .setFrame(layerG, Rect(16, 16, 48, 48))
+                    .setRelativeLayer(layerG, layerR->getHandle(), 1)
+                    .apply();
+            break;
+        default:
+            ASSERT_FALSE(true) << "Unsupported layer type";
+    }
+    {
+        SCOPED_TRACE("layerG above");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
+        shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN);
+    }
+
+    Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply();
+    {
+        SCOPED_TRACE("layerG below");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectColor(Rect(32, 32, 48, 48), Color::GREEN);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferQueue) {
+    ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferState) {
+    ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
+}
+
+void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) {
+    sp<SurfaceControl> layerR;
+    sp<SurfaceControl> layerG;
+    sp<SurfaceControl> layerB;
+    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerB, Color::BLUE, 32, 32));
+
+    // layerR = 0, layerG = layerR + 3, layerB = 2
+    switch (layerType) {
+        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+            Transaction()
+                    .setPosition(layerG, 8, 8)
+                    .setRelativeLayer(layerG, layerR->getHandle(), 3)
+                    .setPosition(layerB, 16, 16)
+                    .setLayer(layerB, mLayerZBase + 2)
+                    .apply();
+            break;
+        case ISurfaceComposerClient::eFXSurfaceBufferState:
+            Transaction()
+                    .setFrame(layerR, Rect(0, 0, 32, 32))
+                    .setFrame(layerG, Rect(8, 8, 40, 40))
+                    .setRelativeLayer(layerG, layerR->getHandle(), 3)
+                    .setFrame(layerB, Rect(16, 16, 48, 48))
+                    .setLayer(layerB, mLayerZBase + 2)
+                    .apply();
+            break;
+        default:
+            ASSERT_FALSE(true) << "Unsupported layer type";
+    }
+
+    {
+        SCOPED_TRACE("(layerR < layerG) < layerB");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
+        shot->expectColor(Rect(8, 8, 16, 16), Color::GREEN);
+        shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
+    }
+
+    // layerR = 4, layerG = layerR + 3, layerB = 2
+    Transaction().setLayer(layerR, mLayerZBase + 4).apply();
+    {
+        SCOPED_TRACE("layerB < (layerR < layerG)");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
+        shot->expectColor(Rect(8, 8, 40, 40), Color::GREEN);
+        shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
+    }
+
+    // layerR = 4, layerG = layerR - 3, layerB = 2
+    Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply();
+    {
+        SCOPED_TRACE("layerB < (layerG < layerR)");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectColor(Rect(32, 32, 40, 40), Color::GREEN);
+        shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
+    }
+
+    // restore to absolute z
+    // layerR = 4, layerG = 0, layerB = 2
+    Transaction().setLayer(layerG, mLayerZBase).apply();
+    {
+        SCOPED_TRACE("layerG < layerB < layerR");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectColor(Rect(32, 32, 48, 48), Color::BLUE);
+    }
+
+    // layerR should not affect layerG anymore
+    // layerR = 1, layerG = 0, layerB = 2
+    Transaction().setLayer(layerR, mLayerZBase + 1).apply();
+    {
+        SCOPED_TRACE("layerG < layerR < layerB");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
+        shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferQueue) {
+    ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferState) {
+    ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferQueue) {
+    const Rect top(0, 0, 32, 16);
+    const Rect bottom(0, 16, 32, 32);
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+
+    ANativeWindow_Buffer buffer;
+    ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillANativeWindowBufferColor(buffer, top, Color::TRANSPARENT));
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillANativeWindowBufferColor(buffer, bottom, Color::RED));
+    // setTransparentRegionHint always applies to the following buffer
+    Transaction().setTransparentRegionHint(layer, Region(top)).apply();
+    ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
+    {
+        SCOPED_TRACE("top transparent");
+        auto shot = getScreenCapture();
+        shot->expectColor(top, Color::BLACK);
+        shot->expectColor(bottom, Color::RED);
+    }
+
+    Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
+    {
+        SCOPED_TRACE("transparent region hint pending");
+        auto shot = getScreenCapture();
+        shot->expectColor(top, Color::BLACK);
+        shot->expectColor(bottom, Color::RED);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillANativeWindowBufferColor(buffer, top, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillANativeWindowBufferColor(buffer, bottom, Color::TRANSPARENT));
+    ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
+    {
+        SCOPED_TRACE("bottom transparent");
+        auto shot = getScreenCapture();
+        shot->expectColor(top, Color::RED);
+        shot->expectColor(bottom, Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferState) {
+    const Rect top(0, 0, 32, 16);
+    const Rect bottom(0, 16, 32, 32);
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillGraphicBufferColor(buffer, top, Color::TRANSPARENT));
+    ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, bottom, Color::RED));
+    Transaction()
+            .setTransparentRegionHint(layer, Region(top))
+            .setBuffer(layer, buffer)
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .apply();
+    {
+        SCOPED_TRACE("top transparent");
+        auto shot = getScreenCapture();
+        shot->expectColor(top, Color::BLACK);
+        shot->expectColor(bottom, Color::RED);
+    }
+
+    Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
+    {
+        SCOPED_TRACE("transparent region hint intermediate");
+        auto shot = getScreenCapture();
+        shot->expectColor(top, Color::BLACK);
+        shot->expectColor(bottom, Color::BLACK);
+    }
+
+    buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                               BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                       BufferUsage::COMPOSER_OVERLAY,
+                               "test");
+
+    ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, top, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillGraphicBufferColor(buffer, bottom, Color::TRANSPARENT));
+    Transaction().setBuffer(layer, buffer).apply();
+    {
+        SCOPED_TRACE("bottom transparent");
+        auto shot = getScreenCapture();
+        shot->expectColor(top, Color::RED);
+        shot->expectColor(bottom, Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferQueue) {
+    sp<SurfaceControl> layerTransparent;
+    sp<SurfaceControl> layerR;
+    ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
+
+    // check that transparent region hint is bound by the layer size
+    Transaction()
+            .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
+            .setPosition(layerR, 16, 16)
+            .setLayer(layerR, mLayerZBase + 1)
+            .apply();
+    ASSERT_NO_FATAL_FAILURE(
+            fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerR, Color::RED, 32, 32));
+    getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferState) {
+    sp<SurfaceControl> layerTransparent;
+    sp<SurfaceControl> layerR;
+    ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(
+            layerR = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    // check that transparent region hint is bound by the layer size
+    Transaction()
+            .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
+            .setFrame(layerR, Rect(16, 16, 48, 48))
+            .setLayer(layerR, mLayerZBase + 1)
+            .apply();
+    ASSERT_NO_FATAL_FAILURE(
+            fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32));
+    getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
+}
+
+void LayerRenderTypeTransactionTest::setAlphaBasicHelper(uint32_t layerType) {
+    sp<SurfaceControl> layer1;
+    sp<SurfaceControl> layer2;
+    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer("test 1", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer("test 2", 32, 32, layerType));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer1, {64, 0, 0, 255}, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer2, {0, 64, 0, 255}, 32, 32));
+
+    switch (layerType) {
+        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+            Transaction()
+                    .setAlpha(layer1, 0.25f)
+                    .setAlpha(layer2, 0.75f)
+                    .setPosition(layer2, 16, 0)
+                    .setLayer(layer2, mLayerZBase + 1)
+                    .apply();
+            break;
+        case ISurfaceComposerClient::eFXSurfaceBufferState:
+            Transaction()
+                    .setAlpha(layer1, 0.25f)
+                    .setAlpha(layer2, 0.75f)
+                    .setFrame(layer1, Rect(0, 0, 32, 32))
+                    .setFrame(layer2, Rect(16, 0, 48, 32))
+                    .setLayer(layer2, mLayerZBase + 1)
+                    .apply();
+            break;
+        default:
+            ASSERT_FALSE(true) << "Unsupported layer type";
+    }
+    {
+        auto shot = getScreenCapture();
+        uint8_t r = 16; // 64 * 0.25f
+        uint8_t g = 48; // 64 * 0.75f
+        shot->expectColor(Rect(0, 0, 16, 32), {r, 0, 0, 255});
+        shot->expectColor(Rect(32, 0, 48, 32), {0, g, 0, 255});
+
+        r /= 4; // r * (1.0f - 0.75f)
+        shot->expectColor(Rect(16, 0, 32, 32), {r, g, 0, 255});
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferQueue) {
+    ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferState) {
+    ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorBasic) {
+    sp<SurfaceControl> bufferLayer;
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceEffect));
+
+    Transaction()
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setLayer(colorLayer, mLayerZBase + 1)
+            .apply();
+
+    {
+        SCOPED_TRACE("default color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
+    const Color expected = {15, 51, 85, 255};
+    // this is handwavy, but the precison loss scaled by 255 (8-bit per
+    // channel) should be less than one
+    const uint8_t tolerance = 1;
+    Transaction().setColor(colorLayer, color).apply();
+    {
+        SCOPED_TRACE("new color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
+    }
+}
+
+// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill
+// BLUE: prior background color
+// GREEN: final background color
+// BLACK: no color or fill
+void LayerRenderTypeTransactionTest::setBackgroundColorHelper(uint32_t layerType, bool priorColor,
+                                                              bool bufferFill, float alpha,
+                                                              Color finalColor) {
+    sp<SurfaceControl> layer;
+    int32_t width = 500;
+    int32_t height = 500;
+
+    Color fillColor = Color::RED;
+    Color priorBgColor = Color::BLUE;
+    Color expectedColor = Color::BLACK;
+    switch (layerType) {
+        case ISurfaceComposerClient::eFXSurfaceEffect:
+            ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType));
+            Transaction()
+                    .setCrop_legacy(layer, Rect(0, 0, width, height))
+                    .setColor(layer, half3(1.0f, 0, 0))
+                    .apply();
+            expectedColor = fillColor;
+            break;
+        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+            ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
+            if (bufferFill) {
+                ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height));
+                expectedColor = fillColor;
+            }
+            Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply();
+            break;
+        case ISurfaceComposerClient::eFXSurfaceBufferState:
+            ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
+            if (bufferFill) {
+                ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
+                expectedColor = fillColor;
+            }
+            Transaction().setFrame(layer, Rect(0, 0, width, height)).apply();
+            break;
+        default:
+            GTEST_FAIL() << "Unknown layer type in setBackgroundColorHelper";
+            return;
+    }
+
+    if (priorColor && layerType != ISurfaceComposerClient::eFXSurfaceEffect) {
+        Transaction()
+                .setBackgroundColor(layer, half3(0, 0, 1.0f), 1.0f, ui::Dataspace::UNKNOWN)
+                .apply();
+        if (!bufferFill) {
+            expectedColor = priorBgColor;
+        }
+    }
+
+    {
+        SCOPED_TRACE("default before setting background color layer");
+        screenshot()->expectColor(Rect(0, 0, width, height), expectedColor);
+    }
+    Transaction()
+            .setBackgroundColor(layer, half3(0, 1.0f, 0), alpha, ui::Dataspace::UNKNOWN)
+            .apply();
+
+    {
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, width, height), finalColor);
+        shot->expectBorder(Rect(0, 0, width, height), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_Color_NoEffect) {
+    bool priorColor = false;
+    bool bufferFill = false;
+    float alpha = 1.0f;
+    Color finalColor = Color::RED;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceEffect,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferQueue_BufferFill_NoPriorColor_Basic) {
+    bool priorColor = false;
+    bool bufferFill = true;
+    float alpha = 1.0f;
+    Color finalColor = Color::RED;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferQueue_NoBufferFill_NoPriorColor_Basic) {
+    bool priorColor = false;
+    bool bufferFill = false;
+    float alpha = 1.0f;
+    Color finalColor = Color::GREEN;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferQueue_BufferFill_PriorColor_Basic) {
+    bool priorColor = true;
+    bool bufferFill = true;
+    float alpha = 1.0f;
+    Color finalColor = Color::RED;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferQueue_NoBufferFill_PriorColor_Basic) {
+    bool priorColor = true;
+    bool bufferFill = false;
+    float alpha = 1.0f;
+    Color finalColor = Color::GREEN;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferQueue_NoPriorColor_ZeroAlpha_NoEffect) {
+    bool priorColor = false;
+    bool bufferFill = false;
+    float alpha = 0;
+    Color finalColor = Color::BLACK;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferQueue_PriorColor_ZeroAlpha_DeleteBackground) {
+    bool priorColor = true;
+    bool bufferFill = false;
+    float alpha = 0;
+    Color finalColor = Color::BLACK;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferState_BufferFill_NoPriorColor_Basic) {
+    bool priorColor = false;
+    bool bufferFill = true;
+    float alpha = 1.0f;
+    Color finalColor = Color::RED;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferState_NoBufferFill_NoPriorColor_Basic) {
+    bool priorColor = false;
+    bool bufferFill = false;
+    float alpha = 1.0f;
+    Color finalColor = Color::GREEN;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferState_NoBufferFill_PriorColor_Basic) {
+    bool priorColor = true;
+    bool bufferFill = false;
+    float alpha = 1.0f;
+    Color finalColor = Color::GREEN;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferState_NoPriorColor_ZeroAlpha_NoEffect) {
+    bool priorColor = false;
+    bool bufferFill = false;
+    float alpha = 0;
+    Color finalColor = Color::BLACK;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferState_PriorColor_ZeroAlpha_DeleteBackground) {
+    bool priorColor = true;
+    bool bufferFill = false;
+    float alpha = 0;
+    Color finalColor = Color::BLACK;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorClamped) {
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceEffect));
+    Transaction()
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setColor(colorLayer, half3(2.0f, 0.0f, 0.0f))
+            .apply();
+
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+// An invalid color will not render a color and the layer will not be visible.
+TEST_P(LayerRenderTypeTransactionTest, SetInvalidColor) {
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceEffect));
+    Transaction()
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setColor(colorLayer, half3(1.0f, -1.0f, 0.5f))
+            .apply();
+
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorWithAlpha) {
+    sp<SurfaceControl> bufferLayer;
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceEffect));
+    Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
+
+    const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
+    const float alpha = 0.25f;
+    const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
+    // this is handwavy, but the precison loss scaled by 255 (8-bit per
+    // channel) should be less than one
+    const uint8_t tolerance = 1;
+    Transaction()
+            .setColor(colorLayer, color)
+            .setAlpha(colorLayer, alpha)
+            .setLayer(colorLayer, mLayerZBase + 1)
+            .apply();
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+                                    tolerance);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorWithParentAlpha_Bug74220420) {
+    sp<SurfaceControl> bufferLayer;
+    sp<SurfaceControl> parentLayer;
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parentWithAlpha", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */,
+                                                     0 /* buffer height */,
+                                                     ISurfaceComposerClient::eFXSurfaceEffect));
+    Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
+    const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
+    const float alpha = 0.25f;
+    const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
+    // this is handwavy, but the precision loss scaled by 255 (8-bit per
+    // channel) should be less than one
+    const uint8_t tolerance = 1;
+    Transaction()
+            .reparent(colorLayer, parentLayer->getHandle())
+            .setColor(colorLayer, color)
+            .setAlpha(parentLayer, alpha)
+            .setLayer(parentLayer, mLayerZBase + 1)
+            .apply();
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+                                    tolerance);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
+
+    Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 0, 0).apply();
+    {
+        SCOPED_TRACE("IDENTITY");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
+    }
+
+    Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 32, 0).apply();
+    {
+        SCOPED_TRACE("FLIP_H");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED,
+                                           Color::WHITE, Color::BLUE);
+    }
+
+    Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).setPosition(layer, 0, 32).apply();
+    {
+        SCOPED_TRACE("FLIP_V");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE,
+                                           Color::RED, Color::GREEN);
+    }
+
+    Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).setPosition(layer, 32, 0).apply();
+    {
+        SCOPED_TRACE("ROT_90");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED,
+                                           Color::WHITE, Color::GREEN);
+    }
+
+    Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setPosition(layer, 0, 0).apply();
+    {
+        SCOPED_TRACE("SCALE");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE, true /* filtered */);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
+
+    Transaction()
+            .setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f)
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .apply();
+    {
+        SCOPED_TRACE("IDENTITY");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
+    }
+
+    Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).apply();
+    {
+        SCOPED_TRACE("FLIP_H");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
+    }
+
+    Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).apply();
+    {
+        SCOPED_TRACE("FLIP_V");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
+    }
+
+    Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).apply();
+    {
+        SCOPED_TRACE("ROT_90");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
+    }
+
+    Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).apply();
+    {
+        SCOPED_TRACE("SCALE");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixRot45_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
+
+    const float rot = M_SQRT1_2; // 45 degrees
+    const float trans = M_SQRT2 * 16.0f;
+    Transaction().setMatrix(layer, rot, rot, -rot, rot).setPosition(layer, trans, 0).apply();
+
+    auto shot = getScreenCapture();
+    // check a 8x8 region inside each color
+    auto get8x8Rect = [](int32_t centerX, int32_t centerY) {
+        const int32_t halfL = 4;
+        return Rect(centerX - halfL, centerY - halfL, centerX + halfL, centerY + halfL);
+    };
+    const int32_t unit = int32_t(trans / 2);
+    shot->expectColor(get8x8Rect(2 * unit, 1 * unit), Color::RED);
+    shot->expectColor(get8x8Rect(3 * unit, 2 * unit), Color::GREEN);
+    shot->expectColor(get8x8Rect(1 * unit, 2 * unit), Color::BLUE);
+    shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    // setMatrix is applied after any pending resize, unlike setPosition
+    Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setSize(layer, 64, 64).apply();
+    {
+        SCOPED_TRACE("resize pending");
+        auto shot = getScreenCapture();
+        const Rect rect(0, 0, 32, 32);
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
+    {
+        SCOPED_TRACE("resize applied");
+        const Rect rect(0, 0, 128, 128);
+        getScreenCapture()->expectColor(rect, Color::RED);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition
+    Transaction()
+            .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
+            .setSize(layer, 64, 64)
+            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
+            .apply();
+    getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
+
+    // XXX SCALE_CROP is not respected; calling setSize and
+    // setOverrideScalingMode in separate transactions does not work
+    // (b/69315456)
+    Transaction()
+            .setSize(layer, 64, 16)
+            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
+            .apply();
+    {
+        SCOPED_TRACE("SCALE_TO_WINDOW");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE, true /* filtered */);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+    const Rect crop(8, 8, 24, 24);
+
+    Transaction().setCrop_legacy(layer, crop).apply();
+    auto shot = getScreenCapture();
+    shot->expectColor(crop, Color::RED);
+    shot->expectBorder(crop, Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    const Rect crop(8, 8, 24, 24);
+
+    Transaction().setCrop(layer, crop).apply();
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("empty rect");
+        Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply();
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+
+    {
+        SCOPED_TRACE("negative rect");
+        Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply();
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("empty rect");
+        Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
+        getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+    }
+
+    {
+        SCOPED_TRACE("negative rect");
+        Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
+        getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply();
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE);
+    TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED);
+
+    Transaction().setFrame(layer, Rect(0, 0, 64, 64)).apply();
+
+    Transaction().setBuffer(layer, buffer).apply();
+
+    // Partially out of bounds in the negative (upper left) direction
+    Transaction().setCrop(layer, Rect(-128, -128, 32, 16)).apply();
+    {
+        SCOPED_TRACE("out of bounds, negative (upper left) direction");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 64, 64), Color::BLUE);
+        shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+    }
+
+    // Partially out of bounds in the positive (lower right) direction
+    Transaction().setCrop(layer, Rect(0, 16, 128, 128)).apply();
+    {
+        SCOPED_TRACE("out of bounds, positive (lower right) direction");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
+        shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+    }
+
+    // Fully out of buffer space bounds
+    Transaction().setCrop(layer, Rect(-128, -128, -1, -1)).apply();
+    {
+        SCOPED_TRACE("Fully out of bounds");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 64, 16), Color::BLUE);
+        shot->expectColor(Rect(0, 16, 64, 64), Color::RED);
+        shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    const Point position(32, 32);
+    const Rect crop(8, 8, 24, 24);
+    Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply();
+    auto shot = getScreenCapture();
+    shot->expectColor(crop + position, Color::RED);
+    shot->expectBorder(crop + position, Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+    const Rect frame(32, 32, 64, 64);
+    const Rect crop(8, 8, 24, 24);
+    Transaction().setFrame(layer, frame).setCrop(layer, crop).apply();
+    auto shot = getScreenCapture();
+    shot->expectColor(frame, Color::RED);
+    shot->expectBorder(frame, Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithScale_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    // crop_legacy is affected by matrix
+    Transaction()
+            .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
+            .setCrop_legacy(layer, Rect(8, 8, 24, 24))
+            .apply();
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
+    shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithResize_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    // setCrop_legacy is applied immediately by default, with or without resize pending
+    Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
+    {
+        SCOPED_TRACE("resize pending");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
+        shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
+    {
+        SCOPED_TRACE("resize applied");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(8, 8, 16, 16), Color::RED);
+        shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    const Rect frame(8, 8, 24, 24);
+
+    Transaction().setFrame(layer, frame).apply();
+    auto shot = getScreenCapture();
+    shot->expectColor(frame, Color::RED);
+    shot->expectBorder(frame, Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameEmpty_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("empty rect");
+        Transaction().setFrame(layer, Rect(8, 8, 8, 8)).apply();
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    {
+        SCOPED_TRACE("negative rect");
+        Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply();
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultParentless_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10));
+
+    // A parentless layer will default to a frame with the same size as the buffer
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) {
+    sp<SurfaceControl> parent, child;
+    ASSERT_NO_FATAL_FAILURE(
+            parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+    Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
+
+    ASSERT_NO_FATAL_FAILURE(
+            child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+
+    Transaction().reparent(child, parent->getHandle()).apply();
+
+    // A layer will default to the frame of its parent
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBQParent_BufferState) {
+    sp<SurfaceControl> parent, child;
+    ASSERT_NO_FATAL_FAILURE(parent = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parent, Color::RED, 32, 32));
+
+    ASSERT_NO_FATAL_FAILURE(
+            child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+
+    Transaction().reparent(child, parent->getHandle()).apply();
+
+    // A layer will default to the frame of its parent
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameUpdate_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+    Transaction().setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+    std::this_thread::sleep_for(500ms);
+
+    Transaction().setFrame(layer, Rect(16, 16, 48, 48)).apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
+    shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameOutsideBounds_BufferState) {
+    sp<SurfaceControl> parent, child;
+    ASSERT_NO_FATAL_FAILURE(
+            parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    ASSERT_NO_FATAL_FAILURE(
+            child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+    Transaction().reparent(child, parent->getHandle()).apply();
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
+    Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
+    Transaction().setFrame(child, Rect(0, 16, 32, 32)).apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, 32, 16), Color::RED);
+    shot->expectColor(Rect(0, 16, 32, 32), Color::BLUE);
+    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("set buffer 1");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
+
+    {
+        SCOPED_TRACE("set buffer 2");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLUE);
+        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("set buffer 3");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) {
+    sp<SurfaceControl> layer1;
+    ASSERT_NO_FATAL_FAILURE(
+            layer1 = createLayer("test", 64, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<SurfaceControl> layer2;
+    ASSERT_NO_FATAL_FAILURE(
+            layer2 = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64));
+
+    Transaction().setFrame(layer1, Rect(0, 0, 64, 64)).apply();
+    {
+        SCOPED_TRACE("set layer 1 buffer red");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32));
+
+    Transaction().setFrame(layer2, Rect(0, 0, 32, 32)).apply();
+    {
+        SCOPED_TRACE("set layer 2 buffer blue");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+        shot->expectColor(Rect(0, 32, 64, 64), Color::RED);
+        shot->expectColor(Rect(0, 32, 32, 64), Color::RED);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64));
+    {
+        SCOPED_TRACE("set layer 1 buffer green");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+        shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
+        shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::WHITE, 32, 32));
+
+    {
+        SCOPED_TRACE("set layer 2 buffer white");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::WHITE);
+        shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
+        shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+    std::array<sp<GraphicBuffer>, 10> buffers;
+
+    size_t idx = 0;
+    for (auto& buffer : buffers) {
+        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                           BufferUsage::COMPOSER_OVERLAY,
+                                   "test");
+        Color color = colors[idx % colors.size()];
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+        idx++;
+    }
+
+    // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+    // cache is working.
+    idx = 0;
+    for (auto& buffer : buffers) {
+        for (int i = 0; i < 2; i++) {
+            Transaction().setBuffer(layer, buffer).apply();
+
+            Color color = colors[idx % colors.size()];
+            auto shot = screenshot();
+            shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
+            shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+        }
+        idx++;
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_LeastRecentlyUsed_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+    std::array<sp<GraphicBuffer>, 70> buffers;
+
+    size_t idx = 0;
+    for (auto& buffer : buffers) {
+        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                           BufferUsage::COMPOSER_OVERLAY,
+                                   "test");
+        Color color = colors[idx % colors.size()];
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+        idx++;
+    }
+
+    // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+    // cache is working.
+    idx = 0;
+    for (auto& buffer : buffers) {
+        for (int i = 0; i < 2; i++) {
+            Transaction().setBuffer(layer, buffer).apply();
+
+            Color color = colors[idx % colors.size()];
+            auto shot = screenshot();
+            shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
+            shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+        }
+        idx++;
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_DestroyedBuffer_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+    std::array<sp<GraphicBuffer>, 65> buffers;
+
+    size_t idx = 0;
+    for (auto& buffer : buffers) {
+        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                           BufferUsage::COMPOSER_OVERLAY,
+                                   "test");
+        Color color = colors[idx % colors.size()];
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+        idx++;
+    }
+
+    // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+    // cache is working.
+    idx = 0;
+    for (auto& buffer : buffers) {
+        for (int i = 0; i < 2; i++) {
+            Transaction().setBuffer(layer, buffer).apply();
+
+            Color color = colors[idx % colors.size()];
+            auto shot = screenshot();
+            shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
+            shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+        }
+        if (idx == 0) {
+            buffers[0].clear();
+        }
+        idx++;
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransformRotate90_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
+
+    Transaction()
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90)
+            .apply();
+
+    getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
+                                       Color::GREEN, true /* filtered */);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipH_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
+
+    Transaction()
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H)
+            .apply();
+
+    getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
+                                       Color::BLUE, true /* filtered */);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipV_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
+                                                         Color::BLUE, Color::WHITE));
+
+    Transaction()
+            .setFrame(layer, Rect(0, 0, 32, 32))
+            .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V)
+            .apply();
+
+    getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
+                                       Color::GREEN, true /* filtered */);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    Transaction transaction;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    sp<Fence> fence;
+    if (getBuffer(nullptr, &fence) != NO_ERROR) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
+
+    status_t status = fence->wait(1000);
+    ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status);
+    std::this_thread::sleep_for(200ms);
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    sp<Fence> fence = Fence::NO_FENCE;
+
+    Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    Transaction().setBuffer(layer, buffer).setDataspace(layer, ui::Dataspace::UNKNOWN).apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    HdrMetadata hdrMetadata;
+    hdrMetadata.validTypes = 0;
+    Transaction().setBuffer(layer, buffer).setHdrMetadata(layer, hdrMetadata).apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    Region region;
+    region.set(32, 32);
+    Transaction().setBuffer(layer, buffer).setSurfaceDamageRegion(layer, region).apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
+
+    Transaction().setBuffer(layer, buffer).setApi(layer, NATIVE_WINDOW_API_CPU).apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceEffect));
+    Transaction()
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setLayer(colorLayer, mLayerZBase + 1)
+            .apply();
+    {
+        SCOPED_TRACE("default color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
+    half3 expected = color;
+    mat3 matrix;
+    matrix[0][0] = 0.3;
+    matrix[1][0] = 0.59;
+    matrix[2][0] = 0.11;
+    matrix[0][1] = 0.3;
+    matrix[1][1] = 0.59;
+    matrix[2][1] = 0.11;
+    matrix[0][2] = 0.3;
+    matrix[1][2] = 0.59;
+    matrix[2][2] = 0.11;
+
+    // degamma before applying the matrix
+    if (mColorManagementUsed) {
+        ColorTransformHelper::DegammaColor(expected);
+    }
+
+    ColorTransformHelper::applyMatrix(expected, matrix);
+
+    if (mColorManagementUsed) {
+        ColorTransformHelper::GammaColor(expected);
+    }
+
+    const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
+                                 uint8_t(expected.b * 255), 255};
+
+    // this is handwavy, but the precison loss scaled by 255 (8-bit per
+    // channel) should be less than one
+    const uint8_t tolerance = 1;
+
+    Transaction().setColor(colorLayer, color).setColorTransform(colorLayer, matrix, vec3()).apply();
+    {
+        SCOPED_TRACE("new color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnParent) {
+    sp<SurfaceControl> parentLayer;
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
+                                                      0 /* buffer height */,
+                                                      ISurfaceComposerClient::eFXSurfaceContainer));
+    ASSERT_NO_FATAL_FAILURE(
+            colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                     ISurfaceComposerClient::eFXSurfaceEffect, parentLayer.get()));
+
+    Transaction()
+            .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setLayer(parentLayer, mLayerZBase + 1)
+            .apply();
+    {
+        SCOPED_TRACE("default color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
+    half3 expected = color;
+    mat3 matrix;
+    matrix[0][0] = 0.3;
+    matrix[1][0] = 0.59;
+    matrix[2][0] = 0.11;
+    matrix[0][1] = 0.3;
+    matrix[1][1] = 0.59;
+    matrix[2][1] = 0.11;
+    matrix[0][2] = 0.3;
+    matrix[1][2] = 0.59;
+    matrix[2][2] = 0.11;
+
+    // degamma before applying the matrix
+    if (mColorManagementUsed) {
+        ColorTransformHelper::DegammaColor(expected);
+    }
+
+    ColorTransformHelper::applyMatrix(expected, matrix);
+
+    if (mColorManagementUsed) {
+        ColorTransformHelper::GammaColor(expected);
+    }
+
+    const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
+                                 uint8_t(expected.b * 255), 255};
+
+    // this is handwavy, but the precison loss scaled by 255 (8-bit per
+    // channel) should be less than one
+    const uint8_t tolerance = 1;
+
+    Transaction()
+            .setColor(colorLayer, color)
+            .setColorTransform(parentLayer, matrix, vec3())
+            .apply();
+    {
+        SCOPED_TRACE("new color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnChildAndParent) {
+    sp<SurfaceControl> parentLayer;
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
+                                                      0 /* buffer height */,
+                                                      ISurfaceComposerClient::eFXSurfaceContainer));
+    ASSERT_NO_FATAL_FAILURE(
+            colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                     ISurfaceComposerClient::eFXSurfaceEffect, parentLayer.get()));
+
+    Transaction()
+            .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setLayer(parentLayer, mLayerZBase + 1)
+            .apply();
+    {
+        SCOPED_TRACE("default color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
+    half3 expected = color;
+    mat3 matrixChild;
+    matrixChild[0][0] = 0.3;
+    matrixChild[1][0] = 0.59;
+    matrixChild[2][0] = 0.11;
+    matrixChild[0][1] = 0.3;
+    matrixChild[1][1] = 0.59;
+    matrixChild[2][1] = 0.11;
+    matrixChild[0][2] = 0.3;
+    matrixChild[1][2] = 0.59;
+    matrixChild[2][2] = 0.11;
+    mat3 matrixParent;
+    matrixParent[0][0] = 0.2;
+    matrixParent[1][0] = 0.4;
+    matrixParent[2][0] = 0.10;
+    matrixParent[0][1] = 0.2;
+    matrixParent[1][1] = 0.4;
+    matrixParent[2][1] = 0.10;
+    matrixParent[0][2] = 0.2;
+    matrixParent[1][2] = 0.4;
+    matrixParent[2][2] = 0.10;
+
+    // degamma before applying the matrix
+    if (mColorManagementUsed) {
+        ColorTransformHelper::DegammaColor(expected);
+    }
+
+    ColorTransformHelper::applyMatrix(expected, matrixChild);
+    ColorTransformHelper::applyMatrix(expected, matrixParent);
+
+    if (mColorManagementUsed) {
+        ColorTransformHelper::GammaColor(expected);
+    }
+
+    const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
+                                 uint8_t(expected.b * 255), 255};
+
+    // this is handwavy, but the precison loss scaled by 255 (8-bit per
+    // channel) should be less than one
+    const uint8_t tolerance = 1;
+
+    Transaction()
+            .setColor(colorLayer, color)
+            .setColorTransform(parentLayer, matrixParent, vec3())
+            .setColorTransform(colorLayer, matrixChild, vec3())
+            .apply();
+    {
+        SCOPED_TRACE("new color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+    }
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
new file mode 100644
index 0000000..f3e11d8
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <ui/DisplayConfig.h>
+
+#include "BufferGenerator.h"
+#include "utils/ScreenshotUtils.h"
+#include "utils/TransactionUtils.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class LayerTransactionTest : public ::testing::Test {
+protected:
+    void SetUp() override {
+        mClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient";
+
+        ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
+
+        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+        ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
+    }
+
+    virtual void TearDown() {
+        mBlackBgSurface = 0;
+        mClient->dispose();
+        mClient = 0;
+    }
+
+    virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
+                                           const char* name, uint32_t width, uint32_t height,
+                                           uint32_t flags = 0, SurfaceControl* parent = nullptr,
+                                           uint32_t* outTransformHint = nullptr,
+                                           PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
+        auto layer =
+                createSurface(client, name, width, height, format, flags, parent, outTransformHint);
+
+        Transaction t;
+        t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
+
+        status_t error = t.apply();
+        if (error != NO_ERROR) {
+            ADD_FAILURE() << "failed to initialize SurfaceControl";
+            layer.clear();
+        }
+
+        return layer;
+    }
+
+    virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client,
+                                             const char* name, uint32_t width, uint32_t height,
+                                             PixelFormat format, uint32_t flags,
+                                             SurfaceControl* parent = nullptr,
+                                             uint32_t* outTransformHint = nullptr) {
+        auto layer = client->createSurface(String8(name), width, height, format, flags, parent,
+                                           LayerMetadata(), outTransformHint);
+        EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
+        return layer;
+    }
+
+    virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
+                                           uint32_t flags = 0, SurfaceControl* parent = nullptr,
+                                           uint32_t* outTransformHint = nullptr,
+                                           PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
+        return createLayer(mClient, name, width, height, flags, parent, outTransformHint, format);
+    }
+
+    sp<SurfaceControl> createColorLayer(const char* name, const Color& color,
+                                        SurfaceControl* parent = nullptr) {
+        auto colorLayer = createSurface(mClient, name, 0 /* buffer width */, 0 /* buffer height */,
+                                        PIXEL_FORMAT_RGBA_8888,
+                                        ISurfaceComposerClient::eFXSurfaceEffect, parent);
+        asTransaction([&](Transaction& t) {
+            t.setColor(colorLayer, half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f});
+            t.setAlpha(colorLayer, color.a / 255.0f);
+        });
+        return colorLayer;
+    }
+
+    ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
+        // wait for previous transactions (such as setSize) to complete
+        Transaction().apply(true);
+
+        ANativeWindow_Buffer buffer = {};
+        EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr));
+
+        return buffer;
+    }
+
+    void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
+        ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
+
+        // wait for the newly posted buffer to be latched
+        waitForLayerBuffers();
+    }
+
+    virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color,
+                                           int32_t bufferWidth, int32_t bufferHeight) {
+        ANativeWindow_Buffer buffer;
+        ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+        TransactionUtils::fillANativeWindowBufferColor(buffer,
+                                                       Rect(0, 0, bufferWidth, bufferHeight),
+                                                       color);
+        postBufferQueueLayerBuffer(layer);
+    }
+
+    virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color,
+                                           int32_t bufferWidth, int32_t bufferHeight) {
+        sp<GraphicBuffer> buffer =
+                new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
+                                  BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                          BufferUsage::COMPOSER_OVERLAY,
+                                  "test");
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight),
+                                                 color);
+        Transaction().setBuffer(layer, buffer).apply();
+    }
+
+    void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color,
+                        int32_t bufferWidth, int32_t bufferHeight) {
+        switch (mLayerType) {
+            case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+                fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight);
+                break;
+            case ISurfaceComposerClient::eFXSurfaceBufferState:
+                fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight);
+                break;
+            default:
+                ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
+        }
+    }
+
+    void fillLayerQuadrant(uint32_t mLayerType, const sp<SurfaceControl>& layer,
+                           int32_t bufferWidth, int32_t bufferHeight, const Color& topLeft,
+                           const Color& topRight, const Color& bottomLeft,
+                           const Color& bottomRight) {
+        switch (mLayerType) {
+            case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+                fillBufferQueueLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
+                                             bottomLeft, bottomRight);
+                break;
+            case ISurfaceComposerClient::eFXSurfaceBufferState:
+                fillBufferStateLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
+                                             bottomLeft, bottomRight);
+                break;
+            default:
+                ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
+        }
+    }
+
+    virtual void fillBufferQueueLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
+                                              int32_t bufferHeight, const Color& topLeft,
+                                              const Color& topRight, const Color& bottomLeft,
+                                              const Color& bottomRight) {
+        ANativeWindow_Buffer buffer;
+        ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+        ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
+
+        const int32_t halfW = bufferWidth / 2;
+        const int32_t halfH = bufferHeight / 2;
+        TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
+        TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH),
+                                                       topRight);
+        TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight),
+                                                       bottomLeft);
+        TransactionUtils::fillANativeWindowBufferColor(buffer,
+                                                       Rect(halfW, halfH, bufferWidth,
+                                                            bufferHeight),
+                                                       bottomRight);
+
+        postBufferQueueLayerBuffer(layer);
+    }
+
+    virtual void fillBufferStateLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
+                                              int32_t bufferHeight, const Color& topLeft,
+                                              const Color& topRight, const Color& bottomLeft,
+                                              const Color& bottomRight) {
+        sp<GraphicBuffer> buffer =
+                new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
+                                  BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                          BufferUsage::COMPOSER_OVERLAY,
+                                  "test");
+
+        ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
+
+        const int32_t halfW = bufferWidth / 2;
+        const int32_t halfH = bufferHeight / 2;
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH),
+                                                 topRight);
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight),
+                                                 bottomLeft);
+        TransactionUtils::fillGraphicBufferColor(buffer,
+                                                 Rect(halfW, halfH, bufferWidth, bufferHeight),
+                                                 bottomRight);
+
+        Transaction().setBuffer(layer, buffer).setSize(layer, bufferWidth, bufferHeight).apply();
+    }
+
+    std::unique_ptr<ScreenCapture> screenshot() {
+        std::unique_ptr<ScreenCapture> screenshot;
+        ScreenCapture::captureScreen(&screenshot);
+        return screenshot;
+    }
+
+    void asTransaction(const std::function<void(Transaction&)>& exec) {
+        Transaction t;
+        exec(t);
+        t.apply(true);
+    }
+
+    static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+        static BufferGenerator bufferGenerator;
+        return bufferGenerator.get(outBuffer, outFence);
+    }
+
+    sp<SurfaceComposerClient> mClient;
+
+    sp<IBinder> mDisplay;
+    uint32_t mDisplayWidth;
+    uint32_t mDisplayHeight;
+    uint32_t mDisplayLayerStack;
+    Rect mDisplayRect = Rect::INVALID_RECT;
+
+    // leave room for ~256 layers
+    const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
+
+    sp<SurfaceControl> mBlackBgSurface;
+    bool mColorManagementUsed;
+
+private:
+    void SetUpDisplay() {
+        mDisplay = mClient->getInternalDisplayToken();
+        ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
+
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplay, &config));
+        mDisplayRect = Rect(config.resolution);
+        mDisplayWidth = mDisplayRect.getWidth();
+        mDisplayHeight = mDisplayRect.getHeight();
+
+        // After a new buffer is queued, SurfaceFlinger is notified and will
+        // latch the new buffer on next vsync.  Let's heuristically wait for 3
+        // vsyncs.
+        mBufferPostDelay = static_cast<int32_t>(1e6 / config.refreshRate) * 3;
+
+        mDisplayLayerStack = 0;
+
+        mBlackBgSurface =
+                createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
+                              PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect);
+
+        // set layer stack (b/68888219)
+        Transaction t;
+        t.setDisplayLayerStack(mDisplay, mDisplayLayerStack);
+        t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
+        t.setLayerStack(mBlackBgSurface, mDisplayLayerStack);
+        t.setColor(mBlackBgSurface, half3{0, 0, 0});
+        t.setLayer(mBlackBgSurface, mLayerZBase);
+        t.apply();
+    }
+
+    void waitForLayerBuffers() {
+        // Request an empty transaction to get applied synchronously to ensure the buffer is
+        // latched.
+        Transaction().apply(true);
+        usleep(mBufferPostDelay);
+    }
+
+    int32_t mBufferPostDelay;
+
+    friend class LayerRenderPathTestHarness;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
new file mode 100644
index 0000000..1f8f7ed
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <private/android_filesystem_config.h>
+#include <thread>
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+    sp<GraphicBuffer> outBuffer;
+    Transaction()
+            .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
+            .apply(true);
+    ASSERT_EQ(PERMISSION_DENIED,
+              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+
+    UIDFaker f(AID_SYSTEM);
+
+    // By default the system can capture screenshots with secure layers but they
+    // will be blacked out
+    ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+
+    {
+        SCOPED_TRACE("as system");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
+    // to receive them...we are expected to take care with the results.
+    bool outCapturedSecureLayers;
+    ASSERT_EQ(NO_ERROR,
+              composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers,
+                                      ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0,
+                                      0, false, ui::ROTATION_0, true));
+    ASSERT_EQ(true, outCapturedSecureLayers);
+    ScreenCapture sc(outBuffer);
+    sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    Transaction().setTransformToDisplayInverse(layer, false).apply();
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::GREEN, 32, 32));
+
+    Transaction().setTransformToDisplayInverse(layer, true).apply();
+}
+
+TEST_F(LayerTransactionTest, SetSidebandStreamNull_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    // verify this doesn't cause a crash
+    Transaction().setSidebandStream(layer, nullptr).apply();
+}
+
+TEST_F(LayerTransactionTest, ReparentToSelf) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+    Transaction().reparent(layer, layer->getHandle()).apply();
+
+    {
+        // We expect the transaction to be silently dropped, but for SurfaceFlinger
+        // to still be functioning.
+        SCOPED_TRACE("after reparent to self");
+        const Rect rect(0, 0, 32, 32);
+        auto shot = screenshot();
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
+    }
+}
+
+// This test ensures that when we drop an app buffer in SurfaceFlinger, we merge
+// the dropped buffer's damage region into the next buffer's damage region. If
+// we don't do this, we'll report an incorrect damage region to hardware
+// composer, resulting in broken rendering. This test checks the BufferQueue
+// case.
+//
+// Unfortunately, we don't currently have a way to inspect the damage region
+// SurfaceFlinger sends to hardware composer from a test, so this test requires
+// the dev to manually watch the device's screen during the test to spot broken
+// rendering. Because the results can't be automatically verified, this test is
+// marked disabled.
+TEST_F(LayerTransactionTest, DISABLED_BufferQueueLayerMergeDamageRegionWhenDroppingBuffers) {
+    const int width = mDisplayWidth;
+    const int height = mDisplayHeight;
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
+    const auto producer = layer->getIGraphicBufferProducer();
+    const sp<IProducerListener> stubListener(new StubProducerListener);
+    IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
+    ASSERT_EQ(OK, producer->connect(stubListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput));
+
+    std::map<int, sp<GraphicBuffer>> slotMap;
+    auto slotToBuffer = [&](int slot, sp<GraphicBuffer>* buf) {
+        ASSERT_NE(nullptr, buf);
+        const auto iter = slotMap.find(slot);
+        ASSERT_NE(slotMap.end(), iter);
+        *buf = iter->second;
+    };
+
+    auto dequeue = [&](int* outSlot) {
+        ASSERT_NE(nullptr, outSlot);
+        *outSlot = -1;
+        int slot;
+        sp<Fence> fence;
+        uint64_t age;
+        FrameEventHistoryDelta timestamps;
+        const status_t dequeueResult =
+                producer->dequeueBuffer(&slot, &fence, width, height, PIXEL_FORMAT_RGBA_8888,
+                                        GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                        &age, &timestamps);
+        if (dequeueResult == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+            sp<GraphicBuffer> newBuf;
+            ASSERT_EQ(OK, producer->requestBuffer(slot, &newBuf));
+            ASSERT_NE(nullptr, newBuf.get());
+            slotMap[slot] = newBuf;
+        } else {
+            ASSERT_EQ(OK, dequeueResult);
+        }
+        *outSlot = slot;
+    };
+
+    auto queue = [&](int slot, const Region& damage, nsecs_t displayTime) {
+        IGraphicBufferProducer::QueueBufferInput input(
+                /*timestamp=*/displayTime, /*isAutoTimestamp=*/false, HAL_DATASPACE_UNKNOWN,
+                /*crop=*/Rect::EMPTY_RECT, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
+                /*transform=*/0, Fence::NO_FENCE);
+        input.setSurfaceDamage(damage);
+        IGraphicBufferProducer::QueueBufferOutput output;
+        ASSERT_EQ(OK, producer->queueBuffer(slot, input, &output));
+    };
+
+    auto fillAndPostBuffers = [&](const Color& color) {
+        int slot1;
+        ASSERT_NO_FATAL_FAILURE(dequeue(&slot1));
+        int slot2;
+        ASSERT_NO_FATAL_FAILURE(dequeue(&slot2));
+
+        sp<GraphicBuffer> buf1;
+        ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot1, &buf1));
+        sp<GraphicBuffer> buf2;
+        ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot2, &buf2));
+        TransactionUtils::fillGraphicBufferColor(buf1, Rect(width, height), color);
+        TransactionUtils::fillGraphicBufferColor(buf2, Rect(width, height), color);
+
+        const auto displayTime = systemTime() + milliseconds_to_nanoseconds(100);
+        ASSERT_NO_FATAL_FAILURE(queue(slot1, Region::INVALID_REGION, displayTime));
+        ASSERT_NO_FATAL_FAILURE(
+                queue(slot2, Region(Rect(width / 3, height / 3, 2 * width / 3, 2 * height / 3)),
+                      displayTime));
+    };
+
+    const auto startTime = systemTime();
+    const std::array<Color, 3> colors = {Color::RED, Color::GREEN, Color::BLUE};
+    int colorIndex = 0;
+    while (nanoseconds_to_seconds(systemTime() - startTime) < 10) {
+        ASSERT_NO_FATAL_FAILURE(fillAndPostBuffers(colors[colorIndex++ % colors.size()]));
+        std::this_thread::sleep_for(1s);
+    }
+
+    ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU));
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
new file mode 100644
index 0000000..7d4314f
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <cutils/properties.h>
+#include <gui/BufferItemConsumer.h>
+#include "TransactionTestHarnesses.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class LayerTypeAndRenderTypeTransactionTest
+      : public LayerTypeTransactionHarness,
+        public ::testing::WithParamInterface<std::tuple<uint32_t, RenderPath>> {
+public:
+    LayerTypeAndRenderTypeTransactionTest()
+          : LayerTypeTransactionHarness(std::get<0>(GetParam())),
+            mRenderPathHarness(LayerRenderPathTestHarness(this, std::get<1>(GetParam()))) {}
+
+    std::unique_ptr<ScreenCapture> getScreenCapture() {
+        return mRenderPathHarness.getScreenCapture();
+    }
+
+protected:
+    LayerRenderPathTestHarness mRenderPathHarness;
+};
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+INSTANTIATE_TEST_CASE_P(
+        LayerTypeAndRenderTypeTransactionTests, LayerTypeAndRenderTypeTransactionTest,
+        ::testing::Combine(
+                ::testing::Values(
+                        static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
+                        static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)),
+                ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT)));
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetSizeInvalid) {
+    // cannot test robustness against invalid sizes (zero or really huge)
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZBasic) {
+    sp<SurfaceControl> layerR;
+    sp<SurfaceControl> layerG;
+    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+
+    Transaction().setLayer(layerR, mLayerZBase + 1).apply();
+    {
+        SCOPED_TRACE("layerR");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+
+    Transaction().setLayer(layerG, mLayerZBase + 2).apply();
+    {
+        SCOPED_TRACE("layerG");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetRelativeZBug64572777) {
+    sp<SurfaceControl> layerR;
+    sp<SurfaceControl> layerG;
+
+    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+
+    Transaction()
+            .setPosition(layerG, 16, 16)
+            .setRelativeLayer(layerG, layerR->getHandle(), 1)
+            .apply();
+
+    Transaction().reparent(layerG, nullptr).apply();
+
+    // layerG should have been removed
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsHidden) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction().setFlags(layer, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden).apply();
+    {
+        SCOPED_TRACE("layer hidden");
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
+    }
+
+    Transaction().setFlags(layer, 0, layer_state_t::eLayerHidden).apply();
+    {
+        SCOPED_TRACE("layer shown");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsOpaque) {
+    const Color translucentRed = {100, 0, 0, 100};
+    sp<SurfaceControl> layerR;
+    sp<SurfaceControl> layerG;
+    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, translucentRed, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+
+    Transaction()
+            .setLayer(layerR, mLayerZBase + 1)
+            .setFlags(layerR, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque)
+            .apply();
+    {
+        SCOPED_TRACE("layerR opaque");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255});
+    }
+
+    Transaction().setFlags(layerR, 0, layer_state_t::eLayerOpaque).apply();
+    {
+        SCOPED_TRACE("layerR translucent");
+        const uint8_t g = uint8_t(255 - translucentRed.a);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZNegative) {
+    sp<SurfaceControl> parent =
+            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+                                              ISurfaceComposerClient::eFXSurfaceContainer);
+    Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
+    sp<SurfaceControl> layerR;
+    sp<SurfaceControl> layerG;
+    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+
+    Transaction()
+            .reparent(layerR, parent->getHandle())
+            .reparent(layerG, parent->getHandle())
+            .apply();
+    Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply();
+    {
+        SCOPED_TRACE("layerR");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+
+    Transaction().setLayer(layerR, -3).apply();
+    {
+        SCOPED_TRACE("layerG");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetAlphaClamped) {
+    const Color color = {64, 0, 0, 255};
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, color, 32, 32));
+
+    Transaction().setAlpha(layer, 2.0f).apply();
+    {
+        SCOPED_TRACE("clamped to 1.0f");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), color);
+    }
+
+    Transaction().setAlpha(layer, -1.0f).apply();
+    {
+        SCOPED_TRACE("clamped to 0.0f");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadius) {
+    sp<SurfaceControl> layer;
+    const uint8_t size = 64;
+    const uint8_t testArea = 4;
+    const float cornerRadius = 20.0f;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
+
+    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+        Transaction()
+                .setCornerRadius(layer, cornerRadius)
+                .setCrop_legacy(layer, Rect(0, 0, size, size))
+                .apply();
+    } else {
+        Transaction()
+                .setCornerRadius(layer, cornerRadius)
+                .setFrame(layer, Rect(0, 0, size, size))
+                .apply();
+    }
+    {
+        const uint8_t bottom = size - 1;
+        const uint8_t right = size - 1;
+        auto shot = getScreenCapture();
+        // Transparent corners
+        shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+        shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
+        shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
+        shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::BLACK);
+        // Solid center
+        shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2,
+                               size / 2 + testArea / 2, size / 2 + testArea / 2),
+                          Color::RED);
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusRotated) {
+    sp<SurfaceControl> parent;
+    sp<SurfaceControl> child;
+    const uint8_t size = 64;
+    const uint8_t testArea = 4;
+    const float cornerRadius = 20.0f;
+    ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, size, size));
+    ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size));
+
+    auto transaction = Transaction()
+                               .setCornerRadius(parent, cornerRadius)
+                               .setCrop_legacy(parent, Rect(0, 0, size, size))
+                               .reparent(child, parent->getHandle())
+                               .setPosition(child, 0, size)
+                               // Rotate by half PI
+                               .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f);
+    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+        transaction.setCrop_legacy(parent, Rect(0, 0, size, size));
+    } else {
+        transaction.setFrame(parent, Rect(0, 0, size, size));
+    }
+    transaction.apply();
+
+    {
+        const uint8_t bottom = size - 1;
+        const uint8_t right = size - 1;
+        auto shot = getScreenCapture();
+        // Edges are transparent
+        shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
+        shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
+        shot->expectColor(Rect(0, bottom - testArea, testArea, bottom - testArea), Color::BLACK);
+        shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK);
+        // Solid center
+        shot->expectColor(Rect(size / 2 - testArea / 2, size / 2 - testArea / 2,
+                               size / 2 + testArea / 2, size / 2 + testArea / 2),
+                          Color::GREEN);
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildCrop) {
+    sp<SurfaceControl> parent;
+    sp<SurfaceControl> child;
+    const uint8_t size = 64;
+    const uint8_t testArea = 4;
+    const float cornerRadius = 20.0f;
+    ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, size, size));
+    ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size / 2));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size / 2));
+
+    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+        Transaction()
+                .setCornerRadius(parent, cornerRadius)
+                .setCrop_legacy(parent, Rect(0, 0, size, size))
+                .reparent(child, parent->getHandle())
+                .setPosition(child, 0, size / 2)
+                .apply();
+    } else {
+        Transaction()
+                .setCornerRadius(parent, cornerRadius)
+                .setFrame(parent, Rect(0, 0, size, size))
+                .reparent(child, parent->getHandle())
+                .setFrame(child, Rect(0, size / 2, size, size))
+                .apply();
+    }
+    {
+        const uint8_t bottom = size - 1;
+        const uint8_t right = size - 1;
+        auto shot = getScreenCapture();
+        // Top edge of child should not have rounded corners because it's translated in the parent
+        shot->expectColor(Rect(0, size / 2, right, static_cast<int>(bottom - cornerRadius)),
+                          Color::GREEN);
+        // But bottom edges should have been clipped according to parent bounds
+        shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
+        shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK);
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadius) {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.surface_flinger.supports_background_blur", value, "0");
+    if (!atoi(value)) {
+        // This device doesn't support blurs, no-op.
+        return;
+    }
+
+    auto size = 256;
+    auto center = size / 2;
+    auto blurRadius = 50;
+
+    sp<SurfaceControl> backgroundLayer;
+    ASSERT_NO_FATAL_FAILURE(backgroundLayer = createLayer("background", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(backgroundLayer, Color::GREEN, size, size));
+
+    sp<SurfaceControl> leftLayer;
+    ASSERT_NO_FATAL_FAILURE(leftLayer = createLayer("left", size / 2, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(leftLayer, Color::RED, size / 2, size));
+
+    sp<SurfaceControl> blurLayer;
+    ASSERT_NO_FATAL_FAILURE(blurLayer = createLayer("blur", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(blurLayer, Color::TRANSPARENT, size, size));
+
+    Transaction().setBackgroundBlurRadius(blurLayer, blurRadius).apply();
+
+    auto shot = getScreenCapture();
+    // Edges are mixed
+    shot->expectColor(Rect(center - 1, center - 5, center, center + 5), Color{150, 150, 0, 255},
+                      50 /* tolerance */);
+    shot->expectColor(Rect(center, center - 5, center + 1, center + 5), Color{150, 150, 0, 255},
+                      50 /* tolerance */);
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusOnMultipleLayers) {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.surface_flinger.supports_background_blur", value, "0");
+    if (!atoi(value)) {
+        // This device doesn't support blurs, no-op.
+        return;
+    }
+
+    auto size = 256;
+    auto center = size / 2;
+    auto blurRadius = 50;
+
+    sp<SurfaceControl> backgroundLayer;
+    ASSERT_NO_FATAL_FAILURE(backgroundLayer = createLayer("background", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(backgroundLayer, Color::GREEN, size, size));
+
+    sp<SurfaceControl> leftLayer;
+    ASSERT_NO_FATAL_FAILURE(leftLayer = createLayer("left", size / 2, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(leftLayer, Color::RED, size / 2, size));
+
+    sp<SurfaceControl> blurLayer1;
+    auto centralSquareSize = size / 2;
+    ASSERT_NO_FATAL_FAILURE(blurLayer1 =
+                                    createLayer("blur1", centralSquareSize, centralSquareSize));
+    ASSERT_NO_FATAL_FAILURE(
+            fillLayerColor(blurLayer1, Color::BLUE, centralSquareSize, centralSquareSize));
+
+    sp<SurfaceControl> blurLayer2;
+    ASSERT_NO_FATAL_FAILURE(blurLayer2 = createLayer("blur2", size, size));
+    ASSERT_NO_FATAL_FAILURE(
+            fillLayerColor(blurLayer2, Color::TRANSPARENT, centralSquareSize, centralSquareSize));
+
+    Transaction()
+            .setBackgroundBlurRadius(blurLayer1, blurRadius)
+            .setBackgroundBlurRadius(blurLayer2, blurRadius)
+            .apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(center - 5, center - 5, center, center), Color{100, 100, 100, 255},
+                      40 /* tolerance */);
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
+    sp<SurfaceControl> bufferLayer;
+    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED, 32, 32));
+
+    // color is ignored
+    Transaction().setColor(bufferLayer, half3(0.0f, 1.0f, 0.0f)).apply();
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetLayerStackBasic) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply();
+    {
+        SCOPED_TRACE("non-existing layer stack");
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
+    }
+
+    Transaction().setLayerStack(layer, mDisplayLayerStack).apply();
+    {
+        SCOPED_TRACE("original layer stack");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBufferFormat) {
+    int32_t width = 100;
+    int32_t height = 100;
+    Rect crop = Rect(0, 0, width, height);
+
+    sp<SurfaceControl> behindLayer = createColorLayer("Behind layer", Color::RED);
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, 0, nullptr, nullptr,
+                                                PIXEL_FORMAT_RGBX_8888));
+
+    Transaction()
+            .setLayer(layer, INT32_MAX - 1)
+            .show(layer)
+            .setLayerStack(behindLayer, mDisplayLayerStack)
+            .setCrop_legacy(behindLayer, crop)
+            .setLayer(behindLayer, INT32_MAX - 2)
+            .show(behindLayer)
+            .apply();
+
+    sp<Surface> surface = layer->getSurface();
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(width, height, PIXEL_FORMAT_RGBX_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
+
+    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+        Surface::attachAndQueueBufferWithDataspace(surface.get(), buffer, ui::Dataspace::V0_SRGB);
+    } else {
+        Transaction().setBuffer(layer, buffer).apply();
+    }
+
+    {
+        SCOPED_TRACE("Buffer Opaque Format");
+        auto shot = screenshot();
+        shot->expectColor(crop, Color::BLACK);
+    }
+
+    buffer = new GraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, 1,
+                               BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                       BufferUsage::COMPOSER_OVERLAY,
+                               "test");
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
+
+    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+        Surface::attachAndQueueBufferWithDataspace(surface.get(), buffer, ui::Dataspace::V0_SRGB);
+    } else {
+        Transaction().setBuffer(layer, buffer).apply();
+    }
+
+    {
+        SCOPED_TRACE("Buffer Transparent Format");
+        auto shot = screenshot();
+        shot->expectColor(crop, Color::RED);
+    }
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
new file mode 100644
index 0000000..84780ba
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <gui/BufferItemConsumer.h>
+#include "TransactionTestHarnesses.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class LayerTypeTransactionTest : public LayerTypeTransactionHarness,
+                                 public ::testing::WithParamInterface<uint32_t> {
+public:
+    LayerTypeTransactionTest() : LayerTypeTransactionHarness(GetParam()) {}
+};
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+INSTANTIATE_TEST_CASE_P(
+        LayerTypeTransactionTests, LayerTypeTransactionTest,
+        ::testing::Values(static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
+                          static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)));
+
+TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) {
+    sp<SurfaceControl> parent =
+            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+                                              ISurfaceComposerClient::eFXSurfaceContainer);
+    Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
+    sp<SurfaceControl> layerR;
+    sp<SurfaceControl> layerG;
+    sp<SurfaceControl> layerB;
+    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32));
+
+    Transaction().reparent(layerB, parent->getHandle()).apply();
+
+    // layerR = mLayerZBase, layerG = layerR - 1, layerB = -2
+    Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply();
+
+    std::unique_ptr<ScreenCapture> screenshot;
+    // only layerB is in this range
+    sp<IBinder> parentHandle = parent->getHandle();
+    ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32));
+    screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+}
+
+TEST_P(LayerTypeTransactionTest, SetLayerAndRelative) {
+    sp<SurfaceControl> parent =
+            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+                                              ISurfaceComposerClient::eFXSurfaceEffect);
+
+    sp<SurfaceControl> childLayer;
+    ASSERT_NO_FATAL_FAILURE(
+            childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
+                                                           0 /* buffer height */,
+                                                           ISurfaceComposerClient::eFXSurfaceEffect,
+                                                           parent.get()));
+    Transaction()
+            .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
+            .setColor(parent, half3{0.0f, 0.0f, 0.0f})
+            .show(childLayer)
+            .show(parent)
+            .setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+            .setCrop_legacy(childLayer, Rect(0, 0, 20, 30))
+            .apply();
+
+    Transaction()
+            .setRelativeLayer(childLayer, parent->getHandle(), -1)
+            .setLayer(childLayer, 1)
+            .apply();
+
+    {
+        SCOPED_TRACE("setLayer above");
+        // Set layer should get applied and place the child above.
+        std::unique_ptr<ScreenCapture> screenshot;
+        ScreenCapture::captureScreen(&screenshot);
+        screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
+    }
+
+    Transaction()
+            .setLayer(childLayer, 1)
+            .setRelativeLayer(childLayer, parent->getHandle(), -1)
+            .apply();
+
+    {
+        SCOPED_TRACE("setRelative below");
+        // Set relative layer should get applied and place the child below.
+        std::unique_ptr<ScreenCapture> screenshot;
+        ScreenCapture::captureScreen(&screenshot);
+        screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
+    }
+}
+
+TEST_P(LayerTypeTransactionTest, HideRelativeParentHidesLayer) {
+    sp<SurfaceControl> parent =
+            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+                                              ISurfaceComposerClient::eFXSurfaceEffect);
+    sp<SurfaceControl> relativeParent =
+            LayerTransactionTest::createLayer("RelativeParent", 0 /* buffer width */,
+                                              0 /* buffer height */,
+                                              ISurfaceComposerClient::eFXSurfaceEffect);
+
+    sp<SurfaceControl> childLayer;
+    ASSERT_NO_FATAL_FAILURE(
+            childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
+                                                           0 /* buffer height */,
+                                                           ISurfaceComposerClient::eFXSurfaceEffect,
+                                                           parent.get()));
+    Transaction()
+            .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
+            .setColor(parent, half3{0.0f, 0.0f, 0.0f})
+            .setColor(relativeParent, half3{0.0f, 1.0f, 0.0f})
+            .show(childLayer)
+            .show(parent)
+            .show(relativeParent)
+            .setLayer(parent, mLayerZBase - 1)
+            .setLayer(relativeParent, mLayerZBase)
+            .apply();
+
+    Transaction().setRelativeLayer(childLayer, relativeParent->getHandle(), 1).apply();
+
+    {
+        SCOPED_TRACE("setLayer above");
+        // Set layer should get applied and place the child above.
+        std::unique_ptr<ScreenCapture> screenshot;
+        ScreenCapture::captureScreen(&screenshot);
+        screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
+    }
+
+    Transaction().hide(relativeParent).apply();
+
+    {
+        SCOPED_TRACE("hide relative parent");
+        // The relative should no longer be visible.
+        std::unique_ptr<ScreenCapture> screenshot;
+        ScreenCapture::captureScreen(&screenshot);
+        screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
+    }
+}
+
+TEST_P(LayerTypeTransactionTest, SetFlagsSecure) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
+
+    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+    sp<GraphicBuffer> outBuffer;
+    Transaction()
+            .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
+            .apply(true);
+    ASSERT_EQ(PERMISSION_DENIED,
+              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+
+    Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
+    ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+}
+TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+
+    sp<IBinder> handle = layer->getHandle();
+    ASSERT_TRUE(handle != nullptr);
+
+    FrameStats frameStats;
+    mClient->getLayerFrameStats(handle, &frameStats);
+
+    ASSERT_GT(frameStats.refreshPeriodNano, static_cast<nsecs_t>(0));
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
new file mode 100644
index 0000000..cdd9d92
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -0,0 +1,1719 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class LayerUpdateTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        ASSERT_FALSE(display == nullptr);
+
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        const ui::Size& resolution = config.resolution;
+
+        // Background surface
+        mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
+                                        resolution.getHeight(), 0);
+        ASSERT_TRUE(mBGSurfaceControl != nullptr);
+        ASSERT_TRUE(mBGSurfaceControl->isValid());
+        TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
+
+        // Foreground surface
+        mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0);
+
+        ASSERT_TRUE(mFGSurfaceControl != nullptr);
+        ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+        TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+
+        // Synchronization surface
+        mSyncSurfaceControl = createLayer(String8("Sync Test Surface"), 1, 1, 0);
+        ASSERT_TRUE(mSyncSurfaceControl != nullptr);
+        ASSERT_TRUE(mSyncSurfaceControl->isValid());
+
+        TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+
+        asTransaction([&](Transaction& t) {
+            t.setDisplayLayerStack(display, 0);
+
+            t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
+
+            t.setLayer(mFGSurfaceControl, INT32_MAX - 1)
+                    .setPosition(mFGSurfaceControl, 64, 64)
+                    .show(mFGSurfaceControl);
+
+            t.setLayer(mSyncSurfaceControl, INT32_MAX - 1)
+                    .setPosition(mSyncSurfaceControl, resolution.getWidth() - 2,
+                                 resolution.getHeight() - 2)
+                    .show(mSyncSurfaceControl);
+        });
+    }
+
+    virtual void TearDown() {
+        LayerTransactionTest::TearDown();
+        mBGSurfaceControl = 0;
+        mFGSurfaceControl = 0;
+        mSyncSurfaceControl = 0;
+    }
+
+    void waitForPostedBuffers() {
+        // Since the sync surface is in synchronous mode (i.e. double buffered)
+        // posting three buffers to it should ensure that at least two
+        // SurfaceFlinger::handlePageFlip calls have been made, which should
+        // guaranteed that a buffer posted to another Surface has been retired.
+        TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+        TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+        TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+    }
+
+    sp<SurfaceControl> mBGSurfaceControl;
+    sp<SurfaceControl> mFGSurfaceControl;
+
+    // This surface is used to ensure that the buffers posted to
+    // mFGSurfaceControl have been picked up by SurfaceFlinger.
+    sp<SurfaceControl> mSyncSurfaceControl;
+};
+
+TEST_F(LayerUpdateTest, RelativesAreNotDetached) {
+    std::unique_ptr<ScreenCapture> sc;
+
+    sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0);
+    TransactionUtils::fillSurfaceRGBA8(relative, 10, 10, 10);
+    waitForPostedBuffers();
+
+    Transaction{}
+            .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1)
+            .setPosition(relative, 64, 64)
+            .apply();
+
+    {
+        // The relative should be on top of the FG control.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(64, 64, 10, 10, 10);
+    }
+    Transaction{}.detachChildren(mFGSurfaceControl).apply();
+
+    {
+        // Nothing should change at this point.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(64, 64, 10, 10, 10);
+    }
+
+    Transaction{}.hide(relative).apply();
+
+    {
+        // Ensure that the relative was actually hidden, rather than
+        // being left in the detached but visible state.
+        ScreenCapture::captureScreen(&sc);
+        sc->expectFGColor(64, 64);
+    }
+}
+
+class GeometryLatchingTest : public LayerUpdateTest {
+protected:
+    void EXPECT_INITIAL_STATE(const char* trace) {
+        SCOPED_TRACE(trace);
+        ScreenCapture::captureScreen(&sc);
+        // We find the leading edge of the FG surface.
+        sc->expectFGColor(127, 127);
+        sc->expectBGColor(128, 128);
+    }
+
+    void lockAndFillFGBuffer() {
+        TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63, false);
+    }
+
+    void unlockFGBuffer() {
+        sp<Surface> s = mFGSurfaceControl->getSurface();
+        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+        waitForPostedBuffers();
+    }
+
+    void completeFGResize() {
+        TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+        waitForPostedBuffers();
+    }
+    void restoreInitialState() {
+        asTransaction([&](Transaction& t) {
+            t.setSize(mFGSurfaceControl, 64, 64);
+            t.setPosition(mFGSurfaceControl, 64, 64);
+            t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
+        });
+
+        EXPECT_INITIAL_STATE("After restoring initial state");
+    }
+    std::unique_ptr<ScreenCapture> sc;
+};
+
+class CropLatchingTest : public GeometryLatchingTest {
+protected:
+    void EXPECT_CROPPED_STATE(const char* trace) {
+        SCOPED_TRACE(trace);
+        ScreenCapture::captureScreen(&sc);
+        // The edge should be moved back one pixel by our crop.
+        sc->expectFGColor(126, 126);
+        sc->expectBGColor(127, 127);
+        sc->expectBGColor(128, 128);
+    }
+
+    void EXPECT_RESIZE_STATE(const char* trace) {
+        SCOPED_TRACE(trace);
+        ScreenCapture::captureScreen(&sc);
+        // The FG is now resized too 128,128 at 64,64
+        sc->expectFGColor(64, 64);
+        sc->expectFGColor(191, 191);
+        sc->expectBGColor(192, 192);
+    }
+};
+
+TEST_F(LayerUpdateTest, DeferredTransactionTest) {
+    std::unique_ptr<ScreenCapture> sc;
+    {
+        SCOPED_TRACE("before anything");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectBGColor(32, 32);
+        sc->expectFGColor(96, 96);
+        sc->expectBGColor(160, 160);
+    }
+
+    // set up two deferred transactions on different frames
+    asTransaction([&](Transaction& t) {
+        t.setAlpha(mFGSurfaceControl, 0.75);
+        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+                                       mSyncSurfaceControl->getSurface()->getNextFrameNumber());
+    });
+
+    asTransaction([&](Transaction& t) {
+        t.setPosition(mFGSurfaceControl, 128, 128);
+        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+                                       mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
+    });
+
+    {
+        SCOPED_TRACE("before any trigger");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectBGColor(32, 32);
+        sc->expectFGColor(96, 96);
+        sc->expectBGColor(160, 160);
+    }
+
+    // should trigger the first deferred transaction, but not the second one
+    TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+    {
+        SCOPED_TRACE("after first trigger");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectBGColor(32, 32);
+        sc->checkPixel(96, 96, 162, 63, 96);
+        sc->expectBGColor(160, 160);
+    }
+
+    // should show up immediately since it's not deferred
+    asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 1.0); });
+
+    // trigger the second deferred transaction
+    TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+    {
+        SCOPED_TRACE("after second trigger");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectBGColor(32, 32);
+        sc->expectBGColor(96, 96);
+        sc->expectFGColor(160, 160);
+    }
+}
+
+TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) {
+    std::unique_ptr<ScreenCapture> sc;
+
+    sp<SurfaceControl> childNoBuffer =
+            createSurface(mClient, "Bufferless child", 0 /* buffer width */, 0 /* buffer height */,
+                          PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    sp<SurfaceControl> childBuffer = createSurface(mClient, "Buffered child", 20, 20,
+                                                   PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
+    TransactionUtils::fillSurfaceRGBA8(childBuffer, 200, 200, 200);
+    SurfaceComposerClient::Transaction{}
+            .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
+            .show(childNoBuffer)
+            .show(childBuffer)
+            .apply(true);
+    {
+        ScreenCapture::captureScreen(&sc);
+        sc->expectChildColor(73, 73);
+        sc->expectFGColor(74, 74);
+    }
+    SurfaceComposerClient::Transaction{}
+            .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
+            .apply(true);
+    {
+        ScreenCapture::captureScreen(&sc);
+        sc->expectChildColor(73, 73);
+        sc->expectChildColor(74, 74);
+    }
+}
+
+TEST_F(LayerUpdateTest, MergingTransactions) {
+    std::unique_ptr<ScreenCapture> sc;
+    {
+        SCOPED_TRACE("before move");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectBGColor(0, 12);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
+    }
+
+    Transaction t1, t2;
+    t1.setPosition(mFGSurfaceControl, 128, 128);
+    t2.setPosition(mFGSurfaceControl, 0, 0);
+    // We expect that the position update from t2 now
+    // overwrites the position update from t1.
+    t1.merge(std::move(t2));
+    t1.apply();
+
+    {
+        ScreenCapture::captureScreen(&sc);
+        sc->expectFGColor(1, 1);
+    }
+}
+
+TEST_F(LayerUpdateTest, MergingTransactionFlags) {
+    Transaction().hide(mFGSurfaceControl).apply();
+    std::unique_ptr<ScreenCapture> sc;
+    {
+        SCOPED_TRACE("before merge");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectBGColor(0, 12);
+        sc->expectBGColor(75, 75);
+        sc->expectBGColor(145, 145);
+    }
+
+    Transaction t1, t2;
+    t1.show(mFGSurfaceControl);
+    t2.setFlags(mFGSurfaceControl, 0 /* flags */, layer_state_t::eLayerSecure /* mask */);
+    t1.merge(std::move(t2));
+    t1.apply();
+
+    {
+        SCOPED_TRACE("after merge");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectFGColor(75, 75);
+    }
+}
+
+class ChildLayerTest : public LayerUpdateTest {
+protected:
+    void SetUp() override {
+        LayerUpdateTest::SetUp();
+        mChild = createSurface(mClient, "Child surface", 10, 15, PIXEL_FORMAT_RGBA_8888, 0,
+                               mFGSurfaceControl.get());
+        TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+        {
+            SCOPED_TRACE("before anything");
+            mCapture = screenshot();
+            mCapture->expectChildColor(64, 64);
+        }
+    }
+    void TearDown() override {
+        LayerUpdateTest::TearDown();
+        mChild = 0;
+    }
+
+    sp<SurfaceControl> mChild;
+    std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(ChildLayerTest, ChildLayerPositioning) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 10, 10);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        mCapture = screenshot();
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+
+    asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
+
+    {
+        mCapture = screenshot();
+        // Top left of foreground should now be at 0, 0
+        mCapture->expectFGColor(0, 0);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(10, 10);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(20, 20);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerCropping) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+        t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
+    });
+
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(4, 4);
+        mCapture->expectBGColor(5, 5);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerConstraints) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+        t.setPosition(mChild, 63, 63);
+    });
+
+    {
+        mCapture = screenshot();
+        mCapture->expectFGColor(0, 0);
+        // Last pixel in foreground should now be the child.
+        mCapture->expectChildColor(63, 63);
+        // But the child should be constrained and the next pixel
+        // must be the background
+        mCapture->expectBGColor(64, 64);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerScaling) {
+    asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
+
+    // Find the boundary between the parent and child
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(9, 9);
+        mCapture->expectFGColor(10, 10);
+    }
+
+    asTransaction([&](Transaction& t) { t.setMatrix(mFGSurfaceControl, 2.0, 0, 0, 2.0); });
+
+    // The boundary should be twice as far from the origin now.
+    // The pixels from the last test should all be child now
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(9, 9);
+        mCapture->expectChildColor(10, 10);
+        mCapture->expectChildColor(19, 19);
+        mCapture->expectFGColor(20, 20);
+    }
+}
+
+// A child with a scale transform should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildLayerScalingCroppedByParent) {
+    asTransaction([&](Transaction& t) {
+        t.setPosition(mFGSurfaceControl, 0, 0);
+        t.setPosition(mChild, 0, 0);
+    });
+
+    // Find the boundary between the parent and child.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(9, 9);
+        mCapture->expectFGColor(10, 10);
+    }
+
+    asTransaction([&](Transaction& t) { t.setMatrix(mChild, 10.0, 0, 0, 10.0); });
+
+    // The child should fill its parent bounds and be cropped by it.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(63, 63);
+        mCapture->expectBGColor(64, 64);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerAlpha) {
+    TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 0, 0, 254);
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 254, 0, 0);
+    TransactionUtils::fillSurfaceRGBA8(mChild, 0, 254, 0);
+    waitForPostedBuffers();
+
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+    });
+
+    {
+        mCapture = screenshot();
+        // Unblended child color
+        mCapture->checkPixel(0, 0, 0, 254, 0);
+    }
+
+    asTransaction([&](Transaction& t) { t.setAlpha(mChild, 0.5); });
+
+    {
+        mCapture = screenshot();
+        // Child and BG blended.
+        mCapture->checkPixel(0, 0, 127, 127, 0);
+    }
+
+    asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); });
+
+    {
+        mCapture = screenshot();
+        // Child and BG blended.
+        mCapture->checkPixel(0, 0, 95, 64, 95);
+    }
+}
+
+TEST_F(ChildLayerTest, ReparentChildren) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 10, 10);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        mCapture = screenshot();
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+
+    asTransaction([&](Transaction& t) {
+        t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle());
+    });
+
+    {
+        mCapture = screenshot();
+        mCapture->expectFGColor(64, 64);
+        // In reparenting we should have exposed the entire foreground surface.
+        mCapture->expectFGColor(74, 74);
+        // And the child layer should now begin at 10, 10 (since the BG
+        // layer is at (0, 0)).
+        mCapture->expectBGColor(9, 9);
+        mCapture->expectChildColor(10, 10);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) {
+    sp<SurfaceControl> mGrandChild =
+            createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+    TransactionUtils::fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
+
+    {
+        SCOPED_TRACE("Grandchild visible");
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->checkPixel(64, 64, 111, 111, 111);
+    }
+
+    Transaction().reparent(mChild, nullptr).apply();
+    mChild.clear();
+
+    {
+        SCOPED_TRACE("After destroying child");
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->expectFGColor(64, 64);
+    }
+
+    asTransaction([&](Transaction& t) { t.reparent(mGrandChild, mFGSurfaceControl->getHandle()); });
+
+    {
+        SCOPED_TRACE("After reparenting grandchild");
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->checkPixel(64, 64, 111, 111, 111);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildrenRelativeZSurvivesParentDestruction) {
+    sp<SurfaceControl> mGrandChild =
+            createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+    TransactionUtils::fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
+
+    // draw grand child behind the foreground surface
+    asTransaction([&](Transaction& t) {
+        t.setRelativeLayer(mGrandChild, mFGSurfaceControl->getHandle(), -1);
+    });
+
+    {
+        SCOPED_TRACE("Child visible");
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->checkPixel(64, 64, 200, 200, 200);
+    }
+
+    asTransaction([&](Transaction& t) {
+        t.reparent(mChild, nullptr);
+        t.reparentChildren(mChild, mFGSurfaceControl->getHandle());
+    });
+
+    {
+        SCOPED_TRACE("foreground visible reparenting grandchild");
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->checkPixel(64, 64, 195, 63, 63);
+    }
+}
+
+TEST_F(ChildLayerTest, DetachChildrenSameClient) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 10, 10);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        mCapture = screenshot();
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+
+    asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
+
+    asTransaction([&](Transaction& t) { t.hide(mChild); });
+
+    // Since the child has the same client as the parent, it will not get
+    // detached and will be hidden.
+    {
+        mCapture = screenshot();
+        mCapture->expectFGColor(64, 64);
+        mCapture->expectFGColor(74, 74);
+        mCapture->expectFGColor(84, 84);
+    }
+}
+
+TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
+    sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
+    sp<SurfaceControl> mChildNewClient =
+            createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
+                          PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+    ASSERT_TRUE(mChildNewClient->isValid());
+
+    TransactionUtils::fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
+
+    asTransaction([&](Transaction& t) {
+        t.hide(mChild);
+        t.show(mChildNewClient);
+        t.setPosition(mChildNewClient, 10, 10);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        mCapture = screenshot();
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+
+    asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
+
+    asTransaction([&](Transaction& t) { t.hide(mChildNewClient); });
+
+    // Nothing should have changed.
+    {
+        mCapture = screenshot();
+        mCapture->expectFGColor(64, 64);
+        mCapture->expectChildColor(74, 74);
+        mCapture->expectFGColor(84, 84);
+    }
+}
+
+TEST_F(ChildLayerTest, DetachChildrenThenAttach) {
+    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+    sp<SurfaceControl> childNewClient =
+            newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+    ASSERT_TRUE(childNewClient != nullptr);
+    ASSERT_TRUE(childNewClient->isValid());
+
+    TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
+
+    Transaction()
+            .hide(mChild)
+            .show(childNewClient)
+            .setPosition(childNewClient, 10, 10)
+            .setPosition(mFGSurfaceControl, 64, 64)
+            .apply();
+
+    {
+        mCapture = screenshot();
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+
+    Transaction().detachChildren(mFGSurfaceControl).apply();
+    Transaction().hide(childNewClient).apply();
+
+    // Nothing should have changed.
+    {
+        mCapture = screenshot();
+        mCapture->expectFGColor(64, 64);
+        mCapture->expectChildColor(74, 74);
+        mCapture->expectFGColor(84, 84);
+    }
+
+    sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0);
+    fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32,
+                   32);
+    Transaction()
+            .setLayer(newParentSurface, INT32_MAX - 1)
+            .show(newParentSurface)
+            .setPosition(newParentSurface, 20, 20)
+            .reparent(childNewClient, newParentSurface->getHandle())
+            .apply();
+    {
+        mCapture = screenshot();
+        // Child is now hidden.
+        mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED);
+    }
+}
+TEST_F(ChildLayerTest, DetachChildrenWithDeferredTransaction) {
+    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+    sp<SurfaceControl> childNewClient =
+            newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+    ASSERT_TRUE(childNewClient != nullptr);
+    ASSERT_TRUE(childNewClient->isValid());
+
+    TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
+
+    Transaction()
+            .hide(mChild)
+            .show(childNewClient)
+            .setPosition(childNewClient, 10, 10)
+            .setPosition(mFGSurfaceControl, 64, 64)
+            .apply();
+
+    {
+        mCapture = screenshot();
+        Rect rect = Rect(74, 74, 84, 84);
+        mCapture->expectBorder(rect, Color{195, 63, 63, 255});
+        mCapture->expectColor(rect, Color{200, 200, 200, 255});
+    }
+
+    Transaction()
+            .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(),
+                                          mFGSurfaceControl->getSurface()->getNextFrameNumber())
+            .apply();
+    Transaction().detachChildren(mFGSurfaceControl).apply();
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32));
+
+    // BufferLayer can still dequeue buffers even though there's a detached layer with a
+    // deferred transaction.
+    {
+        SCOPED_TRACE("new buffer");
+        mCapture = screenshot();
+        Rect rect = Rect(74, 74, 84, 84);
+        mCapture->expectBorder(rect, Color::RED);
+        mCapture->expectColor(rect, Color{200, 200, 200, 255});
+    }
+}
+
+TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+    });
+
+    {
+        mCapture = screenshot();
+        // We've positioned the child in the top left.
+        mCapture->expectChildColor(0, 0);
+        // But it's only 10x15.
+        mCapture->expectFGColor(10, 15);
+    }
+
+    asTransaction([&](Transaction& t) {
+        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        // We cause scaling by 2.
+        t.setSize(mFGSurfaceControl, 128, 128);
+    });
+
+    {
+        mCapture = screenshot();
+        // We've positioned the child in the top left.
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(10, 10);
+        mCapture->expectChildColor(19, 29);
+        // And now it should be scaled all the way to 20x30
+        mCapture->expectFGColor(20, 30);
+    }
+}
+
+// Regression test for b/37673612
+TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+    });
+
+    {
+        mCapture = screenshot();
+        // We've positioned the child in the top left.
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(9, 14);
+        // But it's only 10x15.
+        mCapture->expectFGColor(10, 15);
+    }
+    // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
+    // the WM specified state size.
+    asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
+    sp<Surface> s = mFGSurfaceControl->getSurface();
+    auto anw = static_cast<ANativeWindow*>(s.get());
+    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+    native_window_set_buffers_dimensions(anw, 64, 128);
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+    waitForPostedBuffers();
+
+    {
+        // The child should still be in the same place and not have any strange scaling as in
+        // b/37673612.
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectFGColor(10, 10);
+    }
+}
+
+// A child with a buffer transform from its parents should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferTransform) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+        t.setSize(mChild, 100, 100);
+    });
+    TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+    {
+        mCapture = screenshot();
+
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(63, 63);
+        mCapture->expectBGColor(64, 64);
+    }
+
+    asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
+    sp<Surface> s = mFGSurfaceControl->getSurface();
+    auto anw = static_cast<ANativeWindow*>(s.get());
+    // Apply a 90 transform on the buffer.
+    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+    native_window_set_buffers_dimensions(anw, 64, 128);
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+    waitForPostedBuffers();
+
+    // The child should be cropped by the new parent bounds.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(99, 63);
+        mCapture->expectFGColor(100, 63);
+        mCapture->expectBGColor(128, 64);
+    }
+}
+
+// A child with a scale transform from its parents should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferScale) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+        t.setSize(mChild, 200, 200);
+    });
+    TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+    {
+        mCapture = screenshot();
+
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(63, 63);
+        mCapture->expectBGColor(64, 64);
+    }
+
+    asTransaction([&](Transaction& t) {
+        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        // Set a scaling by 2.
+        t.setSize(mFGSurfaceControl, 128, 128);
+    });
+
+    // Child should inherit its parents scale but should be cropped by its parent bounds.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(127, 127);
+        mCapture->expectBGColor(128, 128);
+    }
+}
+
+// Regression test for b/127368943
+// Child should ignore the buffer transform but apply parent scale transform.
+TEST_F(ChildLayerTest, ChildrenWithParentBufferTransformAndScale) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+    });
+
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(9, 14);
+        mCapture->expectFGColor(10, 15);
+    }
+
+    // Change the size of the foreground to 128 * 64 so we can test rotation as well.
+    asTransaction([&](Transaction& t) {
+        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        t.setSize(mFGSurfaceControl, 128, 64);
+    });
+    sp<Surface> s = mFGSurfaceControl->getSurface();
+    auto anw = static_cast<ANativeWindow*>(s.get());
+    // Apply a 90 transform on the buffer and submit a buffer half the expected size so that we
+    // have an effective scale of 2.0 applied to the buffer along with a rotation transform.
+    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+    native_window_set_buffers_dimensions(anw, 32, 64);
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+    waitForPostedBuffers();
+
+    // The child should ignore the buffer transform but apply the 2.0 scale from parent.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(19, 29);
+        mCapture->expectFGColor(20, 30);
+    }
+}
+
+TEST_F(ChildLayerTest, Bug36858924) {
+    // Destroy the child layer
+    mChild.clear();
+
+    // Now recreate it as hidden
+    mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888,
+                           ISurfaceComposerClient::eHidden, mFGSurfaceControl.get());
+
+    // Show the child layer in a deferred transaction
+    asTransaction([&](Transaction& t) {
+        t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
+                                       mFGSurfaceControl->getSurface()->getNextFrameNumber());
+        t.show(mChild);
+    });
+
+    // Render the foreground surface a few times
+    //
+    // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
+    // frame because SurfaceFlinger would never process the deferred transaction and would therefore
+    // never acquire/release the first buffer
+    ALOGI("Filling 1");
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
+    ALOGI("Filling 2");
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 0, 255);
+    ALOGI("Filling 3");
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 255, 0, 0);
+    ALOGI("Filling 4");
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
+}
+
+TEST_F(ChildLayerTest, Reparent) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 10, 10);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        mCapture = screenshot();
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+
+    asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); });
+
+    {
+        mCapture = screenshot();
+        mCapture->expectFGColor(64, 64);
+        // In reparenting we should have exposed the entire foreground surface.
+        mCapture->expectFGColor(74, 74);
+        // And the child layer should now begin at 10, 10 (since the BG
+        // layer is at (0, 0)).
+        mCapture->expectBGColor(9, 9);
+        mCapture->expectChildColor(10, 10);
+    }
+}
+
+TEST_F(ChildLayerTest, ReparentToNoParent) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 10, 10);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        mCapture = screenshot();
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // But 10 pixels in we should see the child surface
+        mCapture->expectChildColor(74, 74);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(84, 84);
+    }
+    asTransaction([&](Transaction& t) { t.reparent(mChild, nullptr); });
+    {
+        mCapture = screenshot();
+        // The surface should now be offscreen.
+        mCapture->expectFGColor(64, 64);
+        mCapture->expectFGColor(74, 74);
+        mCapture->expectFGColor(84, 84);
+    }
+}
+
+TEST_F(ChildLayerTest, ReparentFromNoParent) {
+    sp<SurfaceControl> newSurface = createLayer(String8("New Surface"), 10, 10, 0);
+    ASSERT_TRUE(newSurface != nullptr);
+    ASSERT_TRUE(newSurface->isValid());
+
+    TransactionUtils::fillSurfaceRGBA8(newSurface, 63, 195, 63);
+    asTransaction([&](Transaction& t) {
+        t.hide(mChild);
+        t.show(newSurface);
+        t.setPosition(newSurface, 10, 10);
+        t.setLayer(newSurface, INT32_MAX - 2);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        mCapture = screenshot();
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // At 10, 10 we should see the new surface
+        mCapture->checkPixel(10, 10, 63, 195, 63);
+    }
+
+    asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); });
+
+    {
+        mCapture = screenshot();
+        // newSurface will now be a child of mFGSurface so it will be 10, 10 offset from
+        // mFGSurface, putting it at 74, 74.
+        mCapture->expectFGColor(64, 64);
+        mCapture->checkPixel(74, 74, 63, 195, 63);
+        mCapture->expectFGColor(84, 84);
+    }
+}
+
+TEST_F(ChildLayerTest, NestedChildren) {
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 10, 10,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+    TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+
+    {
+        mCapture = screenshot();
+        // Expect the grandchild to begin at 64, 64 because it's a child of mChild layer
+        // which begins at 64, 64
+        mCapture->checkPixel(64, 64, 50, 50, 50);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerRelativeLayer) {
+    sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 128, 128, 0);
+    TransactionUtils::fillSurfaceRGBA8(relative, 255, 255, 255);
+
+    Transaction t;
+    t.setLayer(relative, INT32_MAX)
+            .setRelativeLayer(mChild, relative->getHandle(), 1)
+            .setPosition(mFGSurfaceControl, 0, 0)
+            .apply(true);
+
+    // We expect that the child should have been elevated above our
+    // INT_MAX layer even though it's not a child of it.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(9, 9);
+        mCapture->checkPixel(10, 10, 255, 255, 255);
+    }
+}
+
+class BoundlessLayerTest : public LayerUpdateTest {
+protected:
+    std::unique_ptr<ScreenCapture> mCapture;
+};
+
+// Verify setting a size on a buffer layer has no effect.
+TEST_F(BoundlessLayerTest, BufferLayerIgnoresSize) {
+    sp<SurfaceControl> bufferLayer =
+            createSurface(mClient, "BufferLayer", 45, 45, PIXEL_FORMAT_RGBA_8888, 0,
+                          mFGSurfaceControl.get());
+    ASSERT_TRUE(bufferLayer->isValid());
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::BLACK, 30, 30));
+    asTransaction([&](Transaction& t) { t.show(bufferLayer); });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 94, 94), Color::BLACK);
+        // Buffer layer should not extend past buffer bounds
+        mCapture->expectFGColor(95, 95);
+    }
+}
+
+// Verify a boundless color layer will fill its parent bounds. The parent has a buffer size
+// which will crop the color layer.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentBufferBounds) {
+    sp<SurfaceControl> colorLayer =
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceEffect, mFGSurfaceControl.get());
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify a boundless color layer will fill its parent bounds. The parent has no buffer but has
+// a crop which will be used to crop the color layer.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentCropBounds) {
+    sp<SurfaceControl> cropLayer = createSurface(mClient, "CropLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                                 0 /* flags */, mFGSurfaceControl.get());
+    ASSERT_TRUE(cropLayer->isValid());
+    sp<SurfaceControl> colorLayer =
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceEffect, cropLayer.get());
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(cropLayer);
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // 5 pixels from the foreground we should see the child surface
+        mCapture->expectColor(Rect(69, 69, 74, 74), Color::BLACK);
+        // 10 pixels from the foreground we should be back to the foreground surface
+        mCapture->expectFGColor(74, 74);
+    }
+}
+
+// Verify for boundless layer with no children, their transforms have no effect.
+TEST_F(BoundlessLayerTest, BoundlessColorLayerTransformHasNoEffect) {
+    sp<SurfaceControl> colorLayer =
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceEffect, mFGSurfaceControl.get());
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setPosition(colorLayer, 320, 320);
+        t.setMatrix(colorLayer, 2, 0, 0, 2);
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify for boundless layer with children, their transforms have an effect.
+TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerCanSetTransform) {
+    sp<SurfaceControl> boundlessLayerRightShift =
+            createSurface(mClient, "BoundlessLayerRightShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          0 /* flags */, mFGSurfaceControl.get());
+    ASSERT_TRUE(boundlessLayerRightShift->isValid());
+    sp<SurfaceControl> boundlessLayerDownShift =
+            createSurface(mClient, "BoundlessLayerLeftShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          0 /* flags */, boundlessLayerRightShift.get());
+    ASSERT_TRUE(boundlessLayerDownShift->isValid());
+    sp<SurfaceControl> colorLayer =
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceEffect, boundlessLayerDownShift.get());
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setPosition(boundlessLayerRightShift, 32, 0);
+        t.show(boundlessLayerRightShift);
+        t.setPosition(boundlessLayerDownShift, 0, 32);
+        t.show(boundlessLayerDownShift);
+        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(96, 96, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify child layers do not get clipped if they temporarily move into the negative
+// coordinate space as the result of an intermediate transformation.
+TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) {
+    sp<SurfaceControl> boundlessLayer =
+            mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   0 /* flags */, mFGSurfaceControl.get());
+    ASSERT_TRUE(boundlessLayer != nullptr);
+    ASSERT_TRUE(boundlessLayer->isValid());
+    sp<SurfaceControl> colorLayer =
+            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceEffect, boundlessLayer.get());
+    ASSERT_TRUE(colorLayer != nullptr);
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        // shift child layer off bounds. If this layer was not boundless, we will
+        // expect the child layer to be cropped.
+        t.setPosition(boundlessLayer, 32, 32);
+        t.show(boundlessLayer);
+        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        // undo shift by parent
+        t.setPosition(colorLayer, -32, -32);
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(129, 129);
+    }
+}
+
+// Verify for boundless root layers with children, their transforms have an effect.
+TEST_F(BoundlessLayerTest, RootBoundlessLayerCanSetTransform) {
+    sp<SurfaceControl> rootBoundlessLayer = createSurface(mClient, "RootBoundlessLayer", 0, 0,
+                                                          PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
+    ASSERT_TRUE(rootBoundlessLayer->isValid());
+    sp<SurfaceControl> colorLayer =
+            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+                          ISurfaceComposerClient::eFXSurfaceEffect, rootBoundlessLayer.get());
+
+    ASSERT_TRUE(colorLayer->isValid());
+    asTransaction([&](Transaction& t) {
+        t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
+        t.setPosition(rootBoundlessLayer, 32, 32);
+        t.show(rootBoundlessLayer);
+        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
+        t.setColor(colorLayer, half3{0, 0, 0});
+        t.show(colorLayer);
+        t.hide(mFGSurfaceControl);
+    });
+    {
+        mCapture = screenshot();
+        // Top left of background must now be visible
+        mCapture->expectBGColor(0, 0);
+        // Top left of foreground must now be visible
+        mCapture->expectBGColor(31, 31);
+        // Foreground Surface bounds must be color layer
+        mCapture->expectColor(Rect(32, 32, 96, 96), Color::BLACK);
+        // Color layer should not extend past foreground bounds
+        mCapture->expectBGColor(97, 97);
+    }
+}
+
+class ScreenCaptureTest : public LayerUpdateTest {
+protected:
+    std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
+    auto bgHandle = mBGSurfaceControl->getHandle();
+    ScreenCapture::captureLayers(&mCapture, bgHandle);
+    mCapture->expectBGColor(0, 0);
+    // Doesn't capture FG layer which is at 64, 64
+    mCapture->expectBGColor(64, 64);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    // Captures mFGSurfaceControl layer and its child.
+    ScreenCapture::captureLayers(&mCapture, fgHandle);
+    mCapture->expectFGColor(10, 10);
+    mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    // Captures mFGSurfaceControl's child
+    ScreenCapture::captureChildLayers(&mCapture, fgHandle);
+    mCapture->checkPixel(10, 10, 0, 0, 0);
+    mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerExclude) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            .show(child2)
+            .setLayer(child, 1)
+            .setLayer(child2, 2)
+            .apply(true);
+
+    // Child2 would be visible but its excluded, so we should see child1 color instead.
+    ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
+    mCapture->checkPixel(10, 10, 0, 0, 0);
+    mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
+// Like the last test but verifies that children are also exclude.
+TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+    sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10,
+                                              PIXEL_FORMAT_RGBA_8888, 0, child2.get());
+    TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            .show(child2)
+            .show(child3)
+            .setLayer(child, 1)
+            .setLayer(child2, 2)
+            .apply(true);
+
+    // Child2 would be visible but its excluded, so we should see child1 color instead.
+    ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
+    mCapture->checkPixel(10, 10, 0, 0, 0);
+    mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
+TEST_F(ScreenCaptureTest, CaptureTransparent) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    auto childHandle = child->getHandle();
+
+    // Captures child
+    ScreenCapture::captureLayers(&mCapture, childHandle, {0, 0, 10, 20});
+    mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
+    // Area outside of child's bounds is transparent.
+    mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0});
+}
+
+TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    ASSERT_NE(nullptr, child.get()) << "failed to create surface";
+    sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            // Set relative layer above fg layer so should be shown above when computing all layers.
+            .setRelativeLayer(relative, fgHandle, 1)
+            .show(relative)
+            .apply(true);
+
+    // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured.
+    ScreenCapture::captureLayers(&mCapture, fgHandle);
+    mCapture->expectFGColor(10, 10);
+    mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
+                                                PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            // Set relative layer below fg layer but relative to child layer so it should be shown
+            // above child layer.
+            .setLayer(relative, -1)
+            .setRelativeLayer(relative, child->getHandle(), 1)
+            .show(relative)
+            .apply(true);
+
+    // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its
+    // relative value should be taken into account, placing it above child layer.
+    ScreenCapture::captureLayers(&mCapture, fgHandle);
+    mCapture->expectFGColor(10, 10);
+    // Relative layer is showing on top of child layer
+    mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) {
+    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    sp<GraphicBuffer> outBuffer;
+    Rect sourceCrop(0, 0, 10, 10);
+    ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+    ScreenCapture sc(outBuffer);
+
+    sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
+    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+    Rect layerCrop(0, 0, 10, 10);
+    SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    sp<GraphicBuffer> outBuffer;
+    Rect sourceCrop = Rect();
+    ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+    ScreenCapture sc(outBuffer);
+
+    sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) {
+    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    sp<GraphicBuffer> outBuffer;
+    Rect sourceCrop = Rect();
+
+    ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+}
+
+TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    sp<GraphicBuffer> outBuffer;
+    Rect sourceCrop = Rect();
+    ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+
+    TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
+    SurfaceComposerClient::Transaction().apply(true);
+    ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+    ScreenCapture sc(outBuffer);
+    sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+// In the following tests we verify successful skipping of a parent layer,
+// so we use the same verification logic and only change how we mutate
+// the parent layer to verify that various properties are ignored.
+class ScreenCaptureChildOnlyTest : public LayerUpdateTest {
+public:
+    void SetUp() override {
+        LayerUpdateTest::SetUp();
+
+        mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+                               mFGSurfaceControl.get());
+        TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+        SurfaceComposerClient::Transaction().show(mChild).apply(true);
+    }
+
+    void verify(std::function<void()> verifyStartingState) {
+        // Verify starting state before a screenshot is taken.
+        verifyStartingState();
+
+        // Verify child layer does not inherit any of the properties of its
+        // parent when its screenshot is captured.
+        auto fgHandle = mFGSurfaceControl->getHandle();
+        ScreenCapture::captureChildLayers(&mCapture, fgHandle);
+        mCapture->checkPixel(10, 10, 0, 0, 0);
+        mCapture->expectChildColor(0, 0);
+
+        // Verify all assumptions are still true after the screenshot is taken.
+        verifyStartingState();
+    }
+
+    std::unique_ptr<ScreenCapture> mCapture;
+    sp<SurfaceControl> mChild;
+};
+
+// Regression test b/76099859
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) {
+    SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
+
+    // Even though the parent is hidden we should still capture the child.
+
+    // Before and after reparenting, verify child is properly hidden
+    // when rendering full-screen.
+    verify([&] { screenshot()->expectBGColor(64, 64); });
+}
+
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
+    SurfaceComposerClient::Transaction()
+            .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
+            .apply(true);
+
+    // Even though the parent is cropped out we should still capture the child.
+
+    // Before and after reparenting, verify child is cropped by parent.
+    verify([&] { screenshot()->expectBGColor(65, 65); });
+}
+
+// Regression test b/124372894
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) {
+    SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true);
+
+    // We should not inherit the parent scaling.
+
+    // Before and after reparenting, verify child is properly scaled.
+    verify([&] { screenshot()->expectChildColor(80, 80); });
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
+
+    TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            .setPosition(grandchild, 5, 5)
+            .show(grandchild)
+            .apply(true);
+
+    // Captures mFGSurfaceControl, its child, and the grandchild.
+    ScreenCapture::captureLayers(&mCapture, fgHandle);
+    mCapture->expectFGColor(10, 10);
+    mCapture->expectChildColor(0, 0);
+    mCapture->checkPixel(5, 5, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureChildOnly) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    auto childHandle = child->getHandle();
+
+    SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true);
+
+    // Captures only the child layer, and not the parent.
+    ScreenCapture::captureLayers(&mCapture, childHandle);
+    mCapture->expectChildColor(0, 0);
+    mCapture->expectChildColor(9, 9);
+}
+
+TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    auto childHandle = child->getHandle();
+
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
+    TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            .setPosition(grandchild, 5, 5)
+            .show(grandchild)
+            .apply(true);
+
+    auto grandchildHandle = grandchild->getHandle();
+
+    // Captures only the grandchild.
+    ScreenCapture::captureLayers(&mCapture, grandchildHandle);
+    mCapture->checkPixel(0, 0, 50, 50, 50);
+    mCapture->checkPixel(4, 4, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureCrop) {
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+    sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
+
+    SurfaceComposerClient::Transaction()
+            .setLayer(redLayer, INT32_MAX - 1)
+            .show(redLayer)
+            .show(blueLayer)
+            .apply(true);
+
+    auto redLayerHandle = redLayer->getHandle();
+
+    // Capturing full screen should have both red and blue are visible.
+    ScreenCapture::captureLayers(&mCapture, redLayerHandle);
+    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+    // red area below the blue area
+    mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
+    // red area to the right of the blue area
+    mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
+
+    const Rect crop = Rect(0, 0, 30, 30);
+    ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop);
+    // Capturing the cropped screen, cropping out the shown red area, should leave only the blue
+    // area visible.
+    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+    mCapture->checkPixel(30, 30, 0, 0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureSize) {
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+    sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
+                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
+
+    SurfaceComposerClient::Transaction()
+            .setLayer(redLayer, INT32_MAX - 1)
+            .show(redLayer)
+            .show(blueLayer)
+            .apply(true);
+
+    auto redLayerHandle = redLayer->getHandle();
+
+    // Capturing full screen should have both red and blue are visible.
+    ScreenCapture::captureLayers(&mCapture, redLayerHandle);
+    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+    // red area below the blue area
+    mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
+    // red area to the right of the blue area
+    mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
+
+    ScreenCapture::captureLayers(&mCapture, redLayerHandle, Rect::EMPTY_RECT, 0.5);
+    // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
+    mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE);
+    // red area below the blue area
+    mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED);
+    // red area to the right of the blue area
+    mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED);
+    mCapture->checkPixel(30, 30, 0, 0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+
+    auto redLayerHandle = redLayer->getHandle();
+    Transaction().reparent(redLayer, nullptr).apply();
+    redLayer.clear();
+    SurfaceComposerClient::Transaction().apply(true);
+
+    sp<GraphicBuffer> outBuffer;
+
+    // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(redLayerHandle, &outBuffer, Rect::EMPTY_RECT, 1.0));
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
new file mode 100644
index 0000000..b49bd54
--- /dev/null
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class MirrorLayerTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        ASSERT_FALSE(display == nullptr);
+
+        mParentLayer = createColorLayer("Parent layer", Color::RED);
+        mChildLayer = createColorLayer("Child layer", Color::GREEN, mParentLayer.get());
+        asTransaction([&](Transaction& t) {
+            t.setDisplayLayerStack(display, 0);
+            t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
+            t.setCrop_legacy(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
+            t.setPosition(mChildLayer, 50, 50);
+            t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+            t.setFlags(mChildLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+        });
+    }
+
+    virtual void TearDown() {
+        LayerTransactionTest::TearDown();
+        mParentLayer = 0;
+        mChildLayer = 0;
+    }
+
+    sp<SurfaceControl> mParentLayer;
+    sp<SurfaceControl> mChildLayer;
+};
+
+TEST_F(MirrorLayerTest, MirrorColorLayer) {
+    sp<SurfaceControl> grandchild =
+            createColorLayer("Grandchild layer", Color::BLUE, mChildLayer.get());
+    Transaction()
+            .setFlags(grandchild, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque)
+            .setCrop_legacy(grandchild, Rect(0, 0, 200, 200))
+            .show(grandchild)
+            .apply();
+
+    // Mirror mChildLayer
+    sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get());
+    ASSERT_NE(mirrorLayer, nullptr);
+
+    // Add mirrorLayer as child of mParentLayer so it's shown on the display
+    Transaction()
+            .reparent(mirrorLayer, mParentLayer->getHandle())
+            .setPosition(mirrorLayer, 500, 500)
+            .show(mirrorLayer)
+            .apply();
+
+    {
+        SCOPED_TRACE("Initial Mirror");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    // Set color to white on grandchild layer.
+    Transaction().setColor(grandchild, half3{1, 1, 1}).apply();
+    {
+        SCOPED_TRACE("Updated Grandchild Layer Color");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    // Set color to black on child layer.
+    Transaction().setColor(mChildLayer, half3{0, 0, 0}).apply();
+    {
+        SCOPED_TRACE("Updated Child Layer Color");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
+    }
+
+    // Remove grandchild layer
+    Transaction().reparent(grandchild, nullptr).apply();
+    {
+        SCOPED_TRACE("Removed Grandchild Layer");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::BLACK);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
+    }
+
+    // Remove child layer
+    Transaction().reparent(mChildLayer, nullptr).apply();
+    {
+        SCOPED_TRACE("Removed Child Layer");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::RED);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::RED);
+    }
+
+    // Add grandchild layer to offscreen layer
+    Transaction().reparent(grandchild, mChildLayer->getHandle()).apply();
+    {
+        SCOPED_TRACE("Added Grandchild Layer");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::RED);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::RED);
+    }
+
+    // Add child layer
+    Transaction().reparent(mChildLayer, mParentLayer->getHandle()).apply();
+    {
+        SCOPED_TRACE("Added Child Layer");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
+    }
+}
+
+TEST_F(MirrorLayerTest, MirrorBufferLayer) {
+    sp<SurfaceControl> bufferQueueLayer =
+            createLayer("BufferQueueLayer", 200, 200, 0, mChildLayer.get());
+    fillBufferQueueLayerColor(bufferQueueLayer, Color::BLUE, 200, 200);
+    Transaction().show(bufferQueueLayer).apply();
+
+    sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get());
+    Transaction()
+            .reparent(mirrorLayer, mParentLayer->getHandle())
+            .setPosition(mirrorLayer, 500, 500)
+            .show(mirrorLayer)
+            .apply();
+
+    {
+        SCOPED_TRACE("Initial Mirror BufferQueueLayer");
+        auto shot = screenshot();
+        // Buffer mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    fillBufferQueueLayerColor(bufferQueueLayer, Color::WHITE, 200, 200);
+    {
+        SCOPED_TRACE("Update BufferQueueLayer");
+        auto shot = screenshot();
+        // Buffer mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    Transaction().reparent(bufferQueueLayer, nullptr).apply();
+    {
+        SCOPED_TRACE("Removed BufferQueueLayer");
+        auto shot = screenshot();
+        // Buffer mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    sp<SurfaceControl> bufferStateLayer =
+            createLayer("BufferStateLayer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState,
+                        mChildLayer.get());
+    fillBufferStateLayerColor(bufferStateLayer, Color::BLUE, 200, 200);
+    Transaction().setFrame(bufferStateLayer, Rect(0, 0, 200, 200)).show(bufferStateLayer).apply();
+
+    {
+        SCOPED_TRACE("Initial Mirror BufferStateLayer");
+        auto shot = screenshot();
+        // Buffer mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    fillBufferStateLayerColor(bufferStateLayer, Color::WHITE, 200, 200);
+    {
+        SCOPED_TRACE("Update BufferStateLayer");
+        auto shot = screenshot();
+        // Buffer mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    Transaction().reparent(bufferStateLayer, nullptr).apply();
+    {
+        SCOPED_TRACE("Removed BufferStateLayer");
+        auto shot = screenshot();
+        // Buffer mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
new file mode 100644
index 0000000..06e8761
--- /dev/null
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <ui/DisplayState.h>
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class MultiDisplayLayerBoundsTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+        SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState);
+        SurfaceComposerClient::getActiveDisplayConfig(mMainDisplay, &mMainDisplayConfig);
+
+        sp<IGraphicBufferConsumer> consumer;
+        BufferQueue::createBufferQueue(&mProducer, &consumer);
+        consumer->setConsumerName(String8("Virtual disp consumer"));
+        consumer->setDefaultBufferSize(mMainDisplayConfig.resolution.getWidth(),
+                                       mMainDisplayConfig.resolution.getHeight());
+    }
+
+    virtual void TearDown() {
+        SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
+        LayerTransactionTest::TearDown();
+        mColorLayer = 0;
+    }
+
+    void createDisplay(const ui::Size& layerStackSize, uint32_t layerStack) {
+        mVirtualDisplay =
+                SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
+        asTransaction([&](Transaction& t) {
+            t.setDisplaySurface(mVirtualDisplay, mProducer);
+            t.setDisplayLayerStack(mVirtualDisplay, layerStack);
+            t.setDisplayProjection(mVirtualDisplay, mMainDisplayState.orientation,
+                                   Rect(layerStackSize), Rect(mMainDisplayConfig.resolution));
+        });
+    }
+
+    void createColorLayer(uint32_t layerStack) {
+        mColorLayer =
+                createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */,
+                              PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect);
+        ASSERT_TRUE(mColorLayer != nullptr);
+        ASSERT_TRUE(mColorLayer->isValid());
+        asTransaction([&](Transaction& t) {
+            t.setLayerStack(mColorLayer, layerStack);
+            t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
+            t.setLayer(mColorLayer, INT32_MAX - 2);
+            t.setColor(mColorLayer,
+                       half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
+                             mExpectedColor.b / 255.0f});
+            t.show(mColorLayer);
+        });
+    }
+
+    ui::DisplayState mMainDisplayState;
+    DisplayConfig mMainDisplayConfig;
+    sp<IBinder> mMainDisplay;
+    sp<IBinder> mVirtualDisplay;
+    sp<IGraphicBufferProducer> mProducer;
+    sp<SurfaceControl> mColorLayer;
+    Color mExpectedColor = {63, 63, 195, 255};
+};
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
+    createDisplay(mMainDisplayState.viewport, 1 /* layerStack */);
+    createColorLayer(1 /* layerStack */);
+
+    asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+    // Verify color layer does not render on main display.
+    std::unique_ptr<ScreenCapture> sc;
+    ScreenCapture::captureScreen(&sc, mMainDisplay);
+    sc->expectColor(Rect(10, 10, 40, 50), {0, 0, 0, 255});
+    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+    // Verify color layer renders correctly on virtual display.
+    ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+    sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 255});
+}
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) {
+    // Create a display and set its layer stack to the main display's layer stack so
+    // the contents of the main display are mirrored on to the virtual display.
+
+    // Assumption here is that the new mirrored display has the same viewport as the
+    // primary display that it is mirroring.
+    createDisplay(mMainDisplayState.viewport, 0 /* layerStack */);
+    createColorLayer(0 /* layerStack */);
+
+    asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+    // Verify color layer renders correctly on main display and it is mirrored on the
+    // virtual display.
+    std::unique_ptr<ScreenCapture> sc;
+    ScreenCapture::captureScreen(&sc, mMainDisplay);
+    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+    ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
new file mode 100644
index 0000000..3e0b3c6
--- /dev/null
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class RelativeZTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        ASSERT_FALSE(display == nullptr);
+
+        // Back layer
+        mBackgroundLayer = createColorLayer("Background layer", Color::RED);
+
+        // Front layer
+        mForegroundLayer = createColorLayer("Foreground layer", Color::GREEN);
+
+        asTransaction([&](Transaction& t) {
+            t.setDisplayLayerStack(display, 0);
+            t.setLayer(mBackgroundLayer, INT32_MAX - 2).show(mBackgroundLayer);
+            t.setLayer(mForegroundLayer, INT32_MAX - 1).show(mForegroundLayer);
+        });
+    }
+
+    virtual void TearDown() {
+        LayerTransactionTest::TearDown();
+        mBackgroundLayer = 0;
+        mForegroundLayer = 0;
+    }
+
+    sp<SurfaceControl> mBackgroundLayer;
+    sp<SurfaceControl> mForegroundLayer;
+};
+
+// When a layer is reparented offscreen, remove relative z order if the relative parent
+// is still onscreen so that the layer is not drawn.
+TEST_F(RelativeZTest, LayerRemoved) {
+    std::unique_ptr<ScreenCapture> sc;
+
+    // Background layer (RED)
+    //   Child layer (WHITE) (relative to foregroud layer)
+    // Foregroud layer (GREEN)
+    sp<SurfaceControl> childLayer =
+            createColorLayer("Child layer", Color::BLUE, mBackgroundLayer.get());
+
+    Transaction{}
+            .setRelativeLayer(childLayer, mForegroundLayer->getHandle(), 1)
+            .show(childLayer)
+            .apply();
+
+    {
+        // The childLayer should be in front of the FG control.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b);
+    }
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    Transaction{}.reparent(childLayer, nullptr).apply();
+
+    // Background layer (RED)
+    //   Child layer (WHITE)
+    // Foregroud layer (GREEN)
+    Transaction{}.reparent(childLayer, mBackgroundLayer->getHandle()).apply();
+
+    {
+        // The relative z info for child layer should be reset, leaving FG control on top.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+    }
+}
+
+// When a layer is reparented offscreen, preseve relative z order if the relative parent
+// is also offscreen. Regression test b/132613412
+TEST_F(RelativeZTest, LayerRemovedOffscreenRelativeParent) {
+    std::unique_ptr<ScreenCapture> sc;
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   child level 1 (WHITE)
+    //     child level 2a (BLUE)
+    //       child level 3 (GREEN) (relative to child level 2b)
+    //     child level 2b (BLACK)
+    sp<SurfaceControl> childLevel1 =
+            createColorLayer("child level 1", Color::WHITE, mForegroundLayer.get());
+    sp<SurfaceControl> childLevel2a =
+            createColorLayer("child level 2a", Color::BLUE, childLevel1.get());
+    sp<SurfaceControl> childLevel2b =
+            createColorLayer("child level 2b", Color::BLACK, childLevel1.get());
+    sp<SurfaceControl> childLevel3 =
+            createColorLayer("child level 3", Color::GREEN, childLevel2a.get());
+
+    Transaction{}
+            .setRelativeLayer(childLevel3, childLevel2b->getHandle(), 1)
+            .show(childLevel2a)
+            .show(childLevel2b)
+            .show(childLevel3)
+            .apply();
+
+    {
+        // The childLevel3 should be in front of childLevel2b.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+    }
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    Transaction{}.reparent(childLevel1, nullptr).apply();
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   child level 1 (WHITE)
+    //     child level 2 back (BLUE)
+    //       child level 3 (GREEN) (relative to child level 2b)
+    //     child level 2 front (BLACK)
+    Transaction{}.reparent(childLevel1, mForegroundLayer->getHandle()).apply();
+
+    {
+        // Nothing should change at this point since relative z info was preserved.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+    }
+}
+
+TEST_F(RelativeZTest, LayerAndRelativeRemoved) {
+    std::unique_ptr<ScreenCapture> sc;
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   Child layer (BLUE) (relative to relativeToLayer layer)
+    //   Relative layer (WHITE)
+    sp<SurfaceControl> childLayer =
+            createColorLayer("Child layer", Color::BLUE, mForegroundLayer.get());
+    sp<SurfaceControl> relativeToLayer =
+            createColorLayer("Relative layer", Color::WHITE, mForegroundLayer.get());
+
+    Transaction{}
+            .setRelativeLayer(childLayer, relativeToLayer->getHandle(), 1)
+            .show(childLayer)
+            .show(relativeToLayer)
+            .apply();
+
+    {
+        // The childLayer should be in front of relativeToLayer.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b);
+    }
+
+    // Remove layer that childLayer is relative to
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   Child layer (BLUE) (relative to relativeToLayer layer)
+    Transaction{}.reparent(relativeToLayer, nullptr).apply();
+    relativeToLayer = 0;
+
+    {
+        // The child layer is relative to an deleted layer so it won't be drawn.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+    }
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    Transaction{}.reparent(childLayer, nullptr).apply();
+
+    {
+        // The child layer is offscreen, so it won't be drawn.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+    }
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   Child layer (BLUE)
+    Transaction{}.reparent(childLayer, mForegroundLayer->getHandle()).apply();
+
+    {
+        // The relative z info for child layer should be reset, leaving the child layer on top.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b);
+    }
+}
+
+// Preserve the relative z order when a layer is reparented to a layer that's already offscreen
+TEST_F(RelativeZTest, LayerWithRelativeReparentedToOffscreen) {
+    std::unique_ptr<ScreenCapture> sc;
+
+    Color testLayerColor = {255, 100, 0, 255};
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   child level 1a (testLayerColor) (relative to child level 2b)
+    //   child level 1b (WHITE)
+    //     child level 2a (BLUE)
+    //     child level 2b (BLACK)
+    sp<SurfaceControl> childLevel1a =
+            createColorLayer("child level 1a", testLayerColor, mForegroundLayer.get());
+    sp<SurfaceControl> childLevel1b =
+            createColorLayer("child level 1b", Color::WHITE, mForegroundLayer.get());
+    sp<SurfaceControl> childLevel2a =
+            createColorLayer("child level 2a", Color::BLUE, childLevel1b.get());
+    sp<SurfaceControl> childLevel2b =
+            createColorLayer("child level 2b", Color::BLACK, childLevel1b.get());
+
+    Transaction{}
+            .setRelativeLayer(childLevel1a, childLevel2b->getHandle(), 1)
+            .show(childLevel1a)
+            .show(childLevel1b)
+            .show(childLevel2a)
+            .show(childLevel2b)
+            .apply();
+
+    {
+        // The childLevel1a should be in front of childLevel2b.
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), testLayerColor);
+    }
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   child level 1a (testLayerColor) (relative to child level 2b)
+    Transaction{}.reparent(childLevel1b, nullptr).apply();
+
+    // // Background layer (RED)
+    // // Foregroud layer (GREEN)
+    Transaction{}.reparent(childLevel1a, childLevel2a->getHandle()).apply();
+
+    {
+        // The childLevel1a and childLevel1b are no longer on screen
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::GREEN);
+    }
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   child level 1b (WHITE)
+    //     child level 2a (BLUE)
+    //       child level 1a (testLayerColor) (relative to child level 2b)
+    //     child level 2b (BLACK)
+    Transaction{}.reparent(childLevel1b, mForegroundLayer->getHandle()).apply();
+
+    {
+        // Nothing should change at this point since relative z info was preserved.
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), testLayerColor);
+    }
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/SetFrameRate_test.cpp b/services/surfaceflinger/tests/SetFrameRate_test.cpp
new file mode 100644
index 0000000..02ba9e2
--- /dev/null
+++ b/services/surfaceflinger/tests/SetFrameRate_test.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <system/window.h>
+
+#include <thread>
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class SetFrameRateTest : public LayerTransactionTest {
+protected:
+    void TearDown() {
+        mLayer = nullptr;
+        LayerTransactionTest::TearDown();
+    }
+
+    void CreateLayer(uint32_t layerType) {
+        ASSERT_EQ(nullptr, mLayer.get());
+        mLayerType = layerType;
+        ASSERT_NO_FATAL_FAILURE(mLayer = createLayer("TestLayer", mLayerWidth, mLayerHeight,
+                                                     /*flags=*/mLayerType));
+        ASSERT_NE(nullptr, mLayer.get());
+    }
+
+    void PostBuffers(const Color& color) {
+        auto startTime = systemTime();
+        while (systemTime() - startTime < s2ns(1)) {
+            ASSERT_NO_FATAL_FAILURE(
+                    fillLayerColor(mLayerType, mLayer, color, mLayerWidth, mLayerHeight));
+            std::this_thread::sleep_for(100ms);
+        }
+    }
+
+    const int mLayerWidth = 32;
+    const int mLayerHeight = 32;
+    sp<SurfaceControl> mLayer;
+    uint32_t mLayerType;
+};
+
+TEST_F(SetFrameRateTest, BufferQueueLayerSetFrameRate) {
+    CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferQueue);
+    native_window_set_frame_rate(mLayer->getSurface().get(), 100.f,
+                                 ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT);
+    ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
+    Transaction()
+            .setFrameRate(mLayer, 200.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT)
+            .apply();
+    ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
+    native_window_set_frame_rate(mLayer->getSurface().get(), 300.f,
+                                 ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT);
+    ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
+}
+
+TEST_F(SetFrameRateTest, BufferStateLayerSetFrameRate) {
+    CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferState);
+    Transaction()
+            .setFrameRate(mLayer, 400.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT)
+            .apply();
+    ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::GREEN));
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/SetGeometry_test.cpp b/services/surfaceflinger/tests/SetGeometry_test.cpp
new file mode 100644
index 0000000..5fe2513
--- /dev/null
+++ b/services/surfaceflinger/tests/SetGeometry_test.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class SetGeometryTest : public LayerTransactionTest {
+protected:
+    void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        mLayer = createLayer("Layer", mLayerWidth, mLayerHeight);
+        fillBufferQueueLayerColor(mLayer, Color::RED, mLayerWidth, mLayerHeight);
+        asTransaction([&](Transaction& t) { t.setLayer(mLayer, INT32_MAX - 1).show(mLayer); });
+
+        {
+            SCOPED_TRACE("init");
+            ScreenCapture::captureScreen(&sc);
+            sc->expectColor(Rect(0, 0, mLayerWidth, mLayerHeight), Color::RED);
+            sc->expectBorder(Rect(0, 0, mLayerWidth, mLayerHeight), Color::BLACK);
+        }
+    }
+
+    void TearDown() {
+        LayerTransactionTest::TearDown();
+        sc = 0;
+        mLayer = 0;
+    }
+
+    std::unique_ptr<ScreenCapture> sc;
+    sp<SurfaceControl> mLayer;
+    const int mLayerWidth = 100;
+    const int mLayerHeight = 200;
+};
+
+TEST_F(SetGeometryTest, SourceAtZeroNoScale) {
+    Rect source = Rect(0, 0, 30, 30);
+    Rect dest = Rect(60, 60, 90, 90);
+    Transaction{}.setGeometry(mLayer, source, dest, 0).apply();
+
+    {
+        SCOPED_TRACE("geometry applied");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(dest, Color::RED);
+        sc->expectBorder(dest, Color::BLACK);
+    }
+}
+
+TEST_F(SetGeometryTest, SourceNotAtZero) {
+    Rect source = Rect(40, 40, 70, 70);
+    Rect dest = Rect(60, 60, 90, 90);
+    Transaction{}.setGeometry(mLayer, source, dest, 0).apply();
+
+    {
+        SCOPED_TRACE("geometry applied");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(dest, Color::RED);
+        sc->expectBorder(dest, Color::BLACK);
+    }
+}
+
+TEST_F(SetGeometryTest, Scale) {
+    Rect source = Rect(0, 0, 100, 200);
+    Rect dest = Rect(0, 0, 200, 400);
+    Transaction{}.setGeometry(mLayer, source, dest, 0).apply();
+
+    {
+        SCOPED_TRACE("Scaled by 2");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(dest, Color::RED);
+        sc->expectBorder(dest, Color::BLACK);
+    }
+
+    dest = Rect(0, 0, 50, 100);
+    Transaction{}.setGeometry(mLayer, source, dest, 0).apply();
+    {
+        SCOPED_TRACE("Scaled by .5");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(dest, Color::RED);
+        sc->expectBorder(dest, Color::BLACK);
+    }
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp
index ee857b0..e9b6ba0 100644
--- a/services/surfaceflinger/tests/Stress_test.cpp
+++ b/services/surfaceflinger/tests/Stress_test.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <gtest/gtest.h>
 
 #include <gui/SurfaceComposerClient.h>
@@ -107,3 +111,6 @@
 }
 
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index 6b4634a..cf7d570 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
 {
         "presubmit": {
-            "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*:MultiDisplayLayerBoundsTest.*:InvalidHandleTest.*:VirtualDisplayTest.*:RelativeZTest.*"
+            "filter": ""
         }
 }
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 5cc946a..8d97f27 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -14,20 +14,19 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
-
 #include <gtest/gtest.h>
-
-#include <android/native_window.h>
-
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
-
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
 
 #include <fstream>
 #include <random>
@@ -36,6 +35,9 @@
 namespace android {
 
 using Transaction = SurfaceComposerClient::Transaction;
+using SurfaceChange = surfaceflinger::SurfaceChange;
+using Trace = surfaceflinger::Trace;
+using Increment = surfaceflinger::Increment;
 
 constexpr int32_t SCALING_UPDATE = 1;
 constexpr uint32_t BUFFER_UPDATES = 18;
@@ -43,18 +45,23 @@
 constexpr uint32_t SIZE_UPDATE = 134;
 constexpr uint32_t STACK_UPDATE = 1;
 constexpr uint64_t DEFERRED_UPDATE = 0;
+constexpr int32_t RELATIVE_Z = 42;
 constexpr float ALPHA_UPDATE = 0.29f;
 constexpr float CORNER_RADIUS_UPDATE = 0.2f;
+constexpr int BACKGROUND_BLUR_RADIUS_UPDATE = 24;
 constexpr float POSITION_UPDATE = 121;
 const Rect CROP_UPDATE(16, 16, 32, 32);
+const float SHADOW_RADIUS_UPDATE = 35.0f;
 
 const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
-constexpr auto TEST_SURFACE_NAME = "BG Interceptor Test Surface";
-constexpr auto UNIQUE_TEST_SURFACE_NAME = "BG Interceptor Test Surface#0";
+constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface";
+constexpr auto TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface";
+constexpr auto UNIQUE_TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface#0";
+constexpr auto UNIQUE_TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface#0";
 constexpr auto LAYER_NAME = "Layer Create and Delete Test";
 constexpr auto UNIQUE_LAYER_NAME = "Layer Create and Delete Test#0";
 
-constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
+constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb";
 
 // Fill an RGBA_8888 formatted surface with a single color.
 static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
@@ -136,12 +143,16 @@
     void TearDown() override {
         mComposerClient->dispose();
         mBGSurfaceControl.clear();
+        mFGSurfaceControl.clear();
         mComposerClient.clear();
+        system("setenforce 1");
     }
 
     sp<SurfaceComposerClient> mComposerClient;
     sp<SurfaceControl> mBGSurfaceControl;
+    sp<SurfaceControl> mFGSurfaceControl;
     int32_t mBGLayerId;
+    int32_t mFGLayerId;
 
 public:
     using TestTransactionAction = void (SurfaceInterceptorTest::*)(Transaction&);
@@ -169,6 +180,8 @@
     bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
     bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
     bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
+    bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
+                                         bool foundBackgroundBlurRadius);
     bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
     bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
     bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
@@ -177,6 +190,11 @@
     bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag);
     bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag);
     bool deferredTransactionUpdateFound(const SurfaceChange& change, bool foundDeferred);
+    bool reparentUpdateFound(const SurfaceChange& change, bool found);
+    bool relativeParentUpdateFound(const SurfaceChange& change, bool found);
+    bool detachChildrenUpdateFound(const SurfaceChange& change, bool found);
+    bool reparentChildrenUpdateFound(const SurfaceChange& change, bool found);
+    bool shadowRadiusUpdateFound(const SurfaceChange& change, bool found);
     bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
 
     // Find all of the updates in the single trace
@@ -201,6 +219,7 @@
     void layerUpdate(Transaction&);
     void cropUpdate(Transaction&);
     void cornerRadiusUpdate(Transaction&);
+    void backgroundBlurRadiusUpdate(Transaction&);
     void matrixUpdate(Transaction&);
     void overrideScalingModeUpdate(Transaction&);
     void transparentRegionHintUpdate(Transaction&);
@@ -209,6 +228,11 @@
     void opaqueFlagUpdate(Transaction&);
     void secureFlagUpdate(Transaction&);
     void deferredTransactionUpdate(Transaction&);
+    void reparentUpdate(Transaction&);
+    void relativeParentUpdate(Transaction&);
+    void detachChildrenUpdate(Transaction&);
+    void reparentChildrenUpdate(Transaction&);
+    void shadowRadiusUpdate(Transaction&);
     void surfaceCreation(Transaction&);
     void displayCreation(Transaction&);
     void displayDeletion(Transaction&);
@@ -243,28 +267,37 @@
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     ASSERT_FALSE(display == nullptr);
 
-    DisplayInfo info;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-
-    ssize_t displayWidth = info.w;
-    ssize_t displayHeight = info.h;
+    DisplayConfig config;
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+    const ui::Size& resolution = config.resolution;
 
     // Background surface
-    mBGSurfaceControl = mComposerClient->createSurface(
-            String8(TEST_SURFACE_NAME), displayWidth, displayHeight,
-            PIXEL_FORMAT_RGBA_8888, 0);
+    mBGSurfaceControl =
+            mComposerClient->createSurface(String8(TEST_BG_SURFACE_NAME), resolution.getWidth(),
+                                           resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
     ASSERT_TRUE(mBGSurfaceControl != nullptr);
     ASSERT_TRUE(mBGSurfaceControl->isValid());
 
+    // Foreground surface
+    mFGSurfaceControl =
+            mComposerClient->createSurface(String8(TEST_FG_SURFACE_NAME), resolution.getWidth(),
+                                           resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
+    ASSERT_TRUE(mFGSurfaceControl != nullptr);
+    ASSERT_TRUE(mFGSurfaceControl->isValid());
+
     Transaction t;
     t.setDisplayLayerStack(display, 0);
-    ASSERT_EQ(NO_ERROR, t.setLayer(mBGSurfaceControl, INT_MAX-3)
-            .show(mBGSurfaceControl)
-            .apply());
+    ASSERT_EQ(NO_ERROR,
+              t.setLayer(mBGSurfaceControl, INT_MAX - 3)
+                      .show(mBGSurfaceControl)
+                      .setLayer(mFGSurfaceControl, INT_MAX - 3)
+                      .show(mFGSurfaceControl)
+                      .apply());
 }
 
 void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) {
-    mBGLayerId = getSurfaceId(trace, UNIQUE_TEST_SURFACE_NAME);
+    mBGLayerId = getSurfaceId(trace, UNIQUE_TEST_BG_SURFACE_NAME);
+    mFGLayerId = getSurfaceId(trace, UNIQUE_TEST_FG_SURFACE_NAME);
 }
 
 void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
@@ -322,6 +355,10 @@
     t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE);
 }
 
+void SurfaceInterceptorTest::backgroundBlurRadiusUpdate(Transaction& t) {
+    t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE);
+}
+
 void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
     t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
 }
@@ -364,6 +401,26 @@
                                    DEFERRED_UPDATE);
 }
 
+void SurfaceInterceptorTest::reparentUpdate(Transaction& t) {
+    t.reparent(mBGSurfaceControl, mFGSurfaceControl->getHandle());
+}
+
+void SurfaceInterceptorTest::relativeParentUpdate(Transaction& t) {
+    t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl->getHandle(), RELATIVE_Z);
+}
+
+void SurfaceInterceptorTest::detachChildrenUpdate(Transaction& t) {
+    t.detachChildren(mBGSurfaceControl);
+}
+
+void SurfaceInterceptorTest::reparentChildrenUpdate(Transaction& t) {
+    t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl->getHandle());
+}
+
+void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) {
+    t.setShadowRadius(mBGSurfaceControl, SHADOW_RADIUS_UPDATE);
+}
+
 void SurfaceInterceptorTest::displayCreation(Transaction&) {
     sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
     SurfaceComposerClient::destroyDisplay(testDisplay);
@@ -379,6 +436,7 @@
     runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
     runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
     runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
+    runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate);
     runInTransaction(&SurfaceInterceptorTest::layerUpdate);
     runInTransaction(&SurfaceInterceptorTest::cropUpdate);
     runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
@@ -389,6 +447,11 @@
     runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate);
     runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
     runInTransaction(&SurfaceInterceptorTest::deferredTransactionUpdate);
+    runInTransaction(&SurfaceInterceptorTest::reparentUpdate);
+    runInTransaction(&SurfaceInterceptorTest::reparentChildrenUpdate);
+    runInTransaction(&SurfaceInterceptorTest::detachChildrenUpdate);
+    runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate);
+    runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate);
 }
 
 void SurfaceInterceptorTest::surfaceCreation(Transaction&) {
@@ -451,6 +514,18 @@
     return foundCornerRadius;
 }
 
+bool SurfaceInterceptorTest::backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
+                                                             bool foundBackgroundBlur) {
+    bool hasBackgroundBlur(change.background_blur_radius().background_blur_radius() ==
+                           BACKGROUND_BLUR_RADIUS_UPDATE);
+    if (hasBackgroundBlur && !foundBackgroundBlur) {
+        foundBackgroundBlur = true;
+    } else if (hasBackgroundBlur && foundBackgroundBlur) {
+        []() { FAIL(); }();
+    }
+    return foundBackgroundBlur;
+}
+
 bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
     bool hasLayer(change.layer().layer() == LAYER_UPDATE);
     if (hasLayer && !foundLayer) {
@@ -569,6 +644,57 @@
     return foundDeferred;
 }
 
+bool SurfaceInterceptorTest::reparentUpdateFound(const SurfaceChange& change, bool found) {
+    bool hasId(change.reparent().parent_id() == mFGLayerId);
+    if (hasId && !found) {
+        found = true;
+    } else if (hasId && found) {
+        []() { FAIL(); }();
+    }
+    return found;
+}
+
+bool SurfaceInterceptorTest::relativeParentUpdateFound(const SurfaceChange& change, bool found) {
+    bool hasId(change.relative_parent().relative_parent_id() == mFGLayerId);
+    if (hasId && !found) {
+        found = true;
+    } else if (hasId && found) {
+        []() { FAIL(); }();
+    }
+    return found;
+}
+
+bool SurfaceInterceptorTest::detachChildrenUpdateFound(const SurfaceChange& change, bool found) {
+    bool detachChildren(change.detach_children().detach_children());
+    if (detachChildren && !found) {
+        found = true;
+    } else if (detachChildren && found) {
+        []() { FAIL(); }();
+    }
+    return found;
+}
+
+bool SurfaceInterceptorTest::reparentChildrenUpdateFound(const SurfaceChange& change, bool found) {
+    bool hasId(change.reparent_children().parent_id() == mFGLayerId);
+    if (hasId && !found) {
+        found = true;
+    } else if (hasId && found) {
+        []() { FAIL(); }();
+    }
+    return found;
+}
+
+bool SurfaceInterceptorTest::shadowRadiusUpdateFound(const SurfaceChange& change,
+                                                     bool foundShadowRadius) {
+    bool hasShadowRadius(change.shadow_radius().radius() == SHADOW_RADIUS_UPDATE);
+    if (hasShadowRadius && !foundShadowRadius) {
+        foundShadowRadius = true;
+    } else if (hasShadowRadius && foundShadowRadius) {
+        []() { FAIL(); }();
+    }
+    return foundShadowRadius;
+}
+
 bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace,
         SurfaceChange::SurfaceChangeCase changeCase) {
     bool foundUpdate = false;
@@ -596,6 +722,9 @@
                         case SurfaceChange::SurfaceChangeCase::kCornerRadius:
                             foundUpdate = cornerRadiusUpdateFound(change, foundUpdate);
                             break;
+                        case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius:
+                            foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate);
+                            break;
                         case SurfaceChange::SurfaceChangeCase::kMatrix:
                             foundUpdate = matrixUpdateFound(change, foundUpdate);
                             break;
@@ -620,6 +749,21 @@
                         case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
                             foundUpdate = deferredTransactionUpdateFound(change, foundUpdate);
                             break;
+                        case SurfaceChange::SurfaceChangeCase::kReparent:
+                            foundUpdate = reparentUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kReparentChildren:
+                            foundUpdate = reparentChildrenUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kRelativeParent:
+                            foundUpdate = relativeParentUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kDetachChildren:
+                            foundUpdate = detachChildrenUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kShadowRadius:
+                            foundUpdate = shadowRadiusUpdateFound(change, foundUpdate);
+                            break;
                         case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET:
                             break;
                     }
@@ -644,6 +788,10 @@
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOpaqueFlag));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kDeferredTransaction));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparent));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparentChildren));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kRelativeParent));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kDetachChildren));
 }
 
 bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
@@ -759,6 +907,11 @@
             SurfaceChange::SurfaceChangeCase::kCornerRadius);
 }
 
+TEST_F(SurfaceInterceptorTest, InterceptBackgroundBlurRadiusUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate,
+                SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius);
+}
+
 TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
     captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
 }
@@ -798,6 +951,31 @@
             SurfaceChange::SurfaceChangeCase::kDeferredTransaction);
 }
 
+TEST_F(SurfaceInterceptorTest, InterceptReparentUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::reparentUpdate,
+                SurfaceChange::SurfaceChangeCase::kReparent);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptReparentChildrenUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::reparentChildrenUpdate,
+                SurfaceChange::SurfaceChangeCase::kReparentChildren);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptRelativeParentUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::relativeParentUpdate,
+                SurfaceChange::SurfaceChangeCase::kRelativeParent);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptDetachChildrenUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::detachChildrenUpdate,
+                SurfaceChange::SurfaceChangeCase::kDetachChildren);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptShadowRadiusUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::shadowRadiusUpdate,
+                SurfaceChange::SurfaceChangeCase::kShadowRadius);
+}
+
 TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
     captureTest(&SurfaceInterceptorTest::runAllUpdates,
                 &SurfaceInterceptorTest::assertAllUpdatesFound);
@@ -861,5 +1039,7 @@
     ASSERT_TRUE(bufferUpdatesFound(capturedTrace));
     ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kSurfaceCreation));
 }
-
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
new file mode 100644
index 0000000..f0af363
--- /dev/null
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ANDROID_TRANSACTION_TEST_HARNESSES
+#define ANDROID_TRANSACTION_TEST_HARNESSES
+
+#include <ui/DisplayState.h>
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class LayerRenderPathTestHarness {
+public:
+    LayerRenderPathTestHarness(LayerTransactionTest* delegate, RenderPath renderPath)
+          : mDelegate(delegate), mRenderPath(renderPath) {}
+
+    std::unique_ptr<ScreenCapture> getScreenCapture() {
+        switch (mRenderPath) {
+            case RenderPath::SCREENSHOT:
+                return mDelegate->screenshot();
+            case RenderPath::VIRTUAL_DISPLAY:
+
+                const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+
+                ui::DisplayState displayState;
+                SurfaceComposerClient::getDisplayState(displayToken, &displayState);
+
+                DisplayConfig displayConfig;
+                SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+                const ui::Size& resolution = displayConfig.resolution;
+
+                sp<IBinder> vDisplay;
+                sp<IGraphicBufferProducer> producer;
+                sp<IGraphicBufferConsumer> consumer;
+                sp<BufferItemConsumer> itemConsumer;
+                BufferQueue::createBufferQueue(&producer, &consumer);
+
+                consumer->setConsumerName(String8("Virtual disp consumer"));
+                consumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
+
+                itemConsumer = new BufferItemConsumer(consumer,
+                                                      // Sample usage bits from screenrecord
+                                                      GRALLOC_USAGE_HW_VIDEO_ENCODER |
+                                                              GRALLOC_USAGE_SW_READ_OFTEN);
+
+                vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"),
+                                                                false /*secure*/);
+
+                SurfaceComposerClient::Transaction t;
+                t.setDisplaySurface(vDisplay, producer);
+                t.setDisplayLayerStack(vDisplay, 0);
+                t.setDisplayProjection(vDisplay, displayState.orientation,
+                                       Rect(displayState.viewport), Rect(resolution));
+                t.apply();
+                SurfaceComposerClient::Transaction().apply(true);
+                BufferItem item;
+                itemConsumer->acquireBuffer(&item, 0, true);
+                auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer);
+                itemConsumer->releaseBuffer(item);
+                SurfaceComposerClient::destroyDisplay(vDisplay);
+                return sc;
+        }
+    }
+
+protected:
+    LayerTransactionTest* mDelegate;
+    RenderPath mRenderPath;
+};
+
+class LayerTypeTransactionHarness : public LayerTransactionTest {
+public:
+    LayerTypeTransactionHarness(uint32_t layerType) : mLayerType(layerType) {}
+
+    sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
+                                   uint32_t flags = 0, SurfaceControl* parent = nullptr,
+                                   uint32_t* outTransformHint = nullptr,
+                                   PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
+        // if the flags already have a layer type specified, return an error
+        if (flags & ISurfaceComposerClient::eFXSurfaceMask) {
+            return nullptr;
+        }
+        return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent,
+                                                 outTransformHint, format);
+    }
+
+    void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth,
+                        int32_t bufferHeight) {
+        ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerColor(mLayerType, layer, color,
+                                                                     bufferWidth, bufferHeight));
+    }
+
+    void fillLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
+                           int32_t bufferHeight, const Color& topLeft, const Color& topRight,
+                           const Color& bottomLeft, const Color& bottomRight) {
+        ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerQuadrant(mLayerType, layer,
+                                                                        bufferWidth, bufferHeight,
+                                                                        topLeft, topRight,
+                                                                        bottomLeft, bottomRight));
+    }
+
+protected:
+    uint32_t mLayerType;
+};
+} // namespace android
+#endif
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
deleted file mode 100644
index 71016db..0000000
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ /dev/null
@@ -1,6218 +0,0 @@
-/*
- * Copyright (C) 2011 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 <chrono>
-#include <cinttypes>
-#include <functional>
-#include <limits>
-#include <ostream>
-#include <thread>
-
-#include <gtest/gtest.h>
-
-#include <android/native_window.h>
-
-#include <binder/ProcessState.h>
-#include <gui/BufferItemConsumer.h>
-#include <gui/IProducerListener.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/LayerState.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-#include <hardware/hwcomposer_defs.h>
-#include <private/android_filesystem_config.h>
-#include <private/gui/ComposerService.h>
-
-#include <ui/ColorSpace.h>
-#include <ui/DisplayInfo.h>
-#include <ui/Rect.h>
-#include <utils/String8.h>
-
-#include <math.h>
-#include <math/vec3.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "BufferGenerator.h"
-
-namespace android {
-
-namespace {
-
-struct Color {
-    uint8_t r;
-    uint8_t g;
-    uint8_t b;
-    uint8_t a;
-
-    static const Color RED;
-    static const Color GREEN;
-    static const Color BLUE;
-    static const Color WHITE;
-    static const Color BLACK;
-    static const Color TRANSPARENT;
-};
-
-const Color Color::RED{255, 0, 0, 255};
-const Color Color::GREEN{0, 255, 0, 255};
-const Color Color::BLUE{0, 0, 255, 255};
-const Color Color::WHITE{255, 255, 255, 255};
-const Color Color::BLACK{0, 0, 0, 255};
-const Color Color::TRANSPARENT{0, 0, 0, 0};
-
-using android::hardware::graphics::common::V1_1::BufferUsage;
-using namespace std::chrono_literals;
-
-std::ostream& operator<<(std::ostream& os, const Color& color) {
-    os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a);
-    return os;
-}
-
-// Fill a region with the specified color.
-void fillANativeWindowBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect,
-                                  const Color& color) {
-    Rect r(0, 0, buffer.width, buffer.height);
-    if (!r.intersect(rect, &r)) {
-        return;
-    }
-
-    int32_t width = r.right - r.left;
-    int32_t height = r.bottom - r.top;
-
-    for (int32_t row = 0; row < height; row++) {
-        uint8_t* dst =
-                static_cast<uint8_t*>(buffer.bits) + (buffer.stride * (r.top + row) + r.left) * 4;
-        for (int32_t column = 0; column < width; column++) {
-            dst[0] = color.r;
-            dst[1] = color.g;
-            dst[2] = color.b;
-            dst[3] = color.a;
-            dst += 4;
-        }
-    }
-}
-
-// Fill a region with the specified color.
-void fillGraphicBufferColor(const sp<GraphicBuffer>& buffer, const Rect& rect, const Color& color) {
-    Rect r(0, 0, buffer->width, buffer->height);
-    if (!r.intersect(rect, &r)) {
-        return;
-    }
-
-    int32_t width = r.right - r.left;
-    int32_t height = r.bottom - r.top;
-
-    uint8_t* pixels;
-    buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                 reinterpret_cast<void**>(&pixels));
-
-    for (int32_t row = 0; row < height; row++) {
-        uint8_t* dst = pixels + (buffer->getStride() * (r.top + row) + r.left) * 4;
-        for (int32_t column = 0; column < width; column++) {
-            dst[0] = color.r;
-            dst[1] = color.g;
-            dst[2] = color.b;
-            dst[3] = color.a;
-            dst += 4;
-        }
-    }
-    buffer->unlock();
-}
-
-// Check if a region has the specified color.
-void expectBufferColor(const sp<GraphicBuffer>& outBuffer, uint8_t* pixels, const Rect& rect,
-                       const Color& color, uint8_t tolerance) {
-    int32_t x = rect.left;
-    int32_t y = rect.top;
-    int32_t width = rect.right - rect.left;
-    int32_t height = rect.bottom - rect.top;
-
-    int32_t bufferWidth = int32_t(outBuffer->getWidth());
-    int32_t bufferHeight = int32_t(outBuffer->getHeight());
-    if (x + width > bufferWidth) {
-        x = std::min(x, bufferWidth);
-        width = bufferWidth - x;
-    }
-    if (y + height > bufferHeight) {
-        y = std::min(y, bufferHeight);
-        height = bufferHeight - y;
-    }
-
-    auto colorCompare = [tolerance](uint8_t a, uint8_t b) {
-        uint8_t tmp = a >= b ? a - b : b - a;
-        return tmp <= tolerance;
-    };
-    for (int32_t j = 0; j < height; j++) {
-        const uint8_t* src = pixels + (outBuffer->getStride() * (y + j) + x) * 4;
-        for (int32_t i = 0; i < width; i++) {
-            const uint8_t expected[4] = {color.r, color.g, color.b, color.a};
-            EXPECT_TRUE(std::equal(src, src + 4, expected, colorCompare))
-                    << "pixel @ (" << x + i << ", " << y + j << "): "
-                    << "expected (" << color << "), "
-                    << "got (" << Color{src[0], src[1], src[2], src[3]} << ")";
-            src += 4;
-        }
-    }
-}
-
-} // anonymous namespace
-
-using Transaction = SurfaceComposerClient::Transaction;
-
-// Fill an RGBA_8888 formatted surface with a single color.
-static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b,
-                             bool unlock = true) {
-    ANativeWindow_Buffer outBuffer;
-    sp<Surface> s = sc->getSurface();
-    ASSERT_TRUE(s != nullptr);
-    ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
-    uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
-    for (int y = 0; y < outBuffer.height; y++) {
-        for (int x = 0; x < outBuffer.width; x++) {
-            uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
-            pixel[0] = r;
-            pixel[1] = g;
-            pixel[2] = b;
-            pixel[3] = 255;
-        }
-    }
-    if (unlock) {
-        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
-    }
-}
-
-// A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check
-// individual pixel values for testing purposes.
-class ScreenCapture : public RefBase {
-public:
-    static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
-        captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
-    }
-
-    static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
-        const auto sf = ComposerService::getComposerService();
-        SurfaceComposerClient::Transaction().apply(true);
-
-        sp<GraphicBuffer> outBuffer;
-        ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false));
-        *sc = std::make_unique<ScreenCapture>(outBuffer);
-    }
-
-    static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
-                              Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
-        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-        SurfaceComposerClient::Transaction().apply(true);
-
-        sp<GraphicBuffer> outBuffer;
-        ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale));
-        *sc = std::make_unique<ScreenCapture>(outBuffer);
-    }
-
-    static void captureChildLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
-                                   Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
-        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-        SurfaceComposerClient::Transaction().apply(true);
-
-        sp<GraphicBuffer> outBuffer;
-        ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale, true));
-        *sc = std::make_unique<ScreenCapture>(outBuffer);
-    }
-
-    static void captureChildLayersExcluding(
-            std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
-            std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) {
-        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-        SurfaceComposerClient::Transaction().apply(true);
-
-        sp<GraphicBuffer> outBuffer;
-        ASSERT_EQ(NO_ERROR,
-                  sf->captureLayers(parentHandle, &outBuffer, ui::Dataspace::V0_SRGB,
-                                    ui::PixelFormat::RGBA_8888, Rect::EMPTY_RECT, excludeLayers,
-                                    1.0f, true));
-        *sc = std::make_unique<ScreenCapture>(outBuffer);
-    }
-
-    void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
-        ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
-        expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
-    }
-
-    void expectBorder(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
-        ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
-        const bool leftBorder = rect.left > 0;
-        const bool topBorder = rect.top > 0;
-        const bool rightBorder = rect.right < int32_t(mOutBuffer->getWidth());
-        const bool bottomBorder = rect.bottom < int32_t(mOutBuffer->getHeight());
-
-        if (topBorder) {
-            Rect top(rect.left, rect.top - 1, rect.right, rect.top);
-            if (leftBorder) {
-                top.left -= 1;
-            }
-            if (rightBorder) {
-                top.right += 1;
-            }
-            expectColor(top, color, tolerance);
-        }
-        if (leftBorder) {
-            Rect left(rect.left - 1, rect.top, rect.left, rect.bottom);
-            expectColor(left, color, tolerance);
-        }
-        if (rightBorder) {
-            Rect right(rect.right, rect.top, rect.right + 1, rect.bottom);
-            expectColor(right, color, tolerance);
-        }
-        if (bottomBorder) {
-            Rect bottom(rect.left, rect.bottom, rect.right, rect.bottom + 1);
-            if (leftBorder) {
-                bottom.left -= 1;
-            }
-            if (rightBorder) {
-                bottom.right += 1;
-            }
-            expectColor(bottom, color, tolerance);
-        }
-    }
-
-    void expectQuadrant(const Rect& rect, const Color& topLeft, const Color& topRight,
-                        const Color& bottomLeft, const Color& bottomRight, bool filtered = false,
-                        uint8_t tolerance = 0) {
-        ASSERT_TRUE((rect.right - rect.left) % 2 == 0 && (rect.bottom - rect.top) % 2 == 0);
-
-        const int32_t centerX = rect.left + (rect.right - rect.left) / 2;
-        const int32_t centerY = rect.top + (rect.bottom - rect.top) / 2;
-        // avoid checking borders due to unspecified filtering behavior
-        const int32_t offsetX = filtered ? 2 : 0;
-        const int32_t offsetY = filtered ? 2 : 0;
-        expectColor(Rect(rect.left, rect.top, centerX - offsetX, centerY - offsetY), topLeft,
-                    tolerance);
-        expectColor(Rect(centerX + offsetX, rect.top, rect.right, centerY - offsetY), topRight,
-                    tolerance);
-        expectColor(Rect(rect.left, centerY + offsetY, centerX - offsetX, rect.bottom), bottomLeft,
-                    tolerance);
-        expectColor(Rect(centerX + offsetX, centerY + offsetY, rect.right, rect.bottom),
-                    bottomRight, tolerance);
-    }
-
-    void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) {
-        ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
-        const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x));
-        if (r != pixel[0] || g != pixel[1] || b != pixel[2]) {
-            String8 err(String8::format("pixel @ (%3d, %3d): "
-                                        "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]",
-                                        x, y, r, g, b, pixel[0], pixel[1], pixel[2]));
-            EXPECT_EQ(String8(), err) << err.string();
-        }
-    }
-
-    void expectFGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 195, 63, 63); }
-
-    void expectBGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 63, 63, 195); }
-
-    void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); }
-
-    explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) {
-        mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
-    }
-
-    ~ScreenCapture() { mOutBuffer->unlock(); }
-
-private:
-    sp<GraphicBuffer> mOutBuffer;
-    uint8_t* mPixels = nullptr;
-};
-
-class LayerTransactionTest : public ::testing::Test {
-protected:
-    void SetUp() override {
-        mClient = new SurfaceComposerClient;
-        ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient";
-
-        ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
-
-        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-        ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
-    }
-
-    virtual void TearDown() {
-        mBlackBgSurface = 0;
-        mClient->dispose();
-        mClient = 0;
-    }
-
-    virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
-                                           const char* name, uint32_t width, uint32_t height,
-                                           uint32_t flags = 0, SurfaceControl* parent = nullptr,
-                                           PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
-        auto layer = createSurface(client, name, width, height, format, flags, parent);
-
-        Transaction t;
-        t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase);
-
-        status_t error = t.apply();
-        if (error != NO_ERROR) {
-            ADD_FAILURE() << "failed to initialize SurfaceControl";
-            layer.clear();
-        }
-
-        return layer;
-    }
-
-    virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client,
-                                             const char* name, uint32_t width, uint32_t height,
-                                             PixelFormat format, uint32_t flags,
-                                             SurfaceControl* parent = nullptr) {
-        auto layer = client->createSurface(String8(name), width, height, format, flags, parent);
-        EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl";
-        return layer;
-    }
-
-    virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
-                                           uint32_t flags = 0, SurfaceControl* parent = nullptr,
-                                           PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
-        return createLayer(mClient, name, width, height, flags, parent, format);
-    }
-
-    sp<SurfaceControl> createColorLayer(const char* name, const Color& color,
-                                        SurfaceControl* parent = nullptr) {
-        auto colorLayer = createSurface(mClient, name, 0 /* buffer width */, 0 /* buffer height */,
-                                        PIXEL_FORMAT_RGBA_8888,
-                                        ISurfaceComposerClient::eFXSurfaceColor, parent);
-        asTransaction([&](Transaction& t) {
-            t.setColor(colorLayer, half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f});
-            t.setAlpha(colorLayer, color.a / 255.0f);
-        });
-        return colorLayer;
-    }
-
-    ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
-        // wait for previous transactions (such as setSize) to complete
-        Transaction().apply(true);
-
-        ANativeWindow_Buffer buffer = {};
-        EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr));
-
-        return buffer;
-    }
-
-    void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
-        ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
-
-        // wait for the newly posted buffer to be latched
-        waitForLayerBuffers();
-    }
-
-    virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color,
-                                           int32_t bufferWidth, int32_t bufferHeight) {
-        ANativeWindow_Buffer buffer;
-        ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
-        fillANativeWindowBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color);
-        postBufferQueueLayerBuffer(layer);
-    }
-
-    virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color,
-                                           int32_t bufferWidth, int32_t bufferHeight) {
-        sp<GraphicBuffer> buffer =
-                new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
-                                  BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                          BufferUsage::COMPOSER_OVERLAY,
-                                  "test");
-        fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color);
-        Transaction().setBuffer(layer, buffer).apply();
-    }
-
-    void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color,
-                        int32_t bufferWidth, int32_t bufferHeight) {
-        switch (mLayerType) {
-            case ISurfaceComposerClient::eFXSurfaceBufferQueue:
-                fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight);
-                break;
-            case ISurfaceComposerClient::eFXSurfaceBufferState:
-                fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight);
-                break;
-            default:
-                ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
-        }
-    }
-
-    void fillLayerQuadrant(uint32_t mLayerType, const sp<SurfaceControl>& layer,
-                           int32_t bufferWidth, int32_t bufferHeight, const Color& topLeft,
-                           const Color& topRight, const Color& bottomLeft,
-                           const Color& bottomRight) {
-        switch (mLayerType) {
-            case ISurfaceComposerClient::eFXSurfaceBufferQueue:
-                fillBufferQueueLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
-                                             bottomLeft, bottomRight);
-                break;
-            case ISurfaceComposerClient::eFXSurfaceBufferState:
-                fillBufferStateLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight,
-                                             bottomLeft, bottomRight);
-                break;
-            default:
-                ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType;
-        }
-    }
-
-    virtual void fillBufferQueueLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
-                                              int32_t bufferHeight, const Color& topLeft,
-                                              const Color& topRight, const Color& bottomLeft,
-                                              const Color& bottomRight) {
-        ANativeWindow_Buffer buffer;
-        ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
-        ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
-
-        const int32_t halfW = bufferWidth / 2;
-        const int32_t halfH = bufferHeight / 2;
-        fillANativeWindowBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
-        fillANativeWindowBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH), topRight);
-        fillANativeWindowBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight), bottomLeft);
-        fillANativeWindowBufferColor(buffer, Rect(halfW, halfH, bufferWidth, bufferHeight),
-                                     bottomRight);
-
-        postBufferQueueLayerBuffer(layer);
-    }
-
-    virtual void fillBufferStateLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
-                                              int32_t bufferHeight, const Color& topLeft,
-                                              const Color& topRight, const Color& bottomLeft,
-                                              const Color& bottomRight) {
-        sp<GraphicBuffer> buffer =
-                new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1,
-                                  BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                          BufferUsage::COMPOSER_OVERLAY,
-                                  "test");
-
-        ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0);
-
-        const int32_t halfW = bufferWidth / 2;
-        const int32_t halfH = bufferHeight / 2;
-        fillGraphicBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
-        fillGraphicBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH), topRight);
-        fillGraphicBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight), bottomLeft);
-        fillGraphicBufferColor(buffer, Rect(halfW, halfH, bufferWidth, bufferHeight), bottomRight);
-
-        Transaction().setBuffer(layer, buffer).setSize(layer, bufferWidth, bufferHeight).apply();
-    }
-
-    std::unique_ptr<ScreenCapture> screenshot() {
-        std::unique_ptr<ScreenCapture> screenshot;
-        ScreenCapture::captureScreen(&screenshot);
-        return screenshot;
-    }
-
-    void asTransaction(const std::function<void(Transaction&)>& exec) {
-        Transaction t;
-        exec(t);
-        t.apply(true);
-    }
-
-    static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
-        static BufferGenerator bufferGenerator;
-        return bufferGenerator.get(outBuffer, outFence);
-    }
-
-    sp<SurfaceComposerClient> mClient;
-
-    sp<IBinder> mDisplay;
-    uint32_t mDisplayWidth;
-    uint32_t mDisplayHeight;
-    uint32_t mDisplayLayerStack;
-    Rect mDisplayRect = Rect::INVALID_RECT;
-
-    // leave room for ~256 layers
-    const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256;
-
-    sp<SurfaceControl> mBlackBgSurface;
-    bool mColorManagementUsed;
-
-private:
-    void SetUpDisplay() {
-        mDisplay = mClient->getInternalDisplayToken();
-        ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
-
-        // get display width/height
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
-        mDisplayWidth = info.w;
-        mDisplayHeight = info.h;
-        mDisplayRect =
-                Rect(static_cast<int32_t>(mDisplayWidth), static_cast<int32_t>(mDisplayHeight));
-
-        // After a new buffer is queued, SurfaceFlinger is notified and will
-        // latch the new buffer on next vsync.  Let's heuristically wait for 3
-        // vsyncs.
-        mBufferPostDelay = int32_t(1e6 / info.fps) * 3;
-
-        mDisplayLayerStack = 0;
-
-        mBlackBgSurface =
-                createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
-                              PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
-
-        // set layer stack (b/68888219)
-        Transaction t;
-        t.setDisplayLayerStack(mDisplay, mDisplayLayerStack);
-        t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight));
-        t.setLayerStack(mBlackBgSurface, mDisplayLayerStack);
-        t.setColor(mBlackBgSurface, half3{0, 0, 0});
-        t.setLayer(mBlackBgSurface, mLayerZBase);
-        t.apply();
-    }
-
-    void waitForLayerBuffers() {
-        // Request an empty transaction to get applied synchronously to ensure the buffer is
-        // latched.
-        Transaction().apply(true);
-        usleep(mBufferPostDelay);
-    }
-
-    int32_t mBufferPostDelay;
-
-    friend class LayerRenderPathTestHarness;
-};
-enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY };
-
-class LayerRenderPathTestHarness {
-public:
-    LayerRenderPathTestHarness(LayerTransactionTest* delegate, RenderPath renderPath)
-          : mDelegate(delegate), mRenderPath(renderPath) {}
-
-    std::unique_ptr<ScreenCapture> getScreenCapture() {
-        switch (mRenderPath) {
-            case RenderPath::SCREENSHOT:
-                return mDelegate->screenshot();
-            case RenderPath::VIRTUAL_DISPLAY:
-
-                const auto mainDisplay = SurfaceComposerClient::getInternalDisplayToken();
-                DisplayInfo mainDisplayInfo;
-                SurfaceComposerClient::getDisplayInfo(mainDisplay, &mainDisplayInfo);
-
-                sp<IBinder> vDisplay;
-                sp<IGraphicBufferProducer> producer;
-                sp<IGraphicBufferConsumer> consumer;
-                sp<BufferItemConsumer> itemConsumer;
-                BufferQueue::createBufferQueue(&producer, &consumer);
-
-                consumer->setConsumerName(String8("Virtual disp consumer"));
-                consumer->setDefaultBufferSize(mainDisplayInfo.w, mainDisplayInfo.h);
-
-                itemConsumer = new BufferItemConsumer(consumer,
-                                                      // Sample usage bits from screenrecord
-                                                      GRALLOC_USAGE_HW_VIDEO_ENCODER |
-                                                              GRALLOC_USAGE_SW_READ_OFTEN);
-
-                vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"),
-                                                                false /*secure*/);
-
-                SurfaceComposerClient::Transaction t;
-                t.setDisplaySurface(vDisplay, producer);
-                t.setDisplayLayerStack(vDisplay, 0);
-                t.setDisplayProjection(vDisplay, mainDisplayInfo.orientation,
-                                       Rect(mainDisplayInfo.viewportW, mainDisplayInfo.viewportH),
-                                       Rect(mainDisplayInfo.w, mainDisplayInfo.h));
-                t.apply();
-                SurfaceComposerClient::Transaction().apply(true);
-                BufferItem item;
-                itemConsumer->acquireBuffer(&item, 0, true);
-                auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer);
-                itemConsumer->releaseBuffer(item);
-                SurfaceComposerClient::destroyDisplay(vDisplay);
-                return sc;
-        }
-    }
-
-protected:
-    LayerTransactionTest* mDelegate;
-    RenderPath mRenderPath;
-};
-
-class LayerTypeTransactionHarness : public LayerTransactionTest {
-public:
-    LayerTypeTransactionHarness(uint32_t layerType) : mLayerType(layerType) {}
-
-    sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height,
-                                   uint32_t flags = 0, SurfaceControl* parent = nullptr,
-                                   PixelFormat format = PIXEL_FORMAT_RGBA_8888) {
-        // if the flags already have a layer type specified, return an error
-        if (flags & ISurfaceComposerClient::eFXSurfaceMask) {
-            return nullptr;
-        }
-        return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent,
-                                                 format);
-    }
-
-    void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth,
-                        int32_t bufferHeight) {
-        ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerColor(mLayerType, layer, color,
-                                                                     bufferWidth, bufferHeight));
-    }
-
-    void fillLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth,
-                           int32_t bufferHeight, const Color& topLeft, const Color& topRight,
-                           const Color& bottomLeft, const Color& bottomRight) {
-        ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerQuadrant(mLayerType, layer,
-                                                                        bufferWidth, bufferHeight,
-                                                                        topLeft, topRight,
-                                                                        bottomLeft, bottomRight));
-    }
-
-protected:
-    uint32_t mLayerType;
-};
-
-class LayerTypeTransactionTest : public LayerTypeTransactionHarness,
-                                 public ::testing::WithParamInterface<uint32_t> {
-public:
-    LayerTypeTransactionTest() : LayerTypeTransactionHarness(GetParam()) {}
-};
-
-class LayerTypeAndRenderTypeTransactionTest
-      : public LayerTypeTransactionHarness,
-        public ::testing::WithParamInterface<std::tuple<uint32_t, RenderPath>> {
-public:
-    LayerTypeAndRenderTypeTransactionTest()
-          : LayerTypeTransactionHarness(std::get<0>(GetParam())),
-            mRenderPathHarness(LayerRenderPathTestHarness(this, std::get<1>(GetParam()))) {}
-
-    std::unique_ptr<ScreenCapture> getScreenCapture() {
-        return mRenderPathHarness.getScreenCapture();
-    }
-
-protected:
-    LayerRenderPathTestHarness mRenderPathHarness;
-};
-
-// Environment for starting up binder threads. This is required for testing
-// virtual displays, as BufferQueue parameters may be queried over binder.
-class BinderEnvironment : public ::testing::Environment {
-public:
-    void SetUp() override { ProcessState::self()->startThreadPool(); }
-};
-
-::testing::Environment* const binderEnv =
-        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
-
-class LayerRenderTypeTransactionTest : public LayerTransactionTest,
-                                       public ::testing::WithParamInterface<RenderPath> {
-public:
-    LayerRenderTypeTransactionTest() : mHarness(LayerRenderPathTestHarness(this, GetParam())) {}
-
-    std::unique_ptr<ScreenCapture> getScreenCapture() { return mHarness.getScreenCapture(); }
-    void setRelativeZBasicHelper(uint32_t layerType);
-    void setRelativeZGroupHelper(uint32_t layerType);
-    void setAlphaBasicHelper(uint32_t layerType);
-    void setBackgroundColorHelper(uint32_t layerType, bool priorColor, bool bufferFill, float alpha,
-                                  Color finalColor);
-
-protected:
-    LayerRenderPathTestHarness mHarness;
-};
-
-INSTANTIATE_TEST_CASE_P(
-        LayerTypeAndRenderTypeTransactionTests, LayerTypeAndRenderTypeTransactionTest,
-        ::testing::Combine(
-                ::testing::Values(
-                        static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
-                        static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)),
-                ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT)));
-
-INSTANTIATE_TEST_CASE_P(LayerRenderTypeTransactionTests, LayerRenderTypeTransactionTest,
-                        ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT));
-
-INSTANTIATE_TEST_CASE_P(
-        LayerTypeTransactionTests, LayerTypeTransactionTest,
-        ::testing::Values(static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
-                          static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)));
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionBasic_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    {
-        SCOPED_TRACE("default position");
-        const Rect rect(0, 0, 32, 32);
-        auto shot = getScreenCapture();
-        shot->expectColor(rect, Color::RED);
-        shot->expectBorder(rect, Color::BLACK);
-    }
-
-    Transaction().setPosition(layer, 5, 10).apply();
-    {
-        SCOPED_TRACE("new position");
-        const Rect rect(5, 10, 37, 42);
-        auto shot = getScreenCapture();
-        shot->expectColor(rect, Color::RED);
-        shot->expectBorder(rect, Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionRounding_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // GLES requires only 4 bits of subpixel precision during rasterization
-    // XXX GLES composition does not match HWC composition due to precision
-    // loss (b/69315223)
-    const float epsilon = 1.0f / 16.0f;
-    Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply();
-    {
-        SCOPED_TRACE("rounding down");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setPosition(layer, 0.5f + epsilon, 0.5f + epsilon).apply();
-    {
-        SCOPED_TRACE("rounding up");
-        getScreenCapture()->expectColor(Rect(1, 1, 33, 33), Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionOutOfBounds_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    Transaction().setPosition(layer, -32, -32).apply();
-    {
-        SCOPED_TRACE("negative coordinates");
-        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
-    }
-
-    Transaction().setPosition(layer, mDisplayWidth, mDisplayHeight).apply();
-    {
-        SCOPED_TRACE("positive coordinates");
-        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // partially out of bounds
-    Transaction().setPosition(layer, -30, -30).apply();
-    {
-        SCOPED_TRACE("negative coordinates");
-        getScreenCapture()->expectColor(Rect(0, 0, 2, 2), Color::RED);
-    }
-
-    Transaction().setPosition(layer, mDisplayWidth - 2, mDisplayHeight - 2).apply();
-    {
-        SCOPED_TRACE("positive coordinates");
-        getScreenCapture()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
-                                             mDisplayHeight),
-                                        Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionWithResize_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // setPosition is applied immediately by default, with or without resize
-    // pending
-    Transaction().setPosition(layer, 5, 10).setSize(layer, 64, 64).apply();
-    {
-        SCOPED_TRACE("resize pending");
-        auto shot = getScreenCapture();
-        const Rect rect(5, 10, 37, 42);
-        shot->expectColor(rect, Color::RED);
-        shot->expectBorder(rect, Color::BLACK);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
-    {
-        SCOPED_TRACE("resize applied");
-        getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResize_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // request setPosition to be applied with the next resize
-    Transaction().setPosition(layer, 5, 10).setGeometryAppliesWithResize(layer).apply();
-    {
-        SCOPED_TRACE("new position pending");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setPosition(layer, 15, 20).apply();
-    {
-        SCOPED_TRACE("pending new position modified");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setSize(layer, 64, 64).apply();
-    {
-        SCOPED_TRACE("resize pending");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    // finally resize and latch the buffer
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
-    {
-        SCOPED_TRACE("new position applied");
-        getScreenCapture()->expectColor(Rect(15, 20, 79, 84), Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResizeScaleToWindow_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // setPosition is not immediate even with SCALE_TO_WINDOW override
-    Transaction()
-            .setPosition(layer, 5, 10)
-            .setSize(layer, 64, 64)
-            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
-            .setGeometryAppliesWithResize(layer)
-            .apply();
-    {
-        SCOPED_TRACE("new position pending");
-        getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
-    {
-        SCOPED_TRACE("new position applied");
-        getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    Transaction().setSize(layer, 64, 64).apply();
-    {
-        SCOPED_TRACE("resize pending");
-        auto shot = getScreenCapture();
-        const Rect rect(0, 0, 32, 32);
-        shot->expectColor(rect, Color::RED);
-        shot->expectBorder(rect, Color::BLACK);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
-    {
-        SCOPED_TRACE("resize applied");
-        auto shot = getScreenCapture();
-        const Rect rect(0, 0, 64, 64);
-        shot->expectColor(rect, Color::RED);
-        shot->expectBorder(rect, Color::BLACK);
-    }
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetSizeInvalid) {
-    // cannot test robustness against invalid sizes (zero or really huge)
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // setSize is immediate with SCALE_TO_WINDOW, unlike setPosition
-    Transaction()
-            .setSize(layer, 64, 64)
-            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
-            .apply();
-    getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZBasic) {
-    sp<SurfaceControl> layerR;
-    sp<SurfaceControl> layerG;
-    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
-
-    Transaction().setLayer(layerR, mLayerZBase + 1).apply();
-    {
-        SCOPED_TRACE("layerR");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setLayer(layerG, mLayerZBase + 2).apply();
-    {
-        SCOPED_TRACE("layerG");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
-    }
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZNegative) {
-    sp<SurfaceControl> parent =
-            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
-                                              ISurfaceComposerClient::eFXSurfaceContainer);
-    Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
-    sp<SurfaceControl> layerR;
-    sp<SurfaceControl> layerG;
-    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
-
-    Transaction()
-            .reparent(layerR, parent->getHandle())
-            .reparent(layerG, parent->getHandle())
-            .apply();
-    Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply();
-    {
-        SCOPED_TRACE("layerR");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setLayer(layerR, -3).apply();
-    {
-        SCOPED_TRACE("layerG");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
-    }
-}
-
-void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
-    sp<SurfaceControl> layerR;
-    sp<SurfaceControl> layerG;
-    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32, layerType));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32, layerType));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32));
-
-    switch (layerType) {
-        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
-            Transaction()
-                    .setPosition(layerG, 16, 16)
-                    .setRelativeLayer(layerG, layerR->getHandle(), 1)
-                    .apply();
-            break;
-        case ISurfaceComposerClient::eFXSurfaceBufferState:
-            Transaction()
-                    .setFrame(layerR, Rect(0, 0, 32, 32))
-                    .setFrame(layerG, Rect(16, 16, 48, 48))
-                    .setRelativeLayer(layerG, layerR->getHandle(), 1)
-                    .apply();
-            break;
-        default:
-            ASSERT_FALSE(true) << "Unsupported layer type";
-    }
-    {
-        SCOPED_TRACE("layerG above");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
-        shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN);
-    }
-
-    Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply();
-    {
-        SCOPED_TRACE("layerG below");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
-        shot->expectColor(Rect(32, 32, 48, 48), Color::GREEN);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferQueue) {
-    ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferState) {
-    ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
-}
-
-TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) {
-    sp<SurfaceControl> parent =
-            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
-                                              ISurfaceComposerClient::eFXSurfaceContainer);
-    Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply();
-    sp<SurfaceControl> layerR;
-    sp<SurfaceControl> layerG;
-    sp<SurfaceControl> layerB;
-    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32));
-
-    Transaction()
-            .reparent(layerB, parent->getHandle())
-            .apply();
-
-    // layerR = mLayerZBase, layerG = layerR - 1, layerB = -2
-    Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply();
-
-    std::unique_ptr<ScreenCapture> screenshot;
-    // only layerB is in this range
-    sp<IBinder> parentHandle = parent->getHandle();
-    ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32));
-    screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
-}
-
-TEST_P(LayerTypeTransactionTest, SetLayerAndRelative) {
-    sp<SurfaceControl> parent =
-            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
-                                              ISurfaceComposerClient::eFXSurfaceColor);
-
-    sp<SurfaceControl> childLayer;
-    ASSERT_NO_FATAL_FAILURE(
-            childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
-                                                           0 /* buffer height */,
-                                                           ISurfaceComposerClient::eFXSurfaceColor,
-                                                           parent.get()));
-    Transaction()
-            .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
-            .setColor(parent, half3{0.0f, 0.0f, 0.0f})
-            .show(childLayer)
-            .show(parent)
-            .setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
-            .setCrop_legacy(childLayer, Rect(0, 0, 20, 30))
-            .apply();
-
-    Transaction()
-            .setRelativeLayer(childLayer, parent->getHandle(), -1)
-            .setLayer(childLayer, 1)
-            .apply();
-
-    {
-        SCOPED_TRACE("setLayer above");
-        // Set layer should get applied and place the child above.
-        std::unique_ptr<ScreenCapture> screenshot;
-        ScreenCapture::captureScreen(&screenshot);
-        screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
-    }
-
-    Transaction()
-            .setLayer(childLayer, 1)
-            .setRelativeLayer(childLayer, parent->getHandle(), -1)
-            .apply();
-
-    {
-        SCOPED_TRACE("setRelative below");
-        // Set relative layer should get applied and place the child below.
-        std::unique_ptr<ScreenCapture> screenshot;
-        ScreenCapture::captureScreen(&screenshot);
-        screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
-    }
-}
-
-TEST_P(LayerTypeTransactionTest, HideRelativeParentHidesLayer) {
-    sp<SurfaceControl> parent =
-            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
-                                              ISurfaceComposerClient::eFXSurfaceColor);
-    sp<SurfaceControl> relativeParent =
-            LayerTransactionTest::createLayer("RelativeParent", 0 /* buffer width */,
-                    0 /* buffer height */, ISurfaceComposerClient::eFXSurfaceColor);
-
-    sp<SurfaceControl> childLayer;
-    ASSERT_NO_FATAL_FAILURE(
-            childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
-                                                           0 /* buffer height */,
-                                                           ISurfaceComposerClient::eFXSurfaceColor,
-                                                           parent.get()));
-    Transaction()
-            .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
-            .setColor(parent, half3{0.0f, 0.0f, 0.0f})
-            .setColor(relativeParent, half3{0.0f, 1.0f, 0.0f})
-            .show(childLayer)
-            .show(parent)
-            .show(relativeParent)
-            .setLayer(parent, mLayerZBase - 1)
-            .setLayer(relativeParent, mLayerZBase)
-            .apply();
-
-    Transaction()
-            .setRelativeLayer(childLayer, relativeParent->getHandle(), 1)
-            .apply();
-
-    {
-        SCOPED_TRACE("setLayer above");
-        // Set layer should get applied and place the child above.
-        std::unique_ptr<ScreenCapture> screenshot;
-        ScreenCapture::captureScreen(&screenshot);
-        screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
-    }
-
-    Transaction()
-        .hide(relativeParent)
-        .apply();
-
-    {
-        SCOPED_TRACE("hide relative parent");
-        // The relative should no longer be visible.
-        std::unique_ptr<ScreenCapture> screenshot;
-        ScreenCapture::captureScreen(&screenshot);
-        screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
-    }
-}
-
-void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) {
-    sp<SurfaceControl> layerR;
-    sp<SurfaceControl> layerG;
-    sp<SurfaceControl> layerB;
-    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test", 32, 32, layerType));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test", 32, 32, layerType));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test", 32, 32, layerType));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerB, Color::BLUE, 32, 32));
-
-    // layerR = 0, layerG = layerR + 3, layerB = 2
-    switch (layerType) {
-        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
-            Transaction()
-                    .setPosition(layerG, 8, 8)
-                    .setRelativeLayer(layerG, layerR->getHandle(), 3)
-                    .setPosition(layerB, 16, 16)
-                    .setLayer(layerB, mLayerZBase + 2)
-                    .apply();
-            break;
-        case ISurfaceComposerClient::eFXSurfaceBufferState:
-            Transaction()
-                    .setFrame(layerR, Rect(0, 0, 32, 32))
-                    .setFrame(layerG, Rect(8, 8, 40, 40))
-                    .setRelativeLayer(layerG, layerR->getHandle(), 3)
-                    .setFrame(layerB, Rect(16, 16, 48, 48))
-                    .setLayer(layerB, mLayerZBase + 2)
-                    .apply();
-            break;
-        default:
-            ASSERT_FALSE(true) << "Unsupported layer type";
-    }
-
-    {
-        SCOPED_TRACE("(layerR < layerG) < layerB");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
-        shot->expectColor(Rect(8, 8, 16, 16), Color::GREEN);
-        shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
-    }
-
-    // layerR = 4, layerG = layerR + 3, layerB = 2
-    Transaction().setLayer(layerR, mLayerZBase + 4).apply();
-    {
-        SCOPED_TRACE("layerB < (layerR < layerG)");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 8, 8), Color::RED);
-        shot->expectColor(Rect(8, 8, 40, 40), Color::GREEN);
-        shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
-    }
-
-    // layerR = 4, layerG = layerR - 3, layerB = 2
-    Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply();
-    {
-        SCOPED_TRACE("layerB < (layerG < layerR)");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
-        shot->expectColor(Rect(32, 32, 40, 40), Color::GREEN);
-        shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE);
-    }
-
-    // restore to absolute z
-    // layerR = 4, layerG = 0, layerB = 2
-    Transaction().setLayer(layerG, mLayerZBase).apply();
-    {
-        SCOPED_TRACE("layerG < layerB < layerR");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
-        shot->expectColor(Rect(32, 32, 48, 48), Color::BLUE);
-    }
-
-    // layerR should not affect layerG anymore
-    // layerR = 1, layerG = 0, layerB = 2
-    Transaction().setLayer(layerR, mLayerZBase + 1).apply();
-    {
-        SCOPED_TRACE("layerG < layerR < layerB");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
-        shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferQueue) {
-    ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferState) {
-    ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetRelativeZBug64572777) {
-    sp<SurfaceControl> layerR;
-    sp<SurfaceControl> layerG;
-
-    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
-
-    Transaction()
-            .setPosition(layerG, 16, 16)
-            .setRelativeLayer(layerG, layerR->getHandle(), 1)
-            .apply();
-
-    layerG.clear();
-    // layerG should have been removed
-    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsHidden) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
-
-    Transaction().setFlags(layer, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden).apply();
-    {
-        SCOPED_TRACE("layer hidden");
-        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
-    }
-
-    Transaction().setFlags(layer, 0, layer_state_t::eLayerHidden).apply();
-    {
-        SCOPED_TRACE("layer shown");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsOpaque) {
-    const Color translucentRed = {100, 0, 0, 100};
-    sp<SurfaceControl> layerR;
-    sp<SurfaceControl> layerG;
-    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, translucentRed, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
-
-    Transaction()
-            .setLayer(layerR, mLayerZBase + 1)
-            .setFlags(layerR, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque)
-            .apply();
-    {
-        SCOPED_TRACE("layerR opaque");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255});
-    }
-
-    Transaction().setFlags(layerR, 0, layer_state_t::eLayerOpaque).apply();
-    {
-        SCOPED_TRACE("layerR translucent");
-        const uint8_t g = uint8_t(255 - translucentRed.a);
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
-    }
-}
-
-TEST_P(LayerTypeTransactionTest, SetFlagsSecure) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
-
-    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
-    sp<GraphicBuffer> outBuffer;
-    Transaction()
-            .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
-            .apply(true);
-    ASSERT_EQ(PERMISSION_DENIED,
-              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
-    Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true);
-    ASSERT_EQ(NO_ERROR,
-              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-}
-
-/** RAII Wrapper around get/seteuid */
-class UIDFaker {
-    uid_t oldId;
-public:
-    UIDFaker(uid_t uid) {
-        oldId = geteuid();
-        seteuid(uid);
-    }
-    ~UIDFaker() {
-        seteuid(oldId);
-    }
-};
-
-TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
-    sp<GraphicBuffer> outBuffer;
-    Transaction()
-            .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
-            .apply(true);
-    ASSERT_EQ(PERMISSION_DENIED,
-              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
-    UIDFaker f(AID_SYSTEM);
-
-    // By default the system can capture screenshots with secure layers but they
-    // will be blacked out
-    ASSERT_EQ(NO_ERROR,
-              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
-    {
-        SCOPED_TRACE("as system");
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-
-    // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
-    // to receive them...we are expected to take care with the results.
-    bool outCapturedSecureLayers;
-    ASSERT_EQ(NO_ERROR,
-              composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers,
-                                      ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0,
-                                      0, false, ISurfaceComposer::eRotateNone, true));
-    ASSERT_EQ(true, outCapturedSecureLayers);
-    ScreenCapture sc(outBuffer);
-    sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferQueue) {
-    const Rect top(0, 0, 32, 16);
-    const Rect bottom(0, 16, 32, 32);
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-
-    ANativeWindow_Buffer buffer;
-    ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
-    ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, top, Color::TRANSPARENT));
-    ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, bottom, Color::RED));
-    // setTransparentRegionHint always applies to the following buffer
-    Transaction().setTransparentRegionHint(layer, Region(top)).apply();
-    ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
-    {
-        SCOPED_TRACE("top transparent");
-        auto shot = getScreenCapture();
-        shot->expectColor(top, Color::BLACK);
-        shot->expectColor(bottom, Color::RED);
-    }
-
-    Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
-    {
-        SCOPED_TRACE("transparent region hint pending");
-        auto shot = getScreenCapture();
-        shot->expectColor(top, Color::BLACK);
-        shot->expectColor(bottom, Color::RED);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
-    ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, top, Color::RED));
-    ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, bottom, Color::TRANSPARENT));
-    ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
-    {
-        SCOPED_TRACE("bottom transparent");
-        auto shot = getScreenCapture();
-        shot->expectColor(top, Color::RED);
-        shot->expectColor(bottom, Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferState) {
-    const Rect top(0, 0, 32, 16);
-    const Rect bottom(0, 16, 32, 32);
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
-
-    ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, top, Color::TRANSPARENT));
-    ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, bottom, Color::RED));
-    Transaction()
-            .setTransparentRegionHint(layer, Region(top))
-            .setBuffer(layer, buffer)
-            .setFrame(layer, Rect(0, 0, 32, 32))
-            .apply();
-    {
-        SCOPED_TRACE("top transparent");
-        auto shot = getScreenCapture();
-        shot->expectColor(top, Color::BLACK);
-        shot->expectColor(bottom, Color::RED);
-    }
-
-    Transaction().setTransparentRegionHint(layer, Region(bottom)).apply();
-    {
-        SCOPED_TRACE("transparent region hint intermediate");
-        auto shot = getScreenCapture();
-        shot->expectColor(top, Color::BLACK);
-        shot->expectColor(bottom, Color::BLACK);
-    }
-
-    buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                               BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                       BufferUsage::COMPOSER_OVERLAY,
-                               "test");
-
-    ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, top, Color::RED));
-    ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, bottom, Color::TRANSPARENT));
-    Transaction().setBuffer(layer, buffer).apply();
-    {
-        SCOPED_TRACE("bottom transparent");
-        auto shot = getScreenCapture();
-        shot->expectColor(top, Color::RED);
-        shot->expectColor(bottom, Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferQueue) {
-    sp<SurfaceControl> layerTransparent;
-    sp<SurfaceControl> layerR;
-    ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-
-    // check that transparent region hint is bound by the layer size
-    Transaction()
-            .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
-            .setPosition(layerR, 16, 16)
-            .setLayer(layerR, mLayerZBase + 1)
-            .apply();
-    ASSERT_NO_FATAL_FAILURE(
-            fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerR, Color::RED, 32, 32));
-    getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferState) {
-    sp<SurfaceControl> layerTransparent;
-    sp<SurfaceControl> layerR;
-    ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(
-            layerR = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    // check that transparent region hint is bound by the layer size
-    Transaction()
-            .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
-            .setFrame(layerR, Rect(16, 16, 48, 48))
-            .setLayer(layerR, mLayerZBase + 1)
-            .apply();
-    ASSERT_NO_FATAL_FAILURE(
-            fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32));
-    getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED);
-}
-
-void LayerRenderTypeTransactionTest::setAlphaBasicHelper(uint32_t layerType) {
-    sp<SurfaceControl> layer1;
-    sp<SurfaceControl> layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer("test 1", 32, 32, layerType));
-    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer("test 2", 32, 32, layerType));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer1, {64, 0, 0, 255}, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer2, {0, 64, 0, 255}, 32, 32));
-
-    switch (layerType) {
-        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
-            Transaction()
-                    .setAlpha(layer1, 0.25f)
-                    .setAlpha(layer2, 0.75f)
-                    .setPosition(layer2, 16, 0)
-                    .setLayer(layer2, mLayerZBase + 1)
-                    .apply();
-            break;
-        case ISurfaceComposerClient::eFXSurfaceBufferState:
-            Transaction()
-                    .setAlpha(layer1, 0.25f)
-                    .setAlpha(layer2, 0.75f)
-                    .setFrame(layer1, Rect(0, 0, 32, 32))
-                    .setFrame(layer2, Rect(16, 0, 48, 32))
-                    .setLayer(layer2, mLayerZBase + 1)
-                    .apply();
-            break;
-        default:
-            ASSERT_FALSE(true) << "Unsupported layer type";
-    }
-    {
-        auto shot = getScreenCapture();
-        uint8_t r = 16; // 64 * 0.25f
-        uint8_t g = 48; // 64 * 0.75f
-        shot->expectColor(Rect(0, 0, 16, 32), {r, 0, 0, 255});
-        shot->expectColor(Rect(32, 0, 48, 32), {0, g, 0, 255});
-
-        r /= 4; // r * (1.0f - 0.75f)
-        shot->expectColor(Rect(16, 0, 32, 32), {r, g, 0, 255});
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferQueue) {
-    ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferState) {
-    ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetAlphaClamped) {
-    const Color color = {64, 0, 0, 255};
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, color, 32, 32));
-
-    Transaction().setAlpha(layer, 2.0f).apply();
-    {
-        SCOPED_TRACE("clamped to 1.0f");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), color);
-    }
-
-    Transaction().setAlpha(layer, -1.0f).apply();
-    {
-        SCOPED_TRACE("clamped to 0.0f");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadius) {
-    sp<SurfaceControl> layer;
-    const uint8_t size = 64;
-    const uint8_t testArea = 4;
-    const float cornerRadius = 20.0f;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
-
-    Transaction()
-            .setCornerRadius(layer, cornerRadius)
-            .apply();
-    {
-        const uint8_t bottom = size - 1;
-        const uint8_t right = size - 1;
-        auto shot = getScreenCapture();
-        // Transparent corners
-        shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK);
-        shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK);
-        shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
-        shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::BLACK);
-    }
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildCrop) {
-    sp<SurfaceControl> parent;
-    sp<SurfaceControl> child;
-    const uint8_t size = 64;
-    const uint8_t testArea = 4;
-    const float cornerRadius = 20.0f;
-    ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, size, size));
-    ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size / 2));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size / 2));
-
-    Transaction()
-            .setCornerRadius(parent, cornerRadius)
-            .reparent(child, parent->getHandle())
-            .setPosition(child, 0, size / 2)
-            .apply();
-    {
-        const uint8_t bottom = size - 1;
-        const uint8_t right = size - 1;
-        auto shot = getScreenCapture();
-        // Top edge of child should not have rounded corners because it's translated in the parent
-        shot->expectColor(Rect(0, size / 2, right, static_cast<int>(bottom - cornerRadius)),
-            Color::GREEN);
-        // But bottom edges should have been clipped according to parent bounds
-        shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK);
-        shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorBasic) {
-    sp<SurfaceControl> bufferLayer;
-    sp<SurfaceControl> colorLayer;
-    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(colorLayer =
-                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
-                                                ISurfaceComposerClient::eFXSurfaceColor));
-
-    Transaction()
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
-            .setLayer(colorLayer, mLayerZBase + 1)
-            .apply();
-
-    {
-        SCOPED_TRACE("default color");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-
-    const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
-    const Color expected = {15, 51, 85, 255};
-    // this is handwavy, but the precison loss scaled by 255 (8-bit per
-    // channel) should be less than one
-    const uint8_t tolerance = 1;
-    Transaction().setColor(colorLayer, color).apply();
-    {
-        SCOPED_TRACE("new color");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
-    }
-}
-
-// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill
-// BLUE: prior background color
-// GREEN: final background color
-// BLACK: no color or fill
-void LayerRenderTypeTransactionTest::setBackgroundColorHelper(uint32_t layerType, bool priorColor,
-                                                              bool bufferFill, float alpha,
-                                                              Color finalColor) {
-    sp<SurfaceControl> layer;
-    int32_t width = 500;
-    int32_t height = 500;
-
-    Color fillColor = Color::RED;
-    Color priorBgColor = Color::BLUE;
-    Color expectedColor = Color::BLACK;
-    switch (layerType) {
-        case ISurfaceComposerClient::eFXSurfaceColor:
-            ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType));
-            Transaction()
-                    .setCrop_legacy(layer, Rect(0, 0, width, height))
-                    .setColor(layer, half3(1.0f, 0, 0))
-                    .apply();
-            expectedColor = fillColor;
-            break;
-        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
-            ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
-            if (bufferFill) {
-                ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height));
-                expectedColor = fillColor;
-            }
-            Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply();
-            break;
-        case ISurfaceComposerClient::eFXSurfaceBufferState:
-            ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
-            if (bufferFill) {
-                ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
-                expectedColor = fillColor;
-            }
-            Transaction().setFrame(layer, Rect(0, 0, width, height)).apply();
-            break;
-        default:
-            GTEST_FAIL() << "Unknown layer type in setBackgroundColorHelper";
-            return;
-    }
-
-    if (priorColor && layerType != ISurfaceComposerClient::eFXSurfaceColor) {
-        Transaction()
-                .setBackgroundColor(layer, half3(0, 0, 1.0f), 1.0f, ui::Dataspace::UNKNOWN)
-                .apply();
-        if (!bufferFill) {
-            expectedColor = priorBgColor;
-        }
-    }
-
-    {
-        SCOPED_TRACE("default before setting background color layer");
-        screenshot()->expectColor(Rect(0, 0, width, height), expectedColor);
-    }
-    Transaction()
-            .setBackgroundColor(layer, half3(0, 1.0f, 0), alpha, ui::Dataspace::UNKNOWN)
-            .apply();
-
-    {
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, width, height), finalColor);
-        shot->expectBorder(Rect(0, 0, width, height), Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_Color_NoEffect) {
-    bool priorColor = false;
-    bool bufferFill = false;
-    float alpha = 1.0f;
-    Color finalColor = Color::RED;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceColor,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferQueue_BufferFill_NoPriorColor_Basic) {
-    bool priorColor = false;
-    bool bufferFill = true;
-    float alpha = 1.0f;
-    Color finalColor = Color::RED;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferQueue_NoBufferFill_NoPriorColor_Basic) {
-    bool priorColor = false;
-    bool bufferFill = false;
-    float alpha = 1.0f;
-    Color finalColor = Color::GREEN;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferQueue_BufferFill_PriorColor_Basic) {
-    bool priorColor = true;
-    bool bufferFill = true;
-    float alpha = 1.0f;
-    Color finalColor = Color::RED;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferQueue_NoBufferFill_PriorColor_Basic) {
-    bool priorColor = true;
-    bool bufferFill = false;
-    float alpha = 1.0f;
-    Color finalColor = Color::GREEN;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferQueue_NoPriorColor_ZeroAlpha_NoEffect) {
-    bool priorColor = false;
-    bool bufferFill = false;
-    float alpha = 0;
-    Color finalColor = Color::BLACK;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferQueue_PriorColor_ZeroAlpha_DeleteBackground) {
-    bool priorColor = true;
-    bool bufferFill = false;
-    float alpha = 0;
-    Color finalColor = Color::BLACK;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferState_BufferFill_NoPriorColor_Basic) {
-    bool priorColor = false;
-    bool bufferFill = true;
-    float alpha = 1.0f;
-    Color finalColor = Color::RED;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferState_NoBufferFill_NoPriorColor_Basic) {
-    bool priorColor = false;
-    bool bufferFill = false;
-    float alpha = 1.0f;
-    Color finalColor = Color::GREEN;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferState_NoBufferFill_PriorColor_Basic) {
-    bool priorColor = true;
-    bool bufferFill = false;
-    float alpha = 1.0f;
-    Color finalColor = Color::GREEN;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferState_NoPriorColor_ZeroAlpha_NoEffect) {
-    bool priorColor = false;
-    bool bufferFill = false;
-    float alpha = 0;
-    Color finalColor = Color::BLACK;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferState_PriorColor_ZeroAlpha_DeleteBackground) {
-    bool priorColor = true;
-    bool bufferFill = false;
-    float alpha = 0;
-    Color finalColor = Color::BLACK;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorClamped) {
-    sp<SurfaceControl> colorLayer;
-    ASSERT_NO_FATAL_FAILURE(colorLayer =
-                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
-                                                ISurfaceComposerClient::eFXSurfaceColor));
-    Transaction()
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
-            .setColor(colorLayer, half3(2.0f, -1.0f, 0.0f))
-            .apply();
-
-    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorWithAlpha) {
-    sp<SurfaceControl> bufferLayer;
-    sp<SurfaceControl> colorLayer;
-    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(colorLayer =
-                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
-                                                ISurfaceComposerClient::eFXSurfaceColor));
-    Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
-
-    const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
-    const float alpha = 0.25f;
-    const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
-    // this is handwavy, but the precison loss scaled by 255 (8-bit per
-    // channel) should be less than one
-    const uint8_t tolerance = 1;
-    Transaction()
-            .setColor(colorLayer, color)
-            .setAlpha(colorLayer, alpha)
-            .setLayer(colorLayer, mLayerZBase + 1)
-            .apply();
-    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
-                                    tolerance);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorWithParentAlpha_Bug74220420) {
-    sp<SurfaceControl> bufferLayer;
-    sp<SurfaceControl> parentLayer;
-    sp<SurfaceControl> colorLayer;
-    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parentWithAlpha", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */,
-                                                     0 /* buffer height */,
-                                                     ISurfaceComposerClient::eFXSurfaceColor));
-    Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply();
-    const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
-    const float alpha = 0.25f;
-    const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
-    // this is handwavy, but the precision loss scaled by 255 (8-bit per
-    // channel) should be less than one
-    const uint8_t tolerance = 1;
-    Transaction()
-            .reparent(colorLayer, parentLayer->getHandle())
-            .setColor(colorLayer, color)
-            .setAlpha(parentLayer, alpha)
-            .setLayer(parentLayer, mLayerZBase + 1)
-            .apply();
-    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
-                                    tolerance);
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
-    sp<SurfaceControl> bufferLayer;
-    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED, 32, 32));
-
-    // color is ignored
-    Transaction().setColor(bufferLayer, half3(0.0f, 1.0f, 0.0f)).apply();
-    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetLayerStackBasic) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
-
-    Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply();
-    {
-        SCOPED_TRACE("non-existing layer stack");
-        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
-    }
-
-    Transaction().setLayerStack(layer, mDisplayLayerStack).apply();
-    {
-        SCOPED_TRACE("original layer stack");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBufferFormat) {
-    int32_t width = 100;
-    int32_t height = 100;
-    Rect crop = Rect(0, 0, width, height);
-
-    sp<SurfaceControl> behindLayer = createColorLayer("Behind layer", Color::RED);
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", width, height, 0, nullptr, PIXEL_FORMAT_RGBX_8888));
-
-    Transaction()
-            .setLayer(layer, INT32_MAX - 1)
-            .show(layer)
-            .setLayerStack(behindLayer, mDisplayLayerStack)
-            .setCrop_legacy(behindLayer, crop)
-            .setLayer(behindLayer, INT32_MAX - 2)
-            .show(behindLayer)
-            .apply();
-
-    sp<Surface> surface = layer->getSurface();
-
-    sp<GraphicBuffer> buffer =
-            new GraphicBuffer(width, height, PIXEL_FORMAT_RGBX_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
-    ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
-
-    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
-        Surface::attachAndQueueBufferWithDataspace(surface.get(), buffer, ui::Dataspace::V0_SRGB);
-    } else {
-        Transaction().setBuffer(layer, buffer).apply();
-    }
-
-    {
-        SCOPED_TRACE("Buffer Opaque Format");
-        auto shot = screenshot();
-        shot->expectColor(crop, Color::BLACK);
-    }
-
-    buffer = new GraphicBuffer(width, height, PIXEL_FORMAT_RGBA_8888, 1,
-                               BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                       BufferUsage::COMPOSER_OVERLAY,
-                               "test");
-    ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, crop, Color::TRANSPARENT));
-
-    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
-        Surface::attachAndQueueBufferWithDataspace(surface.get(), buffer, ui::Dataspace::V0_SRGB);
-    } else {
-        Transaction().setBuffer(layer, buffer).apply();
-    }
-
-    {
-        SCOPED_TRACE("Buffer Transparent Format");
-        auto shot = screenshot();
-        shot->expectColor(crop, Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
-                                                         Color::BLUE, Color::WHITE));
-
-    Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 0, 0).apply();
-    {
-        SCOPED_TRACE("IDENTITY");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
-                                           Color::BLUE, Color::WHITE);
-    }
-
-    Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 32, 0).apply();
-    {
-        SCOPED_TRACE("FLIP_H");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED,
-                                           Color::WHITE, Color::BLUE);
-    }
-
-    Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).setPosition(layer, 0, 32).apply();
-    {
-        SCOPED_TRACE("FLIP_V");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE,
-                                           Color::RED, Color::GREEN);
-    }
-
-    Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).setPosition(layer, 32, 0).apply();
-    {
-        SCOPED_TRACE("ROT_90");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED,
-                                           Color::WHITE, Color::GREEN);
-    }
-
-    Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setPosition(layer, 0, 0).apply();
-    {
-        SCOPED_TRACE("SCALE");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN,
-                                           Color::BLUE, Color::WHITE, true /* filtered */);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
-                                                         Color::BLUE, Color::WHITE));
-
-    Transaction()
-            .setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f)
-            .setFrame(layer, Rect(0, 0, 32, 32))
-            .apply();
-    {
-        SCOPED_TRACE("IDENTITY");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
-                                           Color::BLUE, Color::WHITE);
-    }
-
-    Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).apply();
-    {
-        SCOPED_TRACE("FLIP_H");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
-                                           Color::BLUE, Color::WHITE);
-    }
-
-    Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).apply();
-    {
-        SCOPED_TRACE("FLIP_V");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
-                                           Color::BLUE, Color::WHITE);
-    }
-
-    Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).apply();
-    {
-        SCOPED_TRACE("ROT_90");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
-                                           Color::BLUE, Color::WHITE);
-    }
-
-    Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).apply();
-    {
-        SCOPED_TRACE("SCALE");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN,
-                                           Color::BLUE, Color::WHITE);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixRot45_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
-                                                         Color::BLUE, Color::WHITE));
-
-    const float rot = M_SQRT1_2; // 45 degrees
-    const float trans = M_SQRT2 * 16.0f;
-    Transaction().setMatrix(layer, rot, rot, -rot, rot).setPosition(layer, trans, 0).apply();
-
-    auto shot = getScreenCapture();
-    // check a 8x8 region inside each color
-    auto get8x8Rect = [](int32_t centerX, int32_t centerY) {
-        const int32_t halfL = 4;
-        return Rect(centerX - halfL, centerY - halfL, centerX + halfL, centerY + halfL);
-    };
-    const int32_t unit = int32_t(trans / 2);
-    shot->expectColor(get8x8Rect(2 * unit, 1 * unit), Color::RED);
-    shot->expectColor(get8x8Rect(3 * unit, 2 * unit), Color::GREEN);
-    shot->expectColor(get8x8Rect(1 * unit, 2 * unit), Color::BLUE);
-    shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // setMatrix is applied after any pending resize, unlike setPosition
-    Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setSize(layer, 64, 64).apply();
-    {
-        SCOPED_TRACE("resize pending");
-        auto shot = getScreenCapture();
-        const Rect rect(0, 0, 32, 32);
-        shot->expectColor(rect, Color::RED);
-        shot->expectBorder(rect, Color::BLACK);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
-    {
-        SCOPED_TRACE("resize applied");
-        const Rect rect(0, 0, 128, 128);
-        getScreenCapture()->expectColor(rect, Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition
-    Transaction()
-            .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
-            .setSize(layer, 64, 64)
-            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
-            .apply();
-    getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
-                                                         Color::BLUE, Color::WHITE));
-
-    // XXX SCALE_CROP is not respected; calling setSize and
-    // setOverrideScalingMode in separate transactions does not work
-    // (b/69315456)
-    Transaction()
-            .setSize(layer, 64, 16)
-            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
-            .apply();
-    {
-        SCOPED_TRACE("SCALE_TO_WINDOW");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN,
-                                           Color::BLUE, Color::WHITE, true /* filtered */);
-    }
-}
-
-TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-
-    sp<IBinder> handle = layer->getHandle();
-    ASSERT_TRUE(handle != nullptr);
-
-    FrameStats frameStats;
-    mClient->getLayerFrameStats(handle, &frameStats);
-
-    ASSERT_GT(frameStats.refreshPeriodNano, static_cast<nsecs_t>(0));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-    const Rect crop(8, 8, 24, 24);
-
-    Transaction().setCrop_legacy(layer, crop).apply();
-    auto shot = getScreenCapture();
-    shot->expectColor(crop, Color::RED);
-    shot->expectBorder(crop, Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-    const Rect crop(8, 8, 24, 24);
-
-    Transaction().setCrop(layer, crop).apply();
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    {
-        SCOPED_TRACE("empty rect");
-        Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply();
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    {
-        SCOPED_TRACE("negative rect");
-        Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply();
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-
-    {
-        SCOPED_TRACE("empty rect");
-        Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply();
-        getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    }
-
-    {
-        SCOPED_TRACE("negative rect");
-        Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
-        getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply();
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
-    sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
-    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE);
-    fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED);
-
-    Transaction().setFrame(layer, Rect(0, 0, 64, 64)).apply();
-
-    Transaction().setBuffer(layer, buffer).apply();
-
-    // Partially out of bounds in the negative (upper left) direction
-    Transaction().setCrop(layer, Rect(-128, -128, 32, 16)).apply();
-    {
-        SCOPED_TRACE("out of bounds, negative (upper left) direction");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 64, 64), Color::BLUE);
-        shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
-    }
-
-    // Partially out of bounds in the positive (lower right) direction
-    Transaction().setCrop(layer, Rect(0, 16, 128, 128)).apply();
-    {
-        SCOPED_TRACE("out of bounds, positive (lower right) direction");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
-        shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
-    }
-
-    // Fully out of buffer space bounds
-    Transaction().setCrop(layer, Rect(-128, -128, -1, -1)).apply();
-    {
-        SCOPED_TRACE("Fully out of bounds");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 64, 16), Color::BLUE);
-        shot->expectColor(Rect(0, 16, 64, 64), Color::RED);
-        shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    const Point position(32, 32);
-    const Rect crop(8, 8, 24, 24);
-    Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply();
-    auto shot = getScreenCapture();
-    shot->expectColor(crop + position, Color::RED);
-    shot->expectBorder(crop + position, Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-
-    const Rect frame(32, 32, 64, 64);
-    const Rect crop(8, 8, 24, 24);
-    Transaction().setFrame(layer, frame).setCrop(layer, crop).apply();
-    auto shot = getScreenCapture();
-    shot->expectColor(frame, Color::RED);
-    shot->expectBorder(frame, Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithScale_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // crop_legacy is affected by matrix
-    Transaction()
-            .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
-            .setCrop_legacy(layer, Rect(8, 8, 24, 24))
-            .apply();
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
-    shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithResize_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // setCrop_legacy is applied immediately by default, with or without resize pending
-    Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply();
-    {
-        SCOPED_TRACE("resize pending");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
-        shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
-    {
-        SCOPED_TRACE("resize applied");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(8, 8, 16, 16), Color::RED);
-        shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResize_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // request setCrop_legacy to be applied with the next resize
-    Transaction()
-            .setCrop_legacy(layer, Rect(8, 8, 24, 24))
-            .setGeometryAppliesWithResize(layer)
-            .apply();
-    {
-        SCOPED_TRACE("waiting for next resize");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setCrop_legacy(layer, Rect(4, 4, 12, 12)).apply();
-    {
-        SCOPED_TRACE("pending crop modified");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setSize(layer, 16, 16).apply();
-    {
-        SCOPED_TRACE("resize pending");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    // finally resize
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
-    {
-        SCOPED_TRACE("new crop applied");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
-        shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResizeScaleToWindow_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // setCrop_legacy is not immediate even with SCALE_TO_WINDOW override
-    Transaction()
-            .setCrop_legacy(layer, Rect(4, 4, 12, 12))
-            .setSize(layer, 16, 16)
-            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
-            .setGeometryAppliesWithResize(layer)
-            .apply();
-    {
-        SCOPED_TRACE("new crop pending");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
-        shot->expectBorder(Rect(0, 0, 16, 16), Color::BLACK);
-    }
-
-    // XXX crop is never latched without other geometry change (b/69315677)
-    Transaction().setPosition(layer, 1, 0).setGeometryAppliesWithResize(layer).apply();
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
-    Transaction().setPosition(layer, 0, 0).apply();
-    {
-        SCOPED_TRACE("new crop applied");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
-        shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-    const Rect frame(8, 8, 24, 24);
-
-    Transaction().setFrame(layer, frame).apply();
-    auto shot = getScreenCapture();
-    shot->expectColor(frame, Color::RED);
-    shot->expectBorder(frame, Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameEmpty_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-
-    {
-        SCOPED_TRACE("empty rect");
-        Transaction().setFrame(layer, Rect(8, 8, 8, 8)).apply();
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-
-    {
-        SCOPED_TRACE("negative rect");
-        Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply();
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultParentless_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10));
-
-    // A parentless layer will default to a frame with the same size as the buffer
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) {
-    sp<SurfaceControl> parent, child;
-    ASSERT_NO_FATAL_FAILURE(
-            parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
-    Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
-
-    ASSERT_NO_FATAL_FAILURE(
-            child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
-
-    Transaction().reparent(child, parent->getHandle()).apply();
-
-    // A layer will default to the frame of its parent
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
-    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBQParent_BufferState) {
-    sp<SurfaceControl> parent, child;
-    ASSERT_NO_FATAL_FAILURE(parent = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parent, Color::RED, 32, 32));
-
-    ASSERT_NO_FATAL_FAILURE(
-            child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
-
-    Transaction().reparent(child, parent->getHandle()).apply();
-
-    // A layer will default to the frame of its parent
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
-    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameUpdate_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-    Transaction().setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
-    std::this_thread::sleep_for(500ms);
-
-    Transaction().setFrame(layer, Rect(16, 16, 48, 48)).apply();
-
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(16, 16, 48, 48), Color::RED);
-    shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameOutsideBounds_BufferState) {
-    sp<SurfaceControl> parent, child;
-    ASSERT_NO_FATAL_FAILURE(
-            parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(
-            child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-    Transaction().reparent(child, parent->getHandle()).apply();
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32));
-    Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply();
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10));
-    Transaction().setFrame(child, Rect(0, 16, 32, 32)).apply();
-
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, 32, 16), Color::RED);
-    shot->expectColor(Rect(0, 16, 32, 32), Color::BLUE);
-    shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferBasic_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-
-    {
-        SCOPED_TRACE("set buffer 1");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32));
-
-    {
-        SCOPED_TRACE("set buffer 2");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLUE);
-        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
-
-    {
-        SCOPED_TRACE("set buffer 3");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) {
-    sp<SurfaceControl> layer1;
-    ASSERT_NO_FATAL_FAILURE(
-            layer1 = createLayer("test", 64, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    sp<SurfaceControl> layer2;
-    ASSERT_NO_FATAL_FAILURE(
-            layer2 = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64));
-
-    Transaction().setFrame(layer1, Rect(0, 0, 64, 64)).apply();
-    {
-        SCOPED_TRACE("set layer 1 buffer red");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32));
-
-    Transaction().setFrame(layer2, Rect(0, 0, 32, 32)).apply();
-    {
-        SCOPED_TRACE("set layer 2 buffer blue");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
-        shot->expectColor(Rect(0, 32, 64, 64), Color::RED);
-        shot->expectColor(Rect(0, 32, 32, 64), Color::RED);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64));
-    {
-        SCOPED_TRACE("set layer 1 buffer green");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
-        shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
-        shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::WHITE, 32, 32));
-
-    {
-        SCOPED_TRACE("set layer 2 buffer white");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::WHITE);
-        shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN);
-        shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
-
-    std::array<sp<GraphicBuffer>, 10> buffers;
-
-    size_t idx = 0;
-    for (auto& buffer : buffers) {
-        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                           BufferUsage::COMPOSER_OVERLAY,
-                                   "test");
-        Color color = colors[idx % colors.size()];
-        fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
-        idx++;
-    }
-
-    // Set each buffer twice. The first time adds it to the cache, the second time tests that the
-    // cache is working.
-    idx = 0;
-    for (auto& buffer : buffers) {
-        for (int i = 0; i < 2; i++) {
-            Transaction().setBuffer(layer, buffer).apply();
-
-            Color color = colors[idx % colors.size()];
-            auto shot = screenshot();
-            shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
-            shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-        }
-        idx++;
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_LeastRecentlyUsed_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
-
-    std::array<sp<GraphicBuffer>, 70> buffers;
-
-    size_t idx = 0;
-    for (auto& buffer : buffers) {
-        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                           BufferUsage::COMPOSER_OVERLAY,
-                                   "test");
-        Color color = colors[idx % colors.size()];
-        fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
-        idx++;
-    }
-
-    // Set each buffer twice. The first time adds it to the cache, the second time tests that the
-    // cache is working.
-    idx = 0;
-    for (auto& buffer : buffers) {
-        for (int i = 0; i < 2; i++) {
-            Transaction().setBuffer(layer, buffer).apply();
-
-            Color color = colors[idx % colors.size()];
-            auto shot = screenshot();
-            shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
-            shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-        }
-        idx++;
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_DestroyedBuffer_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
-
-    std::array<sp<GraphicBuffer>, 65> buffers;
-
-    size_t idx = 0;
-    for (auto& buffer : buffers) {
-        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                           BufferUsage::COMPOSER_OVERLAY,
-                                   "test");
-        Color color = colors[idx % colors.size()];
-        fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
-        idx++;
-    }
-
-    // Set each buffer twice. The first time adds it to the cache, the second time tests that the
-    // cache is working.
-    idx = 0;
-    for (auto& buffer : buffers) {
-        for (int i = 0; i < 2; i++) {
-            Transaction().setBuffer(layer, buffer).apply();
-
-            Color color = colors[idx % colors.size()];
-            auto shot = screenshot();
-            shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
-            shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-        }
-        if (idx == 0) {
-            buffers[0].clear();
-        }
-        idx++;
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransformRotate90_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
-                                                         Color::BLUE, Color::WHITE));
-
-    Transaction()
-            .setFrame(layer, Rect(0, 0, 32, 32))
-            .setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90)
-            .apply();
-
-    getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE,
-                                       Color::GREEN, true /* filtered */);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipH_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
-                                                         Color::BLUE, Color::WHITE));
-
-    Transaction()
-            .setFrame(layer, Rect(0, 0, 32, 32))
-            .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H)
-            .apply();
-
-    getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE,
-                                       Color::BLUE, true /* filtered */);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipV_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN,
-                                                         Color::BLUE, Color::WHITE));
-
-    Transaction()
-            .setFrame(layer, Rect(0, 0, 32, 32))
-            .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V)
-            .apply();
-
-    getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED,
-                                       Color::GREEN, true /* filtered */);
-}
-
-TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    Transaction().setTransformToDisplayInverse(layer, false).apply();
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::GREEN, 32, 32));
-
-    Transaction().setTransformToDisplayInverse(layer, true).apply();
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) {
-    sp<SurfaceControl> layer;
-    Transaction transaction;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
-    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
-    sp<Fence> fence;
-    if (getBuffer(nullptr, &fence) != NO_ERROR) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply();
-
-    status_t status = fence->wait(1000);
-    ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status);
-    std::this_thread::sleep_for(200ms);
-
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
-    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
-    sp<Fence> fence = Fence::NO_FENCE;
-
-    Transaction()
-            .setBuffer(layer, buffer)
-            .setAcquireFence(layer, fence)
-            .apply();
-
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
-    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
-    Transaction()
-            .setBuffer(layer, buffer)
-            .setDataspace(layer, ui::Dataspace::UNKNOWN)
-            .apply();
-
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
-    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
-    HdrMetadata hdrMetadata;
-    hdrMetadata.validTypes = 0;
-    Transaction()
-            .setBuffer(layer, buffer)
-            .setHdrMetadata(layer, hdrMetadata)
-            .apply();
-
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
-    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
-    Region region;
-    region.set(32, 32);
-    Transaction()
-            .setBuffer(layer, buffer)
-            .setSurfaceDamageRegion(layer, region)
-            .apply();
-
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
-    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED);
-
-    Transaction()
-            .setBuffer(layer, buffer)
-            .setApi(layer, NATIVE_WINDOW_API_CPU)
-            .apply();
-
-    auto shot = getScreenCapture();
-    shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_F(LayerTransactionTest, SetSidebandStreamNull_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    // verify this doesn't cause a crash
-    Transaction().setSidebandStream(layer, nullptr).apply();
-}
-
-TEST_F(LayerTransactionTest, ReparentToSelf) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-    Transaction().reparent(layer, layer->getHandle()).apply();
-
-    {
-        // We expect the transaction to be silently dropped, but for SurfaceFlinger
-        // to still be functioning.
-        SCOPED_TRACE("after reparent to self");
-        const Rect rect(0, 0, 32, 32);
-        auto shot = screenshot();
-        shot->expectColor(rect, Color::RED);
-        shot->expectBorder(rect, Color::BLACK);
-    }
-}
-
-class ColorTransformHelper {
-public:
-    static void DegammaColorSingle(half& s) {
-        if (s <= 0.03928f)
-            s = s / 12.92f;
-        else
-            s = pow((s + 0.055f) / 1.055f, 2.4f);
-    }
-
-    static void DegammaColor(half3& color) {
-        DegammaColorSingle(color.r);
-        DegammaColorSingle(color.g);
-        DegammaColorSingle(color.b);
-    }
-
-    static void GammaColorSingle(half& s) {
-        if (s <= 0.0031308f) {
-            s = s * 12.92f;
-        } else {
-            s = 1.055f * pow(s, (1.0f / 2.4f)) - 0.055f;
-        }
-    }
-
-    static void GammaColor(half3& color) {
-        GammaColorSingle(color.r);
-        GammaColorSingle(color.g);
-        GammaColorSingle(color.b);
-    }
-
-    static void applyMatrix(half3& color, const mat3& mat) {
-        half3 ret = half3(0);
-
-        for (int i = 0; i < 3; i++) {
-            for (int j = 0; j < 3; j++) {
-                ret[i] = ret[i] + color[j] * mat[j][i];
-            }
-        }
-        color = ret;
-    }
-};
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
-    sp<SurfaceControl> colorLayer;
-    ASSERT_NO_FATAL_FAILURE(colorLayer =
-                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
-                                                ISurfaceComposerClient::eFXSurfaceColor));
-    Transaction()
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
-            .setLayer(colorLayer, mLayerZBase + 1)
-            .apply();
-    {
-        SCOPED_TRACE("default color");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-
-    const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
-    half3 expected = color;
-    mat3 matrix;
-    matrix[0][0] = 0.3; matrix[1][0] = 0.59; matrix[2][0] = 0.11;
-    matrix[0][1] = 0.3; matrix[1][1] = 0.59; matrix[2][1] = 0.11;
-    matrix[0][2] = 0.3; matrix[1][2] = 0.59; matrix[2][2] = 0.11;
-
-    // degamma before applying the matrix
-    if (mColorManagementUsed) {
-        ColorTransformHelper::DegammaColor(expected);
-    }
-
-    ColorTransformHelper::applyMatrix(expected, matrix);
-
-    if (mColorManagementUsed) {
-        ColorTransformHelper::GammaColor(expected);
-    }
-
-    const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
-                                 uint8_t(expected.b * 255), 255};
-
-    // this is handwavy, but the precison loss scaled by 255 (8-bit per
-    // channel) should be less than one
-    const uint8_t tolerance = 1;
-
-    Transaction().setColor(colorLayer, color)
-        .setColorTransform(colorLayer, matrix, vec3()).apply();
-    {
-        SCOPED_TRACE("new color");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnParent) {
-    sp<SurfaceControl> parentLayer;
-    sp<SurfaceControl> colorLayer;
-    ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
-                                                      0 /* buffer height */,
-                                                      ISurfaceComposerClient::eFXSurfaceContainer));
-    ASSERT_NO_FATAL_FAILURE(
-            colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
-                                     ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get()));
-
-    Transaction()
-            .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
-            .setLayer(parentLayer, mLayerZBase + 1)
-            .apply();
-    {
-        SCOPED_TRACE("default color");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-
-    const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
-    half3 expected = color;
-    mat3 matrix;
-    matrix[0][0] = 0.3; matrix[1][0] = 0.59; matrix[2][0] = 0.11;
-    matrix[0][1] = 0.3; matrix[1][1] = 0.59; matrix[2][1] = 0.11;
-    matrix[0][2] = 0.3; matrix[1][2] = 0.59; matrix[2][2] = 0.11;
-
-    // degamma before applying the matrix
-    if (mColorManagementUsed) {
-        ColorTransformHelper::DegammaColor(expected);
-    }
-
-    ColorTransformHelper::applyMatrix(expected, matrix);
-
-    if (mColorManagementUsed) {
-        ColorTransformHelper::GammaColor(expected);
-    }
-
-    const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
-                                 uint8_t(expected.b * 255), 255};
-
-    // this is handwavy, but the precison loss scaled by 255 (8-bit per
-    // channel) should be less than one
-    const uint8_t tolerance = 1;
-
-    Transaction()
-            .setColor(colorLayer, color)
-            .setColorTransform(parentLayer, matrix, vec3())
-            .apply();
-    {
-        SCOPED_TRACE("new color");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnChildAndParent) {
-    sp<SurfaceControl> parentLayer;
-    sp<SurfaceControl> colorLayer;
-    ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */,
-                                                      0 /* buffer height */,
-                                                      ISurfaceComposerClient::eFXSurfaceContainer));
-    ASSERT_NO_FATAL_FAILURE(
-            colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
-                                     ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get()));
-
-    Transaction()
-            .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100))
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
-            .setLayer(parentLayer, mLayerZBase + 1)
-            .apply();
-    {
-        SCOPED_TRACE("default color");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-
-    const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f);
-    half3 expected = color;
-    mat3 matrixChild;
-    matrixChild[0][0] = 0.3; matrixChild[1][0] = 0.59; matrixChild[2][0] = 0.11;
-    matrixChild[0][1] = 0.3; matrixChild[1][1] = 0.59; matrixChild[2][1] = 0.11;
-    matrixChild[0][2] = 0.3; matrixChild[1][2] = 0.59; matrixChild[2][2] = 0.11;
-    mat3 matrixParent;
-    matrixParent[0][0] = 0.2; matrixParent[1][0] = 0.4; matrixParent[2][0] = 0.10;
-    matrixParent[0][1] = 0.2; matrixParent[1][1] = 0.4; matrixParent[2][1] = 0.10;
-    matrixParent[0][2] = 0.2; matrixParent[1][2] = 0.4; matrixParent[2][2] = 0.10;
-
-    // degamma before applying the matrix
-    if (mColorManagementUsed) {
-        ColorTransformHelper::DegammaColor(expected);
-    }
-
-    ColorTransformHelper::applyMatrix(expected, matrixChild);
-    ColorTransformHelper::applyMatrix(expected, matrixParent);
-
-    if (mColorManagementUsed) {
-        ColorTransformHelper::GammaColor(expected);
-    }
-
-    const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255),
-                                 uint8_t(expected.b * 255), 255};
-
-    // this is handwavy, but the precison loss scaled by 255 (8-bit per
-    // channel) should be less than one
-    const uint8_t tolerance = 1;
-
-    Transaction()
-            .setColor(colorLayer, color)
-            .setColorTransform(parentLayer, matrixParent, vec3())
-            .setColorTransform(colorLayer, matrixChild, vec3())
-            .apply();
-    {
-        SCOPED_TRACE("new color");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
-    }
-}
-
-struct CallbackData {
-    CallbackData() = default;
-    CallbackData(nsecs_t time, const sp<Fence>& fence,
-                 const std::vector<SurfaceControlStats>& stats)
-          : latchTime(time), presentFence(fence), surfaceControlStats(stats) {}
-
-    nsecs_t latchTime;
-    sp<Fence> presentFence;
-    std::vector<SurfaceControlStats> surfaceControlStats;
-};
-
-class ExpectedResult {
-public:
-    enum Transaction {
-        NOT_PRESENTED = 0,
-        PRESENTED,
-    };
-
-    enum Buffer {
-        NOT_ACQUIRED = 0,
-        ACQUIRED,
-    };
-
-    enum PreviousBuffer {
-        NOT_RELEASED = 0,
-        RELEASED,
-        UNKNOWN,
-    };
-
-    void reset() {
-        mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
-        mExpectedSurfaceResults.clear();
-    }
-
-    void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer,
-                    ExpectedResult::Buffer bufferResult = ACQUIRED,
-                    ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
-        mTransactionResult = transactionResult;
-        mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer),
-                                        std::forward_as_tuple(bufferResult, previousBufferResult));
-    }
-
-    void addSurfaces(ExpectedResult::Transaction transactionResult,
-                     const std::vector<sp<SurfaceControl>>& layers,
-                     ExpectedResult::Buffer bufferResult = ACQUIRED,
-                     ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
-        for (const auto& layer : layers) {
-            addSurface(transactionResult, layer, bufferResult, previousBufferResult);
-        }
-    }
-
-    void addExpectedPresentTime(nsecs_t expectedPresentTime) {
-        mExpectedPresentTime = expectedPresentTime;
-    }
-
-    void verifyCallbackData(const CallbackData& callbackData) const {
-        const auto& [latchTime, presentFence, surfaceControlStats] = callbackData;
-        if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
-            ASSERT_GE(latchTime, 0) << "bad latch time";
-            ASSERT_NE(presentFence, nullptr);
-            if (mExpectedPresentTime >= 0) {
-                ASSERT_EQ(presentFence->wait(3000), NO_ERROR);
-                ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6));
-                // if the panel is running at 30 hz, at the worst case, our expected time just
-                // misses vsync and we have to wait another 33.3ms
-                ASSERT_LE(presentFence->getSignalTime(),
-                          mExpectedPresentTime + nsecs_t(66.666666 * 1e6));
-            }
-        } else {
-            ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented";
-            ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
-        }
-
-        ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size())
-                << "wrong number of surfaces";
-
-        for (const auto& stats : surfaceControlStats) {
-            ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control";
-
-            const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl);
-            ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end())
-                    << "unexpected surface control";
-            expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime);
-        }
-    }
-
-private:
-    class ExpectedSurfaceResult {
-    public:
-        ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult,
-                              ExpectedResult::PreviousBuffer previousBufferResult)
-              : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {}
-
-        void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
-                                       nsecs_t latchTime) const {
-            const auto& [surfaceControl, acquireTime, previousReleaseFence] = surfaceControlStats;
-
-            ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
-                    << "bad acquire time";
-            ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time";
-
-            if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) {
-                ASSERT_NE(previousReleaseFence, nullptr)
-                        << "failed to set release prev buffer fence";
-            } else if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::NOT_RELEASED) {
-                ASSERT_EQ(previousReleaseFence, nullptr)
-                        << "should not have set released prev buffer fence";
-            }
-        }
-
-    private:
-        ExpectedResult::Buffer mBufferResult;
-        ExpectedResult::PreviousBuffer mPreviousBufferResult;
-    };
-
-    struct SCHash {
-        std::size_t operator()(const sp<SurfaceControl>& sc) const {
-            return std::hash<IBinder*>{}(sc->getHandle().get());
-        }
-    };
-    ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
-    nsecs_t mExpectedPresentTime = -1;
-    std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults;
-};
-
-class CallbackHelper {
-public:
-    static void function(void* callbackContext, nsecs_t latchTime, const sp<Fence>& presentFence,
-                         const std::vector<SurfaceControlStats>& stats) {
-        if (!callbackContext) {
-            ALOGE("failed to get callback context");
-        }
-        CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext);
-        std::lock_guard lock(helper->mMutex);
-        helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats);
-        helper->mConditionVariable.notify_all();
-    }
-
-    void getCallbackData(CallbackData* outData) {
-        std::unique_lock lock(mMutex);
-
-        if (mCallbackDataQueue.empty()) {
-            ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)),
-                      std::cv_status::timeout)
-                    << "did not receive callback";
-        }
-
-        *outData = std::move(mCallbackDataQueue.front());
-        mCallbackDataQueue.pop();
-    }
-
-    void verifyFinalState() {
-        // Wait to see if there are extra callbacks
-        std::this_thread::sleep_for(500ms);
-
-        std::lock_guard lock(mMutex);
-        EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
-        mCallbackDataQueue = {};
-    }
-
-    void* getContext() { return static_cast<void*>(this); }
-
-    std::mutex mMutex;
-    std::condition_variable mConditionVariable;
-    std::queue<CallbackData> mCallbackDataQueue;
-};
-
-class LayerCallbackTest : public LayerTransactionTest {
-public:
-    virtual sp<SurfaceControl> createBufferStateLayer() {
-        return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState);
-    }
-
-    static int fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper,
-                               const sp<SurfaceControl>& layer = nullptr, bool setBuffer = true,
-                               bool setBackgroundColor = false) {
-        if (layer) {
-            sp<GraphicBuffer> buffer;
-            sp<Fence> fence;
-            if (setBuffer) {
-                int err = getBuffer(&buffer, &fence);
-                if (err != NO_ERROR) {
-                    return err;
-                }
-
-                transaction.setBuffer(layer, buffer);
-                transaction.setAcquireFence(layer, fence);
-            }
-
-            if (setBackgroundColor) {
-                transaction.setBackgroundColor(layer, /*color*/ half3(1.0f, 0, 0), /*alpha*/ 1.0f,
-                                               ui::Dataspace::UNKNOWN);
-            }
-        }
-
-        transaction.addTransactionCompletedCallback(callbackHelper->function,
-                                                    callbackHelper->getContext());
-        return NO_ERROR;
-    }
-
-    static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
-                                bool finalState = false) {
-        CallbackData callbackData;
-        ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
-        EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData));
-
-        if (finalState) {
-            ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
-        }
-    }
-
-    static void waitForCallbacks(CallbackHelper& helper,
-                                 const std::vector<ExpectedResult>& expectedResults,
-                                 bool finalState = false) {
-        for (const auto& expectedResult : expectedResults) {
-            waitForCallback(helper, expectedResult);
-        }
-        if (finalState) {
-            ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
-        }
-    }
-};
-
-TEST_F(LayerCallbackTest, BufferColor) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback, layer, true, true);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction.apply();
-
-    ExpectedResult expected;
-    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, NoBufferNoColor) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback, layer, false, false);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
-    ExpectedResult expected;
-    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
-                        ExpectedResult::Buffer::NOT_ACQUIRED);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, BufferNoColor) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback, layer, true, false);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
-    ExpectedResult expected;
-    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, NoBufferColor) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback, layer, false, true);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
-    ExpectedResult expected;
-    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
-                        ExpectedResult::Buffer::NOT_ACQUIRED);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, NoStateChange) {
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction.apply();
-
-    ExpectedResult expected;
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, OffScreen) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback, layer);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction.setFrame(layer, Rect(-100, -100, 100, 100)).apply();
-
-    ExpectedResult expected;
-    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, MergeBufferNoColor) {
-    sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
-
-    Transaction transaction1, transaction2;
-    CallbackHelper callback1, callback2;
-    int err = fillTransaction(transaction1, &callback1, layer1);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-    err = fillTransaction(transaction2, &callback2, layer2);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
-    ExpectedResult expected;
-    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
-TEST_F(LayerCallbackTest, MergeNoBufferColor) {
-    sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
-
-    Transaction transaction1, transaction2;
-    CallbackHelper callback1, callback2;
-    int err = fillTransaction(transaction1, &callback1, layer1, false, true);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-    err = fillTransaction(transaction2, &callback2, layer2, false, true);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
-    ExpectedResult expected;
-    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
-                         ExpectedResult::Buffer::NOT_ACQUIRED);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
-TEST_F(LayerCallbackTest, MergeOneBufferOneColor) {
-    sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
-
-    Transaction transaction1, transaction2;
-    CallbackHelper callback1, callback2;
-    int err = fillTransaction(transaction1, &callback1, layer1);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-    err = fillTransaction(transaction2, &callback2, layer2, false, true);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
-    ExpectedResult expected;
-    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer1);
-    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer2,
-                        ExpectedResult::Buffer::NOT_ACQUIRED);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-TEST_F(LayerCallbackTest, Merge_SameCallback) {
-    sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
-
-    Transaction transaction1, transaction2;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction1, &callback, layer1);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-    err = fillTransaction(transaction2, &callback, layer2);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction2.merge(std::move(transaction1)).apply();
-
-    ExpectedResult expected;
-    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, Merge_SameLayer) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction1, transaction2;
-    CallbackHelper callback1, callback2;
-    int err = fillTransaction(transaction1, &callback1, layer);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-    err = fillTransaction(transaction2, &callback2, layer);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction2.merge(std::move(transaction1)).apply();
-
-    ExpectedResult expected;
-    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
-TEST_F(LayerCallbackTest, Merge_DifferentClients) {
-    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
-            client2(new SurfaceComposerClient);
-
-    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
-    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
-
-    sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
-                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
-                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    Transaction transaction1, transaction2;
-    CallbackHelper callback1, callback2;
-    int err = fillTransaction(transaction1, &callback1, layer1);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-    err = fillTransaction(transaction2, &callback2, layer2);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
-    ExpectedResult expected;
-    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    for (size_t i = 0; i < 10; i++) {
-        int err = fillTransaction(transaction, &callback, layer);
-        if (err) {
-            GTEST_SUCCEED() << "test not supported";
-            return;
-        }
-
-        transaction.apply();
-
-        ExpectedResult expected;
-        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
-                            ExpectedResult::Buffer::ACQUIRED,
-                            (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
-                                     : ExpectedResult::PreviousBuffer::RELEASED);
-        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
-    }
-    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    for (size_t i = 0; i < 10; i++) {
-        ExpectedResult expected;
-
-        if (i == 0) {
-            int err = fillTransaction(transaction, &callback, layer);
-            if (err) {
-                GTEST_SUCCEED() << "test not supported";
-                return;
-            }
-            expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
-        } else {
-            int err = fillTransaction(transaction, &callback);
-            if (err) {
-                GTEST_SUCCEED() << "test not supported";
-                return;
-            }
-        }
-
-        transaction.apply();
-
-        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected));
-    }
-    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    for (size_t i = 0; i < 10; i++) {
-        if (i == 0) {
-            int err = fillTransaction(transaction, &callback, layer);
-            if (err) {
-                GTEST_SUCCEED() << "test not supported";
-                return;
-            }
-        } else {
-            int err = fillTransaction(transaction, &callback);
-            if (err) {
-                GTEST_SUCCEED() << "test not supported";
-                return;
-            }
-        }
-
-        transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
-        ExpectedResult expected;
-        expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED
-                                     : ExpectedResult::Transaction::NOT_PRESENTED,
-                            layer,
-                            (i == 0) ? ExpectedResult::Buffer::ACQUIRED
-                                     : ExpectedResult::Buffer::NOT_ACQUIRED);
-        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, i == 0));
-    }
-    ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState());
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_Merge) {
-    sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer());
-    ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer());
-
-    Transaction transaction1, transaction2;
-    CallbackHelper callback1, callback2;
-    for (size_t i = 0; i < 10; i++) {
-        int err = fillTransaction(transaction1, &callback1, layer1);
-        if (err) {
-            GTEST_SUCCEED() << "test not supported";
-            return;
-        }
-        err = fillTransaction(transaction2, &callback2, layer2);
-        if (err) {
-            GTEST_SUCCEED() << "test not supported";
-            return;
-        }
-
-        transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-        transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
-        ExpectedResult expected;
-        expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
-                             ExpectedResult::Buffer::ACQUIRED,
-                             (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
-                                      : ExpectedResult::PreviousBuffer::RELEASED);
-        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
-        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
-    }
-    ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
-    ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients) {
-    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
-            client2(new SurfaceComposerClient);
-    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
-    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
-
-    sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
-                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
-                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    Transaction transaction1, transaction2;
-    CallbackHelper callback1, callback2;
-    for (size_t i = 0; i < 10; i++) {
-        int err = fillTransaction(transaction1, &callback1, layer1);
-        if (err) {
-            GTEST_SUCCEED() << "test not supported";
-            return;
-        }
-        err = fillTransaction(transaction2, &callback2, layer2);
-        if (err) {
-            GTEST_SUCCEED() << "test not supported";
-            return;
-        }
-
-        transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-        transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
-        ExpectedResult expected;
-        expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2},
-                             ExpectedResult::Buffer::ACQUIRED,
-                             (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED
-                                      : ExpectedResult::PreviousBuffer::RELEASED);
-        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected));
-        EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected));
-    }
-    ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState());
-    ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState());
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_NoStateChange) {
-    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
-            client2(new SurfaceComposerClient);
-    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
-    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
-
-    sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
-                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
-                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    Transaction transaction1, transaction2;
-    CallbackHelper callback1, callback2;
-
-    // Normal call to set up test
-    int err = fillTransaction(transaction1, &callback1, layer1);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-    err = fillTransaction(transaction2, &callback2, layer2);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
-    ExpectedResult expected;
-    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-    expected.reset();
-
-    // Test
-    err = fillTransaction(transaction1, &callback1);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-    err = fillTransaction(transaction2, &callback2);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction2.merge(std::move(transaction1)).apply();
-
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateChange) {
-    sp<SurfaceComposerClient> client1(new SurfaceComposerClient),
-            client2(new SurfaceComposerClient);
-
-    ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient";
-    ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient";
-
-    sp<SurfaceControl> layer1, layer2;
-    ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0,
-                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
-    ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0,
-                                                 ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    Transaction transaction1, transaction2;
-    CallbackHelper callback1, callback2;
-
-    // Normal call to set up test
-    int err = fillTransaction(transaction1, &callback1, layer1);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-    err = fillTransaction(transaction2, &callback2, layer2);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction1.setFrame(layer1, Rect(0, 0, 32, 32));
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
-    ExpectedResult expected;
-    expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2});
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-    expected.reset();
-
-    // Test
-    err = fillTransaction(transaction1, &callback1);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-    err = fillTransaction(transaction2, &callback2);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply();
-
-    expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2,
-                        ExpectedResult::Buffer::NOT_ACQUIRED);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    std::vector<ExpectedResult> expectedResults(50);
-    for (auto& expected : expectedResults) {
-        expected.reset();
-        expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
-                            ExpectedResult::Buffer::ACQUIRED,
-                            ExpectedResult::PreviousBuffer::UNKNOWN);
-
-        int err = fillTransaction(transaction, &callback, layer);
-        if (err) {
-            GTEST_SUCCEED() << "test not supported";
-            return;
-        }
-
-        transaction.apply();
-    }
-    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    // Normal call to set up test
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback, layer);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction.apply();
-
-    ExpectedResult expected;
-    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-
-    // Test
-    std::vector<ExpectedResult> expectedResults(50);
-    for (auto& expected : expectedResults) {
-        expected.reset();
-
-        err = fillTransaction(transaction, &callback);
-        if (err) {
-            GTEST_SUCCEED() << "test not supported";
-            return;
-        }
-
-        transaction.apply();
-    }
-    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
-}
-
-TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    // Normal call to set up test
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback, layer);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
-    ExpectedResult expectedResult;
-    expectedResult.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expectedResult, true));
-
-    // Test
-    std::vector<ExpectedResult> expectedResults(50);
-    for (auto& expected : expectedResults) {
-        expected.reset();
-        expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer,
-                            ExpectedResult::Buffer::NOT_ACQUIRED);
-
-        err = fillTransaction(transaction, &callback);
-        if (err) {
-            GTEST_SUCCEED() << "test not supported";
-            return;
-        }
-
-        transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-    }
-    EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true));
-}
-
-TEST_F(LayerCallbackTest, DesiredPresentTime) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback, layer);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    // Try to present 100ms in the future
-    nsecs_t time = systemTime() + (100 * 1e6);
-
-    transaction.setDesiredPresentTime(time);
-    transaction.apply();
-
-    ExpectedResult expected;
-    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
-    expected.addExpectedPresentTime(time);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback1;
-    int err = fillTransaction(transaction, &callback1, layer);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    // Try to present 100ms in the future
-    nsecs_t time = systemTime() + (100 * 1e6);
-
-    transaction.setDesiredPresentTime(time);
-    transaction.apply();
-
-    ExpectedResult expected1;
-    expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
-    expected1.addExpectedPresentTime(time);
-
-    CallbackHelper callback2;
-    err = fillTransaction(transaction, &callback2, layer);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    // Try to present 33ms after the first frame
-    time += (33.3 * 1e6);
-
-    transaction.setDesiredPresentTime(time);
-    transaction.apply();
-
-    ExpectedResult expected2;
-    expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
-                         ExpectedResult::Buffer::ACQUIRED,
-                         ExpectedResult::PreviousBuffer::RELEASED);
-    expected2.addExpectedPresentTime(time);
-
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
-}
-
-TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback1;
-    int err = fillTransaction(transaction, &callback1, layer);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    // Try to present 100ms in the future
-    nsecs_t time = systemTime() + (100 * 1e6);
-
-    transaction.setDesiredPresentTime(time);
-    transaction.apply();
-
-    ExpectedResult expected1;
-    expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
-    expected1.addExpectedPresentTime(time);
-
-    CallbackHelper callback2;
-    err = fillTransaction(transaction, &callback2, layer);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    // Try to present 33ms before the previous frame
-    time -= (33.3 * 1e6);
-
-    transaction.setDesiredPresentTime(time);
-    transaction.apply();
-
-    ExpectedResult expected2;
-    expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer,
-                         ExpectedResult::Buffer::ACQUIRED,
-                         ExpectedResult::PreviousBuffer::RELEASED);
-
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true));
-}
-
-TEST_F(LayerCallbackTest, DesiredPresentTime_Past) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback, layer);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    // Try to present 100ms in the past
-    nsecs_t time = systemTime() - (100 * 1e6);
-
-    transaction.setDesiredPresentTime(time);
-    transaction.apply();
-
-    ExpectedResult expected;
-    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
-    expected.addExpectedPresentTime(systemTime());
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-class LayerUpdateTest : public LayerTransactionTest {
-protected:
-    virtual void SetUp() {
-        LayerTransactionTest::SetUp();
-        ASSERT_EQ(NO_ERROR, mClient->initCheck());
-
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
-        ASSERT_FALSE(display == nullptr);
-
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-
-        ssize_t displayWidth = info.w;
-        ssize_t displayHeight = info.h;
-
-        // Background surface
-        mBGSurfaceControl = createLayer(String8("BG Test Surface"), displayWidth,
-                                               displayHeight, 0);
-        ASSERT_TRUE(mBGSurfaceControl != nullptr);
-        ASSERT_TRUE(mBGSurfaceControl->isValid());
-        fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
-
-        // Foreground surface
-        mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0);
-
-        ASSERT_TRUE(mFGSurfaceControl != nullptr);
-        ASSERT_TRUE(mFGSurfaceControl->isValid());
-
-        fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-
-        // Synchronization surface
-        mSyncSurfaceControl = createLayer(String8("Sync Test Surface"), 1, 1, 0);
-        ASSERT_TRUE(mSyncSurfaceControl != nullptr);
-        ASSERT_TRUE(mSyncSurfaceControl->isValid());
-
-        fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
-
-        asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
-
-            t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
-
-            t.setLayer(mFGSurfaceControl, INT32_MAX - 1)
-                    .setPosition(mFGSurfaceControl, 64, 64)
-                    .show(mFGSurfaceControl);
-
-            t.setLayer(mSyncSurfaceControl, INT32_MAX - 1)
-                    .setPosition(mSyncSurfaceControl, displayWidth - 2, displayHeight - 2)
-                    .show(mSyncSurfaceControl);
-        });
-    }
-
-    virtual void TearDown() {
-        LayerTransactionTest::TearDown();
-        mBGSurfaceControl = 0;
-        mFGSurfaceControl = 0;
-        mSyncSurfaceControl = 0;
-    }
-
-    void waitForPostedBuffers() {
-        // Since the sync surface is in synchronous mode (i.e. double buffered)
-        // posting three buffers to it should ensure that at least two
-        // SurfaceFlinger::handlePageFlip calls have been made, which should
-        // guaranteed that a buffer posted to another Surface has been retired.
-        fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
-        fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
-        fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
-    }
-
-
-    sp<SurfaceControl> mBGSurfaceControl;
-    sp<SurfaceControl> mFGSurfaceControl;
-
-    // This surface is used to ensure that the buffers posted to
-    // mFGSurfaceControl have been picked up by SurfaceFlinger.
-    sp<SurfaceControl> mSyncSurfaceControl;
-};
-
-TEST_F(LayerUpdateTest, RelativesAreNotDetached) {
-
-    std::unique_ptr<ScreenCapture> sc;
-
-    sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0);
-    fillSurfaceRGBA8(relative, 10, 10, 10);
-    waitForPostedBuffers();
-
-    Transaction{}
-            .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1)
-            .setPosition(relative, 64, 64)
-            .apply();
-
-    {
-        // The relative should be on top of the FG control.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(64, 64, 10, 10, 10);
-    }
-    Transaction{}.detachChildren(mFGSurfaceControl).apply();
-
-    {
-        // Nothing should change at this point.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(64, 64, 10, 10, 10);
-    }
-
-    Transaction{}.hide(relative).apply();
-
-    {
-        // Ensure that the relative was actually hidden, rather than
-        // being left in the detached but visible state.
-        ScreenCapture::captureScreen(&sc);
-        sc->expectFGColor(64, 64);
-    }
-}
-
-class GeometryLatchingTest : public LayerUpdateTest {
-protected:
-    void EXPECT_INITIAL_STATE(const char* trace) {
-        SCOPED_TRACE(trace);
-        ScreenCapture::captureScreen(&sc);
-        // We find the leading edge of the FG surface.
-        sc->expectFGColor(127, 127);
-        sc->expectBGColor(128, 128);
-    }
-
-    void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63, false); }
-
-    void unlockFGBuffer() {
-        sp<Surface> s = mFGSurfaceControl->getSurface();
-        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
-        waitForPostedBuffers();
-    }
-
-    void completeFGResize() {
-        fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-        waitForPostedBuffers();
-    }
-    void restoreInitialState() {
-        asTransaction([&](Transaction& t) {
-            t.setSize(mFGSurfaceControl, 64, 64);
-            t.setPosition(mFGSurfaceControl, 64, 64);
-            t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
-        });
-
-        EXPECT_INITIAL_STATE("After restoring initial state");
-    }
-    std::unique_ptr<ScreenCapture> sc;
-};
-
-class CropLatchingTest : public GeometryLatchingTest {
-protected:
-    void EXPECT_CROPPED_STATE(const char* trace) {
-        SCOPED_TRACE(trace);
-        ScreenCapture::captureScreen(&sc);
-        // The edge should be moved back one pixel by our crop.
-        sc->expectFGColor(126, 126);
-        sc->expectBGColor(127, 127);
-        sc->expectBGColor(128, 128);
-    }
-
-    void EXPECT_RESIZE_STATE(const char* trace) {
-        SCOPED_TRACE(trace);
-        ScreenCapture::captureScreen(&sc);
-        // The FG is now resized too 128,128 at 64,64
-        sc->expectFGColor(64, 64);
-        sc->expectFGColor(191, 191);
-        sc->expectBGColor(192, 192);
-    }
-};
-
-TEST_F(LayerUpdateTest, DeferredTransactionTest) {
-    std::unique_ptr<ScreenCapture> sc;
-    {
-        SCOPED_TRACE("before anything");
-        ScreenCapture::captureScreen(&sc);
-        sc->expectBGColor(32, 32);
-        sc->expectFGColor(96, 96);
-        sc->expectBGColor(160, 160);
-    }
-
-    // set up two deferred transactions on different frames
-    asTransaction([&](Transaction& t) {
-        t.setAlpha(mFGSurfaceControl, 0.75);
-        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
-                                       mSyncSurfaceControl->getSurface()->getNextFrameNumber());
-    });
-
-    asTransaction([&](Transaction& t) {
-        t.setPosition(mFGSurfaceControl, 128, 128);
-        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
-                                       mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
-    });
-
-    {
-        SCOPED_TRACE("before any trigger");
-        ScreenCapture::captureScreen(&sc);
-        sc->expectBGColor(32, 32);
-        sc->expectFGColor(96, 96);
-        sc->expectBGColor(160, 160);
-    }
-
-    // should trigger the first deferred transaction, but not the second one
-    fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
-    {
-        SCOPED_TRACE("after first trigger");
-        ScreenCapture::captureScreen(&sc);
-        sc->expectBGColor(32, 32);
-        sc->checkPixel(96, 96, 162, 63, 96);
-        sc->expectBGColor(160, 160);
-    }
-
-    // should show up immediately since it's not deferred
-    asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 1.0); });
-
-    // trigger the second deferred transaction
-    fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
-    {
-        SCOPED_TRACE("after second trigger");
-        ScreenCapture::captureScreen(&sc);
-        sc->expectBGColor(32, 32);
-        sc->expectBGColor(96, 96);
-        sc->expectFGColor(160, 160);
-    }
-}
-
-TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) {
-    std::unique_ptr<ScreenCapture> sc;
-
-    sp<SurfaceControl> childNoBuffer =
-            createSurface(mClient, "Bufferless child", 0 /* buffer width */, 0 /* buffer height */,
-                          PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    sp<SurfaceControl> childBuffer = createSurface(mClient, "Buffered child", 20, 20,
-                                                   PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get());
-    fillSurfaceRGBA8(childBuffer, 200, 200, 200);
-    SurfaceComposerClient::Transaction{}
-            .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10))
-            .show(childNoBuffer)
-            .show(childBuffer)
-            .apply(true);
-    {
-        ScreenCapture::captureScreen(&sc);
-        sc->expectChildColor(73, 73);
-        sc->expectFGColor(74, 74);
-    }
-    SurfaceComposerClient::Transaction{}
-            .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
-            .apply(true);
-    {
-        ScreenCapture::captureScreen(&sc);
-        sc->expectChildColor(73, 73);
-        sc->expectChildColor(74, 74);
-    }
-}
-
-TEST_F(LayerUpdateTest, MergingTransactions) {
-    std::unique_ptr<ScreenCapture> sc;
-    {
-        SCOPED_TRACE("before move");
-        ScreenCapture::captureScreen(&sc);
-        sc->expectBGColor(0, 12);
-        sc->expectFGColor(75, 75);
-        sc->expectBGColor(145, 145);
-    }
-
-    Transaction t1, t2;
-    t1.setPosition(mFGSurfaceControl, 128, 128);
-    t2.setPosition(mFGSurfaceControl, 0, 0);
-    // We expect that the position update from t2 now
-    // overwrites the position update from t1.
-    t1.merge(std::move(t2));
-    t1.apply();
-
-    {
-        ScreenCapture::captureScreen(&sc);
-        sc->expectFGColor(1, 1);
-    }
-}
-
-class ChildLayerTest : public LayerUpdateTest {
-protected:
-    void SetUp() override {
-        LayerUpdateTest::SetUp();
-        mChild = createSurface(mClient, "Child surface", 10, 15, PIXEL_FORMAT_RGBA_8888, 0,
-                               mFGSurfaceControl.get());
-        fillSurfaceRGBA8(mChild, 200, 200, 200);
-
-        {
-            SCOPED_TRACE("before anything");
-            mCapture = screenshot();
-            mCapture->expectChildColor(64, 64);
-        }
-    }
-    void TearDown() override {
-        LayerUpdateTest::TearDown();
-        mChild = 0;
-    }
-
-    sp<SurfaceControl> mChild;
-    std::unique_ptr<ScreenCapture> mCapture;
-};
-
-TEST_F(ChildLayerTest, ChildLayerPositioning) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        mCapture = screenshot();
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // But 10 pixels in we should see the child surface
-        mCapture->expectChildColor(74, 74);
-        // And 10 more pixels we should be back to the foreground surface
-        mCapture->expectFGColor(84, 84);
-    }
-
-    asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
-
-    {
-        mCapture = screenshot();
-        // Top left of foreground should now be at 0, 0
-        mCapture->expectFGColor(0, 0);
-        // But 10 pixels in we should see the child surface
-        mCapture->expectChildColor(10, 10);
-        // And 10 more pixels we should be back to the foreground surface
-        mCapture->expectFGColor(20, 20);
-    }
-}
-
-TEST_F(ChildLayerTest, ChildLayerCropping) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
-    });
-
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(4, 4);
-        mCapture->expectBGColor(5, 5);
-    }
-}
-
-TEST_F(ChildLayerTest, ChildLayerConstraints) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setPosition(mChild, 63, 63);
-    });
-
-    {
-        mCapture = screenshot();
-        mCapture->expectFGColor(0, 0);
-        // Last pixel in foreground should now be the child.
-        mCapture->expectChildColor(63, 63);
-        // But the child should be constrained and the next pixel
-        // must be the background
-        mCapture->expectBGColor(64, 64);
-    }
-}
-
-TEST_F(ChildLayerTest, ChildLayerScaling) {
-    asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
-
-    // Find the boundary between the parent and child
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(9, 9);
-        mCapture->expectFGColor(10, 10);
-    }
-
-    asTransaction([&](Transaction& t) { t.setMatrix(mFGSurfaceControl, 2.0, 0, 0, 2.0); });
-
-    // The boundary should be twice as far from the origin now.
-    // The pixels from the last test should all be child now
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(9, 9);
-        mCapture->expectChildColor(10, 10);
-        mCapture->expectChildColor(19, 19);
-        mCapture->expectFGColor(20, 20);
-    }
-}
-
-// A child with a scale transform should be cropped by its parent bounds.
-TEST_F(ChildLayerTest, ChildLayerScalingCroppedByParent) {
-    asTransaction([&](Transaction& t) {
-        t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setPosition(mChild, 0, 0);
-    });
-
-    // Find the boundary between the parent and child.
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(9, 9);
-        mCapture->expectFGColor(10, 10);
-    }
-
-    asTransaction([&](Transaction& t) { t.setMatrix(mChild, 10.0, 0, 0, 10.0); });
-
-    // The child should fill its parent bounds and be cropped by it.
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(63, 63);
-        mCapture->expectBGColor(64, 64);
-    }
-}
-
-TEST_F(ChildLayerTest, ChildLayerAlpha) {
-    fillSurfaceRGBA8(mBGSurfaceControl, 0, 0, 254);
-    fillSurfaceRGBA8(mFGSurfaceControl, 254, 0, 0);
-    fillSurfaceRGBA8(mChild, 0, 254, 0);
-    waitForPostedBuffers();
-
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-    });
-
-    {
-        mCapture = screenshot();
-        // Unblended child color
-        mCapture->checkPixel(0, 0, 0, 254, 0);
-    }
-
-    asTransaction([&](Transaction& t) { t.setAlpha(mChild, 0.5); });
-
-    {
-        mCapture = screenshot();
-        // Child and BG blended.
-        mCapture->checkPixel(0, 0, 127, 127, 0);
-    }
-
-    asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); });
-
-    {
-        mCapture = screenshot();
-        // Child and BG blended.
-        mCapture->checkPixel(0, 0, 95, 64, 95);
-    }
-}
-
-TEST_F(ChildLayerTest, ReparentChildren) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        mCapture = screenshot();
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // But 10 pixels in we should see the child surface
-        mCapture->expectChildColor(74, 74);
-        // And 10 more pixels we should be back to the foreground surface
-        mCapture->expectFGColor(84, 84);
-    }
-
-    asTransaction([&](Transaction& t) {
-        t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle());
-    });
-
-    {
-        mCapture = screenshot();
-        mCapture->expectFGColor(64, 64);
-        // In reparenting we should have exposed the entire foreground surface.
-        mCapture->expectFGColor(74, 74);
-        // And the child layer should now begin at 10, 10 (since the BG
-        // layer is at (0, 0)).
-        mCapture->expectBGColor(9, 9);
-        mCapture->expectChildColor(10, 10);
-    }
-}
-
-TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) {
-    sp<SurfaceControl> mGrandChild =
-            createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
-    fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
-
-    {
-        SCOPED_TRACE("Grandchild visible");
-        ScreenCapture::captureScreen(&mCapture);
-        mCapture->checkPixel(64, 64, 111, 111, 111);
-    }
-
-    mChild.clear();
-
-    {
-        SCOPED_TRACE("After destroying child");
-        ScreenCapture::captureScreen(&mCapture);
-        mCapture->expectFGColor(64, 64);
-    }
-
-    asTransaction([&](Transaction& t) {
-         t.reparent(mGrandChild, mFGSurfaceControl->getHandle());
-    });
-
-    {
-        SCOPED_TRACE("After reparenting grandchild");
-        ScreenCapture::captureScreen(&mCapture);
-        mCapture->checkPixel(64, 64, 111, 111, 111);
-    }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenSameClient) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        mCapture = screenshot();
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // But 10 pixels in we should see the child surface
-        mCapture->expectChildColor(74, 74);
-        // And 10 more pixels we should be back to the foreground surface
-        mCapture->expectFGColor(84, 84);
-    }
-
-
-    asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
-    asTransaction([&](Transaction& t) { t.hide(mChild); });
-
-    // Since the child has the same client as the parent, it will not get
-    // detached and will be hidden.
-    {
-        mCapture = screenshot();
-        mCapture->expectFGColor(64, 64);
-        mCapture->expectFGColor(74, 74);
-        mCapture->expectFGColor(84, 84);
-    }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
-    sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
-    sp<SurfaceControl> mChildNewClient =
-            createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
-                          PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
-    ASSERT_TRUE(mChildNewClient->isValid());
-
-    fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
-
-    asTransaction([&](Transaction& t) {
-        t.hide(mChild);
-        t.show(mChildNewClient);
-        t.setPosition(mChildNewClient, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        mCapture = screenshot();
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // But 10 pixels in we should see the child surface
-        mCapture->expectChildColor(74, 74);
-        // And 10 more pixels we should be back to the foreground surface
-        mCapture->expectFGColor(84, 84);
-    }
-
-    asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
-    asTransaction([&](Transaction& t) { t.hide(mChildNewClient); });
-
-    // Nothing should have changed.
-    {
-        mCapture = screenshot();
-        mCapture->expectFGColor(64, 64);
-        mCapture->expectChildColor(74, 74);
-        mCapture->expectFGColor(84, 84);
-    }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenThenAttach) {
-    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
-    sp<SurfaceControl> childNewClient =
-            newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
-    ASSERT_TRUE(childNewClient != nullptr);
-    ASSERT_TRUE(childNewClient->isValid());
-
-    fillSurfaceRGBA8(childNewClient, 200, 200, 200);
-
-    Transaction()
-            .hide(mChild)
-            .show(childNewClient)
-            .setPosition(childNewClient, 10, 10)
-            .setPosition(mFGSurfaceControl, 64, 64)
-            .apply();
-
-    {
-        mCapture = screenshot();
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // But 10 pixels in we should see the child surface
-        mCapture->expectChildColor(74, 74);
-        // And 10 more pixels we should be back to the foreground surface
-        mCapture->expectFGColor(84, 84);
-    }
-
-    Transaction().detachChildren(mFGSurfaceControl).apply();
-    Transaction().hide(childNewClient).apply();
-
-    // Nothing should have changed.
-    {
-        mCapture = screenshot();
-        mCapture->expectFGColor(64, 64);
-        mCapture->expectChildColor(74, 74);
-        mCapture->expectFGColor(84, 84);
-    }
-
-    sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0);
-    fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32,
-                   32);
-    Transaction()
-            .setLayer(newParentSurface, INT32_MAX - 1)
-            .show(newParentSurface)
-            .setPosition(newParentSurface, 20, 20)
-            .reparent(childNewClient, newParentSurface->getHandle())
-            .apply();
-    {
-        mCapture = screenshot();
-        // Child is now hidden.
-        mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED);
-    }
-}
-TEST_F(ChildLayerTest, DetachChildrenWithDeferredTransaction) {
-    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
-    sp<SurfaceControl> childNewClient =
-            newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
-    ASSERT_TRUE(childNewClient != nullptr);
-    ASSERT_TRUE(childNewClient->isValid());
-
-    fillSurfaceRGBA8(childNewClient, 200, 200, 200);
-
-    Transaction()
-            .hide(mChild)
-            .show(childNewClient)
-            .setPosition(childNewClient, 10, 10)
-            .setPosition(mFGSurfaceControl, 64, 64)
-            .apply();
-
-    {
-        mCapture = screenshot();
-        Rect rect = Rect(74, 74, 84, 84);
-        mCapture->expectBorder(rect, Color{195, 63, 63, 255});
-        mCapture->expectColor(rect, Color{200, 200, 200, 255});
-    }
-
-    Transaction()
-            .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(),
-                                          mFGSurfaceControl->getSurface()->getNextFrameNumber())
-            .apply();
-    Transaction().detachChildren(mFGSurfaceControl).apply();
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32));
-
-    // BufferLayer can still dequeue buffers even though there's a detached layer with a
-    // deferred transaction.
-    {
-        SCOPED_TRACE("new buffer");
-        mCapture = screenshot();
-        Rect rect = Rect(74, 74, 84, 84);
-        mCapture->expectBorder(rect, Color::RED);
-        mCapture->expectColor(rect, Color{200, 200, 200, 255});
-    }
-}
-
-TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-    });
-
-    {
-        mCapture = screenshot();
-        // We've positioned the child in the top left.
-        mCapture->expectChildColor(0, 0);
-        // But it's only 10x15.
-        mCapture->expectFGColor(10, 15);
-    }
-
-    asTransaction([&](Transaction& t) {
-        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-        // We cause scaling by 2.
-        t.setSize(mFGSurfaceControl, 128, 128);
-    });
-
-    {
-        mCapture = screenshot();
-        // We've positioned the child in the top left.
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(10, 10);
-        mCapture->expectChildColor(19, 29);
-        // And now it should be scaled all the way to 20x30
-        mCapture->expectFGColor(20, 30);
-    }
-}
-
-// Regression test for b/37673612
-TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-    });
-
-    {
-        mCapture = screenshot();
-        // We've positioned the child in the top left.
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(9, 14);
-        // But it's only 10x15.
-        mCapture->expectFGColor(10, 15);
-    }
-    // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
-    // the WM specified state size.
-    asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
-    sp<Surface> s = mFGSurfaceControl->getSurface();
-    auto anw = static_cast<ANativeWindow*>(s.get());
-    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
-    native_window_set_buffers_dimensions(anw, 64, 128);
-    fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-    waitForPostedBuffers();
-
-    {
-        // The child should still be in the same place and not have any strange scaling as in
-        // b/37673612.
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectFGColor(10, 10);
-    }
-}
-
-// A child with a buffer transform from its parents should be cropped by its parent bounds.
-TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferTransform) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setSize(mChild, 100, 100);
-    });
-    fillSurfaceRGBA8(mChild, 200, 200, 200);
-
-    {
-        mCapture = screenshot();
-
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(63, 63);
-        mCapture->expectBGColor(64, 64);
-    }
-
-    asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
-    sp<Surface> s = mFGSurfaceControl->getSurface();
-    auto anw = static_cast<ANativeWindow*>(s.get());
-    // Apply a 90 transform on the buffer.
-    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
-    native_window_set_buffers_dimensions(anw, 64, 128);
-    fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-    waitForPostedBuffers();
-
-    // The child should be cropped by the new parent bounds.
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(99, 63);
-        mCapture->expectFGColor(100, 63);
-        mCapture->expectBGColor(128, 64);
-    }
-}
-
-// A child with a scale transform from its parents should be cropped by its parent bounds.
-TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferScale) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setSize(mChild, 200, 200);
-    });
-    fillSurfaceRGBA8(mChild, 200, 200, 200);
-
-    {
-        mCapture = screenshot();
-
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(63, 63);
-        mCapture->expectBGColor(64, 64);
-    }
-
-    asTransaction([&](Transaction& t) {
-        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-        // Set a scaling by 2.
-        t.setSize(mFGSurfaceControl, 128, 128);
-    });
-
-    // Child should inherit its parents scale but should be cropped by its parent bounds.
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(127, 127);
-        mCapture->expectBGColor(128, 128);
-    }
-}
-
-// Regression test for b/127368943
-// Child should ignore the buffer transform but apply parent scale transform.
-TEST_F(ChildLayerTest, ChildrenWithParentBufferTransformAndScale) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-    });
-
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(9, 14);
-        mCapture->expectFGColor(10, 15);
-    }
-
-    // Change the size of the foreground to 128 * 64 so we can test rotation as well.
-    asTransaction([&](Transaction& t) {
-        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-        t.setSize(mFGSurfaceControl, 128, 64);
-    });
-    sp<Surface> s = mFGSurfaceControl->getSurface();
-    auto anw = static_cast<ANativeWindow*>(s.get());
-    // Apply a 90 transform on the buffer and submit a buffer half the expected size so that we
-    // have an effective scale of 2.0 applied to the buffer along with a rotation transform.
-    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
-    native_window_set_buffers_dimensions(anw, 32, 64);
-    fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-    waitForPostedBuffers();
-
-    // The child should ignore the buffer transform but apply the 2.0 scale from parent.
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(19, 29);
-        mCapture->expectFGColor(20, 30);
-    }
-}
-
-TEST_F(ChildLayerTest, Bug36858924) {
-    // Destroy the child layer
-    mChild.clear();
-
-    // Now recreate it as hidden
-    mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888,
-                           ISurfaceComposerClient::eHidden, mFGSurfaceControl.get());
-
-    // Show the child layer in a deferred transaction
-    asTransaction([&](Transaction& t) {
-        t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
-                                       mFGSurfaceControl->getSurface()->getNextFrameNumber());
-        t.show(mChild);
-    });
-
-    // Render the foreground surface a few times
-    //
-    // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
-    // frame because SurfaceFlinger would never process the deferred transaction and would therefore
-    // never acquire/release the first buffer
-    ALOGI("Filling 1");
-    fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
-    ALOGI("Filling 2");
-    fillSurfaceRGBA8(mFGSurfaceControl, 0, 0, 255);
-    ALOGI("Filling 3");
-    fillSurfaceRGBA8(mFGSurfaceControl, 255, 0, 0);
-    ALOGI("Filling 4");
-    fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
-}
-
-TEST_F(ChildLayerTest, Reparent) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        mCapture = screenshot();
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // But 10 pixels in we should see the child surface
-        mCapture->expectChildColor(74, 74);
-        // And 10 more pixels we should be back to the foreground surface
-        mCapture->expectFGColor(84, 84);
-    }
-
-    asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); });
-
-    {
-        mCapture = screenshot();
-        mCapture->expectFGColor(64, 64);
-        // In reparenting we should have exposed the entire foreground surface.
-        mCapture->expectFGColor(74, 74);
-        // And the child layer should now begin at 10, 10 (since the BG
-        // layer is at (0, 0)).
-        mCapture->expectBGColor(9, 9);
-        mCapture->expectChildColor(10, 10);
-    }
-}
-
-TEST_F(ChildLayerTest, ReparentToNoParent) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        mCapture = screenshot();
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // But 10 pixels in we should see the child surface
-        mCapture->expectChildColor(74, 74);
-        // And 10 more pixels we should be back to the foreground surface
-        mCapture->expectFGColor(84, 84);
-    }
-    asTransaction([&](Transaction& t) { t.reparent(mChild, nullptr); });
-    {
-        mCapture = screenshot();
-        // The surface should now be offscreen.
-        mCapture->expectFGColor(64, 64);
-        mCapture->expectFGColor(74, 74);
-        mCapture->expectFGColor(84, 84);
-    }
-}
-
-TEST_F(ChildLayerTest, ReparentFromNoParent) {
-    sp<SurfaceControl> newSurface = createLayer(String8("New Surface"), 10, 10, 0);
-    ASSERT_TRUE(newSurface != nullptr);
-    ASSERT_TRUE(newSurface->isValid());
-
-    fillSurfaceRGBA8(newSurface, 63, 195, 63);
-    asTransaction([&](Transaction& t) {
-        t.hide(mChild);
-        t.show(newSurface);
-        t.setPosition(newSurface, 10, 10);
-        t.setLayer(newSurface, INT32_MAX - 2);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        mCapture = screenshot();
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // At 10, 10 we should see the new surface
-        mCapture->checkPixel(10, 10, 63, 195, 63);
-    }
-
-    asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); });
-
-    {
-        mCapture = screenshot();
-        // newSurface will now be a child of mFGSurface so it will be 10, 10 offset from
-        // mFGSurface, putting it at 74, 74.
-        mCapture->expectFGColor(64, 64);
-        mCapture->checkPixel(74, 74, 63, 195, 63);
-        mCapture->expectFGColor(84, 84);
-    }
-}
-
-TEST_F(ChildLayerTest, NestedChildren) {
-    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 10, 10,
-                                                  PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
-    fillSurfaceRGBA8(grandchild, 50, 50, 50);
-
-    {
-        mCapture = screenshot();
-        // Expect the grandchild to begin at 64, 64 because it's a child of mChild layer
-        // which begins at 64, 64
-        mCapture->checkPixel(64, 64, 50, 50, 50);
-    }
-}
-
-TEST_F(ChildLayerTest, ChildLayerRelativeLayer) {
-    sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 128, 128, 0);
-    fillSurfaceRGBA8(relative, 255, 255, 255);
-
-    Transaction t;
-    t.setLayer(relative, INT32_MAX)
-            .setRelativeLayer(mChild, relative->getHandle(), 1)
-            .setPosition(mFGSurfaceControl, 0, 0)
-            .apply(true);
-
-    // We expect that the child should have been elevated above our
-    // INT_MAX layer even though it's not a child of it.
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(9, 9);
-        mCapture->checkPixel(10, 10, 255, 255, 255);
-    }
-}
-
-class BoundlessLayerTest : public LayerUpdateTest {
-protected:
-    std::unique_ptr<ScreenCapture> mCapture;
-};
-
-// Verify setting a size on a buffer layer has no effect.
-TEST_F(BoundlessLayerTest, BufferLayerIgnoresSize) {
-    sp<SurfaceControl> bufferLayer =
-            createSurface(mClient, "BufferLayer", 45, 45, PIXEL_FORMAT_RGBA_8888, 0,
-                          mFGSurfaceControl.get());
-    ASSERT_TRUE(bufferLayer->isValid());
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::BLACK, 30, 30));
-    asTransaction([&](Transaction& t) { t.show(bufferLayer); });
-    {
-        mCapture = screenshot();
-        // Top left of background must now be visible
-        mCapture->expectBGColor(0, 0);
-        // Foreground Surface bounds must be color layer
-        mCapture->expectColor(Rect(64, 64, 94, 94), Color::BLACK);
-        // Buffer layer should not extend past buffer bounds
-        mCapture->expectFGColor(95, 95);
-    }
-}
-
-// Verify a boundless color layer will fill its parent bounds. The parent has a buffer size
-// which will crop the color layer.
-TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentBufferBounds) {
-    sp<SurfaceControl> colorLayer =
-            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
-                          ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
-    ASSERT_TRUE(colorLayer->isValid());
-    asTransaction([&](Transaction& t) {
-        t.setColor(colorLayer, half3{0, 0, 0});
-        t.show(colorLayer);
-    });
-    {
-        mCapture = screenshot();
-        // Top left of background must now be visible
-        mCapture->expectBGColor(0, 0);
-        // Foreground Surface bounds must be color layer
-        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
-        // Color layer should not extend past foreground bounds
-        mCapture->expectBGColor(129, 129);
-    }
-}
-
-// Verify a boundless color layer will fill its parent bounds. The parent has no buffer but has
-// a crop which will be used to crop the color layer.
-TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentCropBounds) {
-    sp<SurfaceControl> cropLayer = createSurface(mClient, "CropLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                                 0 /* flags */, mFGSurfaceControl.get());
-    ASSERT_TRUE(cropLayer->isValid());
-    sp<SurfaceControl> colorLayer =
-            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
-                          ISurfaceComposerClient::eFXSurfaceColor, cropLayer.get());
-    ASSERT_TRUE(colorLayer->isValid());
-    asTransaction([&](Transaction& t) {
-        t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10));
-        t.setColor(colorLayer, half3{0, 0, 0});
-        t.show(cropLayer);
-        t.show(colorLayer);
-    });
-    {
-        mCapture = screenshot();
-        // Top left of background must now be visible
-        mCapture->expectBGColor(0, 0);
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // 5 pixels from the foreground we should see the child surface
-        mCapture->expectColor(Rect(69, 69, 74, 74), Color::BLACK);
-        // 10 pixels from the foreground we should be back to the foreground surface
-        mCapture->expectFGColor(74, 74);
-    }
-}
-
-// Verify for boundless layer with no children, their transforms have no effect.
-TEST_F(BoundlessLayerTest, BoundlessColorLayerTransformHasNoEffect) {
-    sp<SurfaceControl> colorLayer =
-            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
-                          ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get());
-    ASSERT_TRUE(colorLayer->isValid());
-    asTransaction([&](Transaction& t) {
-        t.setPosition(colorLayer, 320, 320);
-        t.setMatrix(colorLayer, 2, 0, 0, 2);
-        t.setColor(colorLayer, half3{0, 0, 0});
-        t.show(colorLayer);
-    });
-    {
-        mCapture = screenshot();
-        // Top left of background must now be visible
-        mCapture->expectBGColor(0, 0);
-        // Foreground Surface bounds must be color layer
-        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
-        // Color layer should not extend past foreground bounds
-        mCapture->expectBGColor(129, 129);
-    }
-}
-
-// Verify for boundless layer with children, their transforms have an effect.
-TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerCanSetTransform) {
-    sp<SurfaceControl> boundlessLayerRightShift =
-            createSurface(mClient, "BoundlessLayerRightShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
-                          0 /* flags */, mFGSurfaceControl.get());
-    ASSERT_TRUE(boundlessLayerRightShift->isValid());
-    sp<SurfaceControl> boundlessLayerDownShift =
-            createSurface(mClient, "BoundlessLayerLeftShift", 0, 0, PIXEL_FORMAT_RGBA_8888,
-                          0 /* flags */, boundlessLayerRightShift.get());
-    ASSERT_TRUE(boundlessLayerDownShift->isValid());
-    sp<SurfaceControl> colorLayer =
-            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
-                          ISurfaceComposerClient::eFXSurfaceColor, boundlessLayerDownShift.get());
-    ASSERT_TRUE(colorLayer->isValid());
-    asTransaction([&](Transaction& t) {
-        t.setPosition(boundlessLayerRightShift, 32, 0);
-        t.show(boundlessLayerRightShift);
-        t.setPosition(boundlessLayerDownShift, 0, 32);
-        t.show(boundlessLayerDownShift);
-        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
-        t.setColor(colorLayer, half3{0, 0, 0});
-        t.show(colorLayer);
-    });
-    {
-        mCapture = screenshot();
-        // Top left of background must now be visible
-        mCapture->expectBGColor(0, 0);
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // Foreground Surface bounds must be color layer
-        mCapture->expectColor(Rect(96, 96, 128, 128), Color::BLACK);
-        // Color layer should not extend past foreground bounds
-        mCapture->expectBGColor(129, 129);
-    }
-}
-
-// Verify child layers do not get clipped if they temporarily move into the negative
-// coordinate space as the result of an intermediate transformation.
-TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) {
-    sp<SurfaceControl> boundlessLayer =
-            mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                   0 /* flags */, mFGSurfaceControl.get());
-    ASSERT_TRUE(boundlessLayer != nullptr);
-    ASSERT_TRUE(boundlessLayer->isValid());
-    sp<SurfaceControl> colorLayer =
-            mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                   ISurfaceComposerClient::eFXSurfaceColor, boundlessLayer.get());
-    ASSERT_TRUE(colorLayer != nullptr);
-    ASSERT_TRUE(colorLayer->isValid());
-    asTransaction([&](Transaction& t) {
-        // shift child layer off bounds. If this layer was not boundless, we will
-        // expect the child layer to be cropped.
-        t.setPosition(boundlessLayer, 32, 32);
-        t.show(boundlessLayer);
-        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
-        // undo shift by parent
-        t.setPosition(colorLayer, -32, -32);
-        t.setColor(colorLayer, half3{0, 0, 0});
-        t.show(colorLayer);
-    });
-    {
-        mCapture = screenshot();
-        // Top left of background must now be visible
-        mCapture->expectBGColor(0, 0);
-        // Foreground Surface bounds must be color layer
-        mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK);
-        // Color layer should not extend past foreground bounds
-        mCapture->expectBGColor(129, 129);
-    }
-}
-
-// Verify for boundless root layers with children, their transforms have an effect.
-TEST_F(BoundlessLayerTest, RootBoundlessLayerCanSetTransform) {
-    sp<SurfaceControl> rootBoundlessLayer = createSurface(mClient, "RootBoundlessLayer", 0, 0,
-                                                          PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
-    ASSERT_TRUE(rootBoundlessLayer->isValid());
-    sp<SurfaceControl> colorLayer =
-            createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888,
-                          ISurfaceComposerClient::eFXSurfaceColor, rootBoundlessLayer.get());
-
-    ASSERT_TRUE(colorLayer->isValid());
-    asTransaction([&](Transaction& t) {
-        t.setLayer(rootBoundlessLayer, INT32_MAX - 1);
-        t.setPosition(rootBoundlessLayer, 32, 32);
-        t.show(rootBoundlessLayer);
-        t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64));
-        t.setColor(colorLayer, half3{0, 0, 0});
-        t.show(colorLayer);
-        t.hide(mFGSurfaceControl);
-    });
-    {
-        mCapture = screenshot();
-        // Top left of background must now be visible
-        mCapture->expectBGColor(0, 0);
-        // Top left of foreground must now be visible
-        mCapture->expectBGColor(31, 31);
-        // Foreground Surface bounds must be color layer
-        mCapture->expectColor(Rect(32, 32, 96, 96), Color::BLACK);
-        // Color layer should not extend past foreground bounds
-        mCapture->expectBGColor(97, 97);
-    }
-}
-
-class ScreenCaptureTest : public LayerUpdateTest {
-protected:
-    std::unique_ptr<ScreenCapture> mCapture;
-};
-
-TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
-    auto bgHandle = mBGSurfaceControl->getHandle();
-    ScreenCapture::captureLayers(&mCapture, bgHandle);
-    mCapture->expectBGColor(0, 0);
-    // Doesn't capture FG layer which is at 64, 64
-    mCapture->expectBGColor(64, 64);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child, 200, 200, 200);
-
-    SurfaceComposerClient::Transaction().show(child).apply(true);
-
-    // Captures mFGSurfaceControl layer and its child.
-    ScreenCapture::captureLayers(&mCapture, fgHandle);
-    mCapture->expectFGColor(10, 10);
-    mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child, 200, 200, 200);
-
-    SurfaceComposerClient::Transaction().show(child).apply(true);
-
-    // Captures mFGSurfaceControl's child
-    ScreenCapture::captureChildLayers(&mCapture, fgHandle);
-    mCapture->checkPixel(10, 10, 0, 0, 0);
-    mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerExclude) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child, 200, 200, 200);
-    sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
-                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child2, 200, 0, 200);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            .show(child2)
-            .setLayer(child, 1)
-            .setLayer(child2, 2)
-            .apply(true);
-
-    // Child2 would be visible but its excluded, so we should see child1 color instead.
-    ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
-    mCapture->checkPixel(10, 10, 0, 0, 0);
-    mCapture->checkPixel(0, 0, 200, 200, 200);
-}
-
-// Like the last test but verifies that children are also exclude.
-TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child, 200, 200, 200);
-    sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
-                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child2, 200, 0, 200);
-    sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10,
-                                              PIXEL_FORMAT_RGBA_8888, 0, child2.get());
-    fillSurfaceRGBA8(child2, 200, 0, 200);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            .show(child2)
-            .show(child3)
-            .setLayer(child, 1)
-            .setLayer(child2, 2)
-            .apply(true);
-
-    // Child2 would be visible but its excluded, so we should see child1 color instead.
-    ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
-    mCapture->checkPixel(10, 10, 0, 0, 0);
-    mCapture->checkPixel(0, 0, 200, 200, 200);
-}
-
-TEST_F(ScreenCaptureTest, CaptureTransparent) {
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
-    fillSurfaceRGBA8(child, 200, 200, 200);
-
-    SurfaceComposerClient::Transaction().show(child).apply(true);
-
-    auto childHandle = child->getHandle();
-
-    // Captures child
-    ScreenCapture::captureLayers(&mCapture, childHandle, {0, 0, 10, 20});
-    mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
-    // Area outside of child's bounds is transparent.
-    mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0});
-}
-
-TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    ASSERT_NE(nullptr, child.get()) << "failed to create surface";
-    sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0);
-    fillSurfaceRGBA8(child, 200, 200, 200);
-    fillSurfaceRGBA8(relative, 100, 100, 100);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            // Set relative layer above fg layer so should be shown above when computing all layers.
-            .setRelativeLayer(relative, fgHandle, 1)
-            .show(relative)
-            .apply(true);
-
-    // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured.
-    ScreenCapture::captureLayers(&mCapture, fgHandle);
-    mCapture->expectFGColor(10, 10);
-    mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10,
-                                                PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child, 200, 200, 200);
-    fillSurfaceRGBA8(relative, 100, 100, 100);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            // Set relative layer below fg layer but relative to child layer so it should be shown
-            // above child layer.
-            .setLayer(relative, -1)
-            .setRelativeLayer(relative, child->getHandle(), 1)
-            .show(relative)
-            .apply(true);
-
-    // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its
-    // relative value should be taken into account, placing it above child layer.
-    ScreenCapture::captureLayers(&mCapture, fgHandle);
-    mCapture->expectFGColor(10, 10);
-    // Relative layer is showing on top of child layer
-    mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
-}
-
-// In the following tests we verify successful skipping of a parent layer,
-// so we use the same verification logic and only change how we mutate
-// the parent layer to verify that various properties are ignored.
-class ScreenCaptureChildOnlyTest : public LayerUpdateTest {
-public:
-    void SetUp() override {
-        LayerUpdateTest::SetUp();
-
-        mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
-                               mFGSurfaceControl.get());
-        fillSurfaceRGBA8(mChild, 200, 200, 200);
-
-        SurfaceComposerClient::Transaction().show(mChild).apply(true);
-    }
-
-    void verify(std::function<void()> verifyStartingState) {
-        // Verify starting state before a screenshot is taken.
-        verifyStartingState();
-
-        // Verify child layer does not inherit any of the properties of its
-        // parent when its screenshot is captured.
-        auto fgHandle = mFGSurfaceControl->getHandle();
-        ScreenCapture::captureChildLayers(&mCapture, fgHandle);
-        mCapture->checkPixel(10, 10, 0, 0, 0);
-        mCapture->expectChildColor(0, 0);
-
-        // Verify all assumptions are still true after the screenshot is taken.
-        verifyStartingState();
-    }
-
-    std::unique_ptr<ScreenCapture> mCapture;
-    sp<SurfaceControl> mChild;
-};
-
-// Regression test b/76099859
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) {
-
-    SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
-
-    // Even though the parent is hidden we should still capture the child.
-
-    // Before and after reparenting, verify child is properly hidden
-    // when rendering full-screen.
-    verify([&] { screenshot()->expectBGColor(64, 64); });
-}
-
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
-    SurfaceComposerClient::Transaction()
-            .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1))
-            .apply(true);
-
-    // Even though the parent is cropped out we should still capture the child.
-
-    // Before and after reparenting, verify child is cropped by parent.
-    verify([&] { screenshot()->expectBGColor(65, 65); });
-}
-
-// Regression test b/124372894
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) {
-    SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true);
-
-    // We should not inherit the parent scaling.
-
-    // Before and after reparenting, verify child is properly scaled.
-    verify([&] { screenshot()->expectChildColor(80, 80); });
-}
-
-
-TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child, 200, 200, 200);
-
-    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
-                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
-
-    fillSurfaceRGBA8(grandchild, 50, 50, 50);
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            .setPosition(grandchild, 5, 5)
-            .show(grandchild)
-            .apply(true);
-
-    // Captures mFGSurfaceControl, its child, and the grandchild.
-    ScreenCapture::captureLayers(&mCapture, fgHandle);
-    mCapture->expectFGColor(10, 10);
-    mCapture->expectChildColor(0, 0);
-    mCapture->checkPixel(5, 5, 50, 50, 50);
-}
-
-TEST_F(ScreenCaptureTest, CaptureChildOnly) {
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child, 200, 200, 200);
-    auto childHandle = child->getHandle();
-
-    SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true);
-
-    // Captures only the child layer, and not the parent.
-    ScreenCapture::captureLayers(&mCapture, childHandle);
-    mCapture->expectChildColor(0, 0);
-    mCapture->expectChildColor(9, 9);
-}
-
-TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child, 200, 200, 200);
-    auto childHandle = child->getHandle();
-
-    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
-                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
-    fillSurfaceRGBA8(grandchild, 50, 50, 50);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            .setPosition(grandchild, 5, 5)
-            .show(grandchild)
-            .apply(true);
-
-    auto grandchildHandle = grandchild->getHandle();
-
-    // Captures only the grandchild.
-    ScreenCapture::captureLayers(&mCapture, grandchildHandle);
-    mCapture->checkPixel(0, 0, 50, 50, 50);
-    mCapture->checkPixel(4, 4, 50, 50, 50);
-}
-
-TEST_F(ScreenCaptureTest, CaptureCrop) {
-    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
-    sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
-                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
-
-    SurfaceComposerClient::Transaction()
-            .setLayer(redLayer, INT32_MAX - 1)
-            .show(redLayer)
-            .show(blueLayer)
-            .apply(true);
-
-    auto redLayerHandle = redLayer->getHandle();
-
-    // Capturing full screen should have both red and blue are visible.
-    ScreenCapture::captureLayers(&mCapture, redLayerHandle);
-    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
-    // red area below the blue area
-    mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
-    // red area to the right of the blue area
-    mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
-
-    const Rect crop = Rect(0, 0, 30, 30);
-    ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop);
-    // Capturing the cropped screen, cropping out the shown red area, should leave only the blue
-    // area visible.
-    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
-    mCapture->checkPixel(30, 30, 0, 0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureSize) {
-    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
-    sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30,
-                                                 PIXEL_FORMAT_RGBA_8888, 0, redLayer.get());
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
-
-    SurfaceComposerClient::Transaction()
-            .setLayer(redLayer, INT32_MAX - 1)
-            .show(redLayer)
-            .show(blueLayer)
-            .apply(true);
-
-    auto redLayerHandle = redLayer->getHandle();
-
-    // Capturing full screen should have both red and blue are visible.
-    ScreenCapture::captureLayers(&mCapture, redLayerHandle);
-    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
-    // red area below the blue area
-    mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
-    // red area to the right of the blue area
-    mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
-
-    ScreenCapture::captureLayers(&mCapture, redLayerHandle, Rect::EMPTY_RECT, 0.5);
-    // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
-    mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE);
-    // red area below the blue area
-    mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED);
-    // red area to the right of the blue area
-    mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED);
-    mCapture->checkPixel(30, 30, 0, 0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
-    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-
-    auto redLayerHandle = redLayer->getHandle();
-    redLayer.clear();
-    SurfaceComposerClient::Transaction().apply(true);
-
-    sp<GraphicBuffer> outBuffer;
-
-    // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(redLayerHandle, &outBuffer, Rect::EMPTY_RECT, 1.0));
-}
-
-
-class DereferenceSurfaceControlTest : public LayerTransactionTest {
-protected:
-    void SetUp() override {
-        LayerTransactionTest::SetUp();
-        bgLayer = createLayer("BG layer", 20, 20);
-        fillBufferQueueLayerColor(bgLayer, Color::RED, 20, 20);
-        fgLayer = createLayer("FG layer", 20, 20);
-        fillBufferQueueLayerColor(fgLayer, Color::BLUE, 20, 20);
-        Transaction().setLayer(fgLayer, mLayerZBase + 1).apply();
-        {
-            SCOPED_TRACE("before anything");
-            auto shot = screenshot();
-            shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE);
-        }
-    }
-    void TearDown() override {
-        LayerTransactionTest::TearDown();
-        bgLayer = 0;
-        fgLayer = 0;
-    }
-
-    sp<SurfaceControl> bgLayer;
-    sp<SurfaceControl> fgLayer;
-};
-
-TEST_F(DereferenceSurfaceControlTest, LayerNotInTransaction) {
-    fgLayer = nullptr;
-    {
-        SCOPED_TRACE("after setting null");
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, 20, 20), Color::RED);
-    }
-}
-
-TEST_F(DereferenceSurfaceControlTest, LayerInTransaction) {
-    auto transaction = Transaction().show(fgLayer);
-    fgLayer = nullptr;
-    {
-        SCOPED_TRACE("after setting null");
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE);
-    }
-}
-
-class MultiDisplayLayerBoundsTest : public LayerTransactionTest {
-protected:
-    virtual void SetUp() {
-        LayerTransactionTest::SetUp();
-        ASSERT_EQ(NO_ERROR, mClient->initCheck());
-
-        mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
-        SurfaceComposerClient::getDisplayInfo(mMainDisplay, &mMainDisplayInfo);
-
-        sp<IGraphicBufferConsumer> consumer;
-        BufferQueue::createBufferQueue(&mProducer, &consumer);
-        consumer->setConsumerName(String8("Virtual disp consumer"));
-        consumer->setDefaultBufferSize(mMainDisplayInfo.w, mMainDisplayInfo.h);
-    }
-
-    virtual void TearDown() {
-        SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
-        LayerTransactionTest::TearDown();
-        mColorLayer = 0;
-    }
-
-    void createDisplay(const Rect& layerStackRect, uint32_t layerStack) {
-        mVirtualDisplay =
-                SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
-        asTransaction([&](Transaction& t) {
-            t.setDisplaySurface(mVirtualDisplay, mProducer);
-            t.setDisplayLayerStack(mVirtualDisplay, layerStack);
-            t.setDisplayProjection(mVirtualDisplay, mMainDisplayInfo.orientation, layerStackRect,
-                                   Rect(mMainDisplayInfo.w, mMainDisplayInfo.h));
-        });
-    }
-
-    void createColorLayer(uint32_t layerStack) {
-        mColorLayer =
-                createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */,
-                              PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
-        ASSERT_TRUE(mColorLayer != nullptr);
-        ASSERT_TRUE(mColorLayer->isValid());
-        asTransaction([&](Transaction& t) {
-            t.setLayerStack(mColorLayer, layerStack);
-            t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
-            t.setLayer(mColorLayer, INT32_MAX - 2);
-            t.setColor(mColorLayer,
-                       half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
-                             mExpectedColor.b / 255.0f});
-            t.show(mColorLayer);
-        });
-    }
-
-    DisplayInfo mMainDisplayInfo;
-    sp<IBinder> mMainDisplay;
-    sp<IBinder> mVirtualDisplay;
-    sp<IGraphicBufferProducer> mProducer;
-    sp<SurfaceControl> mColorLayer;
-    Color mExpectedColor = {63, 63, 195, 255};
-};
-
-TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
-    createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 1 /* layerStack */);
-    createColorLayer(1 /* layerStack */);
-
-    asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
-
-    // Verify color layer does not render on main display.
-    std::unique_ptr<ScreenCapture> sc;
-    ScreenCapture::captureScreen(&sc, mMainDisplay);
-    sc->expectColor(Rect(10, 10, 40, 50), {0, 0, 0, 255});
-    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
-
-    // Verify color layer renders correctly on virtual display.
-    ScreenCapture::captureScreen(&sc, mVirtualDisplay);
-    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
-    sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 0});
-}
-
-TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) {
-    // Create a display and set its layer stack to the main display's layer stack so
-    // the contents of the main display are mirrored on to the virtual display.
-
-    // Assumption here is that the new mirrored display has the same viewport as the
-    // primary display that it is mirroring.
-    createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 0 /* layerStack */);
-    createColorLayer(0 /* layerStack */);
-
-    asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
-
-    // Verify color layer renders correctly on main display and it is mirrored on the
-    // virtual display.
-    std::unique_ptr<ScreenCapture> sc;
-    ScreenCapture::captureScreen(&sc, mMainDisplay);
-    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
-    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
-
-    ScreenCapture::captureScreen(&sc, mVirtualDisplay);
-    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
-    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
-}
-
-class DisplayActiveConfigTest : public ::testing::Test {
-protected:
-    void SetUp() override {
-        mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
-        SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &mDisplayconfigs);
-        EXPECT_GT(mDisplayconfigs.size(), 0);
-
-        // set display power to on to make sure config can be changed
-        SurfaceComposerClient::setDisplayPowerMode(mDisplayToken, HWC_POWER_MODE_NORMAL);
-    }
-
-    sp<IBinder> mDisplayToken;
-    Vector<DisplayInfo> mDisplayconfigs;
-};
-
-TEST_F(DisplayActiveConfigTest, allConfigsAllowed) {
-    std::vector<int32_t> allowedConfigs;
-
-    // Add all configs to the allowed configs
-    for (int i = 0; i < mDisplayconfigs.size(); i++) {
-        allowedConfigs.push_back(i);
-    }
-
-    status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs);
-    EXPECT_EQ(res, NO_ERROR);
-
-    std::vector<int32_t> outConfigs;
-    res = SurfaceComposerClient::getAllowedDisplayConfigs(mDisplayToken, &outConfigs);
-    EXPECT_EQ(res, NO_ERROR);
-    EXPECT_EQ(allowedConfigs, outConfigs);
-}
-
-TEST_F(DisplayActiveConfigTest, changeAllowedConfig) {
-    // we need at least 2 configs available for this test
-    if (mDisplayconfigs.size() <= 1) return;
-
-    int activeConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken);
-
-    // We want to set the allowed config to everything but the active config
-    std::vector<int32_t> allowedConfigs;
-    for (int i = 0; i < mDisplayconfigs.size(); i++) {
-        if (i != activeConfig) {
-            allowedConfigs.push_back(i);
-        }
-    }
-
-    status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs);
-    EXPECT_EQ(res, NO_ERROR);
-
-    // Allow some time for the config change
-    std::this_thread::sleep_for(200ms);
-
-    int newActiveConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken);
-    EXPECT_NE(activeConfig, newActiveConfig);
-
-    // Make sure the new config is part of allowed config
-    EXPECT_TRUE(std::find(allowedConfigs.begin(), allowedConfigs.end(), newActiveConfig) !=
-                allowedConfigs.end());
-}
-
-class RelativeZTest : public LayerTransactionTest {
-protected:
-    virtual void SetUp() {
-        LayerTransactionTest::SetUp();
-        ASSERT_EQ(NO_ERROR, mClient->initCheck());
-
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
-        ASSERT_FALSE(display == nullptr);
-
-        // Back layer
-        mBackgroundLayer = createColorLayer("Background layer", Color::RED);
-
-        // Front layer
-        mForegroundLayer = createColorLayer("Foreground layer", Color::GREEN);
-
-        asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
-            t.setLayer(mBackgroundLayer, INT32_MAX - 2).show(mBackgroundLayer);
-            t.setLayer(mForegroundLayer, INT32_MAX - 1).show(mForegroundLayer);
-        });
-    }
-
-    virtual void TearDown() {
-        LayerTransactionTest::TearDown();
-        mBackgroundLayer = 0;
-        mForegroundLayer = 0;
-    }
-
-    sp<SurfaceControl> mBackgroundLayer;
-    sp<SurfaceControl> mForegroundLayer;
-};
-
-// When a layer is reparented offscreen, remove relative z order if the relative parent
-// is still onscreen so that the layer is not drawn.
-TEST_F(RelativeZTest, LayerRemoved) {
-    std::unique_ptr<ScreenCapture> sc;
-
-    // Background layer (RED)
-    //   Child layer (WHITE) (relative to foregroud layer)
-    // Foregroud layer (GREEN)
-    sp<SurfaceControl> childLayer =
-            createColorLayer("Child layer", Color::BLUE, mBackgroundLayer.get());
-
-    Transaction{}
-            .setRelativeLayer(childLayer, mForegroundLayer->getHandle(), 1)
-            .show(childLayer)
-            .apply();
-
-    {
-        // The childLayer should be in front of the FG control.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b);
-    }
-
-    // Background layer (RED)
-    // Foregroud layer (GREEN)
-    Transaction{}.reparent(childLayer, nullptr).apply();
-
-    // Background layer (RED)
-    //   Child layer (WHITE)
-    // Foregroud layer (GREEN)
-    Transaction{}.reparent(childLayer, mBackgroundLayer->getHandle()).apply();
-
-    {
-        // The relative z info for child layer should be reset, leaving FG control on top.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
-    }
-}
-
-// When a layer is reparented offscreen, preseve relative z order if the relative parent
-// is also offscreen. Regression test b/132613412
-TEST_F(RelativeZTest, LayerRemovedOffscreenRelativeParent) {
-    std::unique_ptr<ScreenCapture> sc;
-
-    // Background layer (RED)
-    // Foregroud layer (GREEN)
-    //   child level 1 (WHITE)
-    //     child level 2a (BLUE)
-    //       child level 3 (GREEN) (relative to child level 2b)
-    //     child level 2b (BLACK)
-    sp<SurfaceControl> childLevel1 =
-            createColorLayer("child level 1", Color::WHITE, mForegroundLayer.get());
-    sp<SurfaceControl> childLevel2a =
-            createColorLayer("child level 2a", Color::BLUE, childLevel1.get());
-    sp<SurfaceControl> childLevel2b =
-            createColorLayer("child level 2b", Color::BLACK, childLevel1.get());
-    sp<SurfaceControl> childLevel3 =
-            createColorLayer("child level 3", Color::GREEN, childLevel2a.get());
-
-    Transaction{}
-            .setRelativeLayer(childLevel3, childLevel2b->getHandle(), 1)
-            .show(childLevel2a)
-            .show(childLevel2b)
-            .show(childLevel3)
-            .apply();
-
-    {
-        // The childLevel3 should be in front of childLevel2b.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
-    }
-
-    // Background layer (RED)
-    // Foregroud layer (GREEN)
-    Transaction{}.reparent(childLevel1, nullptr).apply();
-
-    // Background layer (RED)
-    // Foregroud layer (GREEN)
-    //   child level 1 (WHITE)
-    //     child level 2 back (BLUE)
-    //       child level 3 (GREEN) (relative to child level 2b)
-    //     child level 2 front (BLACK)
-    Transaction{}.reparent(childLevel1, mForegroundLayer->getHandle()).apply();
-
-    {
-        // Nothing should change at this point since relative z info was preserved.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
-    }
-}
-
-// This test ensures that when we drop an app buffer in SurfaceFlinger, we merge
-// the dropped buffer's damage region into the next buffer's damage region. If
-// we don't do this, we'll report an incorrect damage region to hardware
-// composer, resulting in broken rendering. This test checks the BufferQueue
-// case.
-//
-// Unfortunately, we don't currently have a way to inspect the damage region
-// SurfaceFlinger sends to hardware composer from a test, so this test requires
-// the dev to manually watch the device's screen during the test to spot broken
-// rendering. Because the results can't be automatically verified, this test is
-// marked disabled.
-TEST_F(LayerTransactionTest, DISABLED_BufferQueueLayerMergeDamageRegionWhenDroppingBuffers) {
-    const int width = mDisplayWidth;
-    const int height = mDisplayHeight;
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
-    const auto producer = layer->getIGraphicBufferProducer();
-    const sp<IProducerListener> dummyListener(new DummyProducerListener);
-    IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
-    ASSERT_EQ(OK,
-              producer->connect(dummyListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput));
-
-    std::map<int, sp<GraphicBuffer>> slotMap;
-    auto slotToBuffer = [&](int slot, sp<GraphicBuffer>* buf) {
-        ASSERT_NE(nullptr, buf);
-        const auto iter = slotMap.find(slot);
-        ASSERT_NE(slotMap.end(), iter);
-        *buf = iter->second;
-    };
-
-    auto dequeue = [&](int* outSlot) {
-        ASSERT_NE(nullptr, outSlot);
-        *outSlot = -1;
-        int slot;
-        sp<Fence> fence;
-        uint64_t age;
-        FrameEventHistoryDelta timestamps;
-        const status_t dequeueResult =
-                producer->dequeueBuffer(&slot, &fence, width, height, PIXEL_FORMAT_RGBA_8888,
-                                        GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                                        &age, &timestamps);
-        if (dequeueResult == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
-            sp<GraphicBuffer> newBuf;
-            ASSERT_EQ(OK, producer->requestBuffer(slot, &newBuf));
-            ASSERT_NE(nullptr, newBuf.get());
-            slotMap[slot] = newBuf;
-        } else {
-            ASSERT_EQ(OK, dequeueResult);
-        }
-        *outSlot = slot;
-    };
-
-    auto queue = [&](int slot, const Region& damage, nsecs_t displayTime) {
-        IGraphicBufferProducer::QueueBufferInput input(
-                /*timestamp=*/displayTime, /*isAutoTimestamp=*/false, HAL_DATASPACE_UNKNOWN,
-                /*crop=*/Rect::EMPTY_RECT, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
-                /*transform=*/0, Fence::NO_FENCE);
-        input.setSurfaceDamage(damage);
-        IGraphicBufferProducer::QueueBufferOutput output;
-        ASSERT_EQ(OK, producer->queueBuffer(slot, input, &output));
-    };
-
-    auto fillAndPostBuffers = [&](const Color& color) {
-        int slot1;
-        ASSERT_NO_FATAL_FAILURE(dequeue(&slot1));
-        int slot2;
-        ASSERT_NO_FATAL_FAILURE(dequeue(&slot2));
-
-        sp<GraphicBuffer> buf1;
-        ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot1, &buf1));
-        sp<GraphicBuffer> buf2;
-        ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot2, &buf2));
-        fillGraphicBufferColor(buf1, Rect(width, height), color);
-        fillGraphicBufferColor(buf2, Rect(width, height), color);
-
-        const auto displayTime = systemTime() + milliseconds_to_nanoseconds(100);
-        ASSERT_NO_FATAL_FAILURE(queue(slot1, Region::INVALID_REGION, displayTime));
-        ASSERT_NO_FATAL_FAILURE(
-                queue(slot2, Region(Rect(width / 3, height / 3, 2 * width / 3, 2 * height / 3)),
-                      displayTime));
-    };
-
-    const auto startTime = systemTime();
-    const std::array<Color, 3> colors = {Color::RED, Color::GREEN, Color::BLUE};
-    int colorIndex = 0;
-    while (nanoseconds_to_seconds(systemTime() - startTime) < 10) {
-        ASSERT_NO_FATAL_FAILURE(fillAndPostBuffers(colors[colorIndex++ % colors.size()]));
-        std::this_thread::sleep_for(1s);
-    }
-
-    ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU));
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 2feff45..2861013 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -10,32 +10,38 @@
     ],
     shared_libs: [
         "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.3",
+        "android.hardware.graphics.composer@2.4",
+        "android.hardware.graphics.composer@2.1-resources",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@3.0",
+        "android.hardware.graphics.mapper@4.0",
         "android.hardware.power@1.3",
         "libbase",
         "libbinder",
         "libcutils",
         "libfmq",
         "libgui",
-        "libhardware",
         "libhidlbase",
         "liblayers_proto",
         "liblog",
         "libnativewindow",
         "libsync",
-        "libtimestats_proto",
+        "libtimestats",
         "libui",
         "libutils",
     ],
     static_libs: [
+        "libcompositionengine",
         "libgmock",
+        "libperfetto_client_experimental",
         "librenderengine",
         "libtrace_proto",
     ],
     header_libs: [
-        "android.hardware.graphics.composer@2.1-command-buffer",
-        "android.hardware.graphics.composer@2.1-hal",
+        "android.hardware.graphics.composer@2.4-command-buffer",
+        "android.hardware.graphics.composer@2.4-hal",
         "libsurfaceflinger_headers",
     ],
 }
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index eeb6efe..5cbf2ef 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "FakeComposer"
@@ -146,6 +150,7 @@
 
 FakeComposerClient::FakeComposerClient()
       : mEventCallback(nullptr),
+        mEventCallback_2_4(nullptr),
         mCurrentConfig(NULL_DISPLAY_CONFIG),
         mVsyncEnabled(false),
         mLayers(),
@@ -165,6 +170,9 @@
 
 void FakeComposerClient::registerEventCallback(EventCallback* callback) {
     ALOGV("registerEventCallback");
+    LOG_FATAL_IF(mEventCallback_2_4 != nullptr,
+                 "already registered using registerEventCallback_2_4");
+
     mEventCallback = callback;
     if (mEventCallback) {
         mEventCallback->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
@@ -179,12 +187,16 @@
 void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) {
     if (mEventCallback) {
         mEventCallback->onHotplug(display, state);
+    } else if (mEventCallback_2_4) {
+        mEventCallback_2_4->onHotplug(display, state);
     }
 }
 
 void FakeComposerClient::refreshDisplay(Display display) {
     if (mEventCallback) {
         mEventCallback->onRefresh(display);
+    } else if (mEventCallback_2_4) {
+        mEventCallback_2_4->onRefresh(display);
     }
 }
 
@@ -193,33 +205,37 @@
     return 1;
 }
 
-Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/,
-                                               PixelFormat* /*format*/, Display* /*outDisplay*/) {
+V2_1::Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/,
+                                                     V1_0::PixelFormat* /*format*/,
+                                                     Display* /*outDisplay*/) {
     ALOGV("createVirtualDisplay");
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) {
+V2_1::Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) {
     ALOGV("destroyVirtualDisplay");
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) {
+V2_1::Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) {
     ALOGV("createLayer");
     *outLayer = mLayers.size();
     auto newLayer = std::make_unique<LayerImpl>();
     mLayers.push_back(std::move(newLayer));
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) {
+V2_1::Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) {
     ALOGV("destroyLayer");
     mLayers[layer]->mValid = false;
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::getActiveConfig(Display /*display*/, Config* outConfig) {
+V2_1::Error FakeComposerClient::getActiveConfig(Display display, Config* outConfig) {
     ALOGV("getActiveConfig");
+    if (mMockHal) {
+        return mMockHal->getActiveConfig(display, outConfig);
+    }
 
     // TODO Assert outConfig != nullptr
 
@@ -227,30 +243,480 @@
     // IComposerClient::getActiveConfig, but returning BAD_CONFIG
     // seems to not fit SurfaceFlinger plans. See version 2 below.
     // if (mCurrentConfig == NULL_DISPLAY_CONFIG) {
-    //     return Error::BAD_CONFIG;
+    //     return V2_1::Error::BAD_CONFIG;
     // }
     //*outConfig = mCurrentConfig;
     *outConfig = 1; // Very special config for you my friend
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/,
-                                                 uint32_t /*height*/, PixelFormat /*format*/,
-                                                 Dataspace /*dataspace*/) {
+V2_1::Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/,
+                                                       uint32_t /*height*/,
+                                                       V1_0::PixelFormat /*format*/,
+                                                       V1_0::Dataspace /*dataspace*/) {
     ALOGV("getClientTargetSupport");
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::getColorModes(Display /*display*/, hidl_vec<ColorMode>* /*outModes*/) {
+V2_1::Error FakeComposerClient::getColorModes(Display /*display*/,
+                                              hidl_vec<V1_0::ColorMode>* /*outModes*/) {
     ALOGV("getColorModes");
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::getDisplayAttribute(Display display, Config config,
-                                              IComposerClient::Attribute attribute,
-                                              int32_t* outValue) {
+V2_1::Error FakeComposerClient::getDisplayAttribute(Display display, Config config,
+                                                    V2_1::IComposerClient::Attribute attribute,
+                                                    int32_t* outValue) {
+    auto tmpError =
+            getDisplayAttribute_2_4(display, config,
+                                    static_cast<IComposerClient::Attribute>(attribute), outValue);
+    return static_cast<V2_1::Error>(tmpError);
+}
+
+V2_1::Error FakeComposerClient::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) {
+    ALOGV("getDisplayConfigs");
+    if (mMockHal) {
+        return mMockHal->getDisplayConfigs(display, outConfigs);
+    }
+
+    // TODO assert display == 1, outConfigs != nullptr
+
+    outConfigs->resize(1);
+    (*outConfigs)[0] = 1;
+
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) {
+    ALOGV("getDisplayName");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::getDisplayType(Display /*display*/,
+                                               IComposerClient::DisplayType* outType) {
+    ALOGV("getDisplayType");
+    // TODO: This setting nothing on the output had no effect on initial trials. Is first display
+    // assumed to be physical?
+    *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL);
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) {
+    ALOGV("getDozeSupport");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::getHdrCapabilities(Display /*display*/,
+                                                   hidl_vec<V1_0::Hdr>* /*outTypes*/,
+                                                   float* /*outMaxLuminance*/,
+                                                   float* /*outMaxAverageLuminance*/,
+                                                   float* /*outMinLuminance*/) {
+    ALOGV("getHdrCapabilities");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setActiveConfig(Display display, Config config) {
+    ALOGV("setActiveConfig");
+    if (mMockHal) {
+        return mMockHal->setActiveConfig(display, config);
+    }
+    mCurrentConfig = config;
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setColorMode(Display /*display*/, V1_0::ColorMode /*mode*/) {
+    ALOGV("setColorMode");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setPowerMode(Display /*display*/,
+                                             V2_1::IComposerClient::PowerMode /*mode*/) {
+    ALOGV("setPowerMode");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setVsyncEnabled(Display /*display*/,
+                                                IComposerClient::Vsync enabled) {
+    mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE);
+    ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/,
+                                                  int32_t /*hint*/) {
+    ALOGV("setColorTransform");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/,
+                                                int32_t /*acquireFence*/, int32_t /*dataspace*/,
+                                                const std::vector<hwc_rect_t>& /*damage*/) {
+    ALOGV("setClientTarget");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/,
+                                                int32_t /*releaseFence*/) {
+    ALOGV("setOutputBuffer");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::validateDisplay(
+        Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
+        std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
+        uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
+        std::vector<uint32_t>* /*outRequestMasks*/) {
+    ALOGV("validateDisplay");
+    // TODO: Assume touching nothing means All Korrekt!
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) {
+    ALOGV("acceptDisplayChanges");
+    // Didn't ask for changes because software is omnipotent.
+    return V2_1::Error::NONE;
+}
+
+bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) {
+    return a->z <= b->z;
+}
+
+V2_1::Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/,
+                                               std::vector<Layer>* /*outLayers*/,
+                                               std::vector<int32_t>* /*outReleaseFences*/) {
+    ALOGV("presentDisplay");
+    // TODO Leaving layers and their fences out for now. Doing so
+    // means that we've already processed everything. Important to
+    // test that the fences are respected, though. (How?)
+
+    std::unique_ptr<Frame> newFrame(new Frame);
+    for (uint64_t layer = 0; layer < mLayers.size(); layer++) {
+        const LayerImpl& layerImpl = *mLayers[layer];
+
+        if (!layerImpl.mValid) continue;
+
+        auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ);
+        newFrame->rectangles.push_back(std::move(rect));
+    }
+    std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering);
+    {
+        Mutex::Autolock _l(mStateMutex);
+        mFrames.push_back(std::move(newFrame));
+        mFramesAvailable.broadcast();
+    }
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
+                                                       int32_t /*x*/, int32_t /*y*/) {
+    ALOGV("setLayerCursorPosition");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer,
+                                               buffer_handle_t buffer, int32_t acquireFence) {
+    ALOGV("setLayerBuffer");
+    LayerImpl& l = getLayerImpl(layer);
+    if (buffer != l.mRenderState.mBuffer) {
+        l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not?
+    }
+    l.mRenderState.mBuffer = buffer;
+    l.mRenderState.mAcquireFence = acquireFence;
+
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/,
+                                                      const std::vector<hwc_rect_t>& /*damage*/) {
+    ALOGV("setLayerSurfaceDamage");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) {
+    ALOGV("setLayerBlendMode");
+    getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode);
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer,
+                                              IComposerClient::Color color) {
+    ALOGV("setLayerColor");
+    getLayerImpl(layer).mRenderState.mLayerColor.r = color.r;
+    getLayerImpl(layer).mRenderState.mLayerColor.g = color.g;
+    getLayerImpl(layer).mRenderState.mLayerColor.b = color.b;
+    getLayerImpl(layer).mRenderState.mLayerColor.a = color.a;
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/,
+                                                        int32_t /*type*/) {
+    ALOGV("setLayerCompositionType");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/,
+                                                  int32_t /*dataspace*/) {
+    ALOGV("setLayerDataspace");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer,
+                                                     const hwc_rect_t& frame) {
+    ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right,
+          frame.bottom);
+    getLayerImpl(layer).mRenderState.mDisplayFrame = frame;
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) {
+    ALOGV("setLayerPlaneAlpha");
+    getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha;
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/,
+                                                       buffer_handle_t /*stream*/) {
+    ALOGV("setLayerSidebandStream");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer,
+                                                   const hwc_frect_t& crop) {
+    ALOGV("setLayerSourceCrop");
+    getLayerImpl(layer).mRenderState.mSourceCrop = crop;
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer,
+                                                  int32_t transform) {
+    ALOGV("setLayerTransform");
+    getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform);
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer,
+                                                      const std::vector<hwc_rect_t>& visible) {
+    ALOGV("setLayerVisibleRegion");
+    getLayerImpl(layer).mRenderState.mVisibleRegion = visible;
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) {
+    ALOGV("setLayerZOrder");
+    getLayerImpl(layer).mZ = z;
+    return V2_1::Error::NONE;
+}
+
+// Composer 2.2
+V2_1::Error FakeComposerClient::getPerFrameMetadataKeys(
+        Display /*display*/, std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerPerFrameMetadata(
+        Display /*display*/, Layer /*layer*/,
+        const std::vector<V2_2::IComposerClient::PerFrameMetadata>& /*metadata*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getReadbackBufferAttributes(
+        Display /*display*/, graphics::common::V1_1::PixelFormat* /*outFormat*/,
+        graphics::common::V1_1::Dataspace* /*outDataspace*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setReadbackBuffer(Display /*display*/,
+                                                  const native_handle_t* /*bufferHandle*/,
+                                                  android::base::unique_fd /*fenceFd*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getReadbackBufferFence(Display /*display*/,
+                                                       android::base::unique_fd* /*outFenceFd*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::createVirtualDisplay_2_2(
+        uint32_t /*width*/, uint32_t /*height*/, graphics::common::V1_1::PixelFormat* /*format*/,
+        Display* /*outDisplay*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+V2_1::Error FakeComposerClient::getClientTargetSupport_2_2(
+        Display /*display*/, uint32_t /*width*/, uint32_t /*height*/,
+        graphics::common::V1_1::PixelFormat /*format*/,
+        graphics::common::V1_1::Dataspace /*dataspace*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setPowerMode_2_2(Display /*display*/,
+                                                 V2_2::IComposerClient::PowerMode /*mode*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerFloatColor(Display /*display*/, Layer /*layer*/,
+                                                   V2_2::IComposerClient::FloatColor /*color*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getColorModes_2_2(
+        Display /*display*/, hidl_vec<graphics::common::V1_1::ColorMode>* /*outModes*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getRenderIntents(
+        Display /*display*/, graphics::common::V1_1::ColorMode /*mode*/,
+        std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setColorMode_2_2(Display /*display*/,
+                                                 graphics::common::V1_1::ColorMode /*mode*/,
+                                                 graphics::common::V1_1::RenderIntent /*intent*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+std::array<float, 16> FakeComposerClient::getDataspaceSaturationMatrix(
+        graphics::common::V1_1::Dataspace /*dataspace*/) {
+    return {};
+}
+
+// Composer 2.3
+V2_1::Error FakeComposerClient::getPerFrameMetadataKeys_2_3(
+        Display /*display*/, std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setColorMode_2_3(Display /*display*/,
+                                                 graphics::common::V1_2::ColorMode /*mode*/,
+                                                 graphics::common::V1_1::RenderIntent /*intent*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getRenderIntents_2_3(
+        Display /*display*/, graphics::common::V1_2::ColorMode /*mode*/,
+        std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getColorModes_2_3(
+        Display /*display*/, hidl_vec<graphics::common::V1_2::ColorMode>* /*outModes*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getClientTargetSupport_2_3(
+        Display /*display*/, uint32_t /*width*/, uint32_t /*height*/,
+        graphics::common::V1_2::PixelFormat /*format*/,
+        graphics::common::V1_2::Dataspace /*dataspace*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getReadbackBufferAttributes_2_3(
+        Display /*display*/, graphics::common::V1_2::PixelFormat* /*outFormat*/,
+        graphics::common::V1_2::Dataspace* /*outDataspace*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getHdrCapabilities_2_3(
+        Display /*display*/, hidl_vec<graphics::common::V1_2::Hdr>* /*outTypes*/,
+        float* /*outMaxLuminance*/, float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerPerFrameMetadata_2_3(
+        Display /*display*/, Layer /*layer*/,
+        const std::vector<V2_3::IComposerClient::PerFrameMetadata>& /*metadata*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayIdentificationData(Display /*display*/,
+                                                             uint8_t* /*outPort*/,
+                                                             std::vector<uint8_t>* /*outData*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerColorTransform(Display /*display*/, Layer /*layer*/,
+                                                       const float* /*matrix*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayedContentSamplingAttributes(
+        uint64_t /*display*/, graphics::common::V1_2::PixelFormat& /*format*/,
+        graphics::common::V1_2::Dataspace& /*dataspace*/,
+        hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& /*componentMask*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setDisplayedContentSamplingEnabled(
+        uint64_t /*display*/, V2_3::IComposerClient::DisplayedContentSampling /*enable*/,
+        hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> /*componentMask*/,
+        uint64_t /*maxFrames*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayedContentSample(
+        uint64_t /*display*/, uint64_t /*maxFrames*/, uint64_t /*timestamp*/,
+        uint64_t& /*frameCount*/, hidl_vec<uint64_t>& /*sampleComponent0*/,
+        hidl_vec<uint64_t>& /*sampleComponent1*/, hidl_vec<uint64_t>& /*sampleComponent2*/,
+        hidl_vec<uint64_t>& /*sampleComponent3*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayCapabilities(
+        Display /*display*/,
+        std::vector<V2_3::IComposerClient::DisplayCapability>* /*outCapabilities*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerPerFrameMetadataBlobs(
+        Display /*display*/, Layer /*layer*/,
+        std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& /*blobs*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayBrightnessSupport(Display /*display*/,
+                                                            bool* /*outSupport*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setDisplayBrightness(Display /*display*/, float /*brightness*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+// Composer 2.4
+void FakeComposerClient::registerEventCallback_2_4(EventCallback_2_4* callback) {
+    ALOGV("registerEventCallback_2_4");
+    LOG_FATAL_IF(mEventCallback != nullptr, "already registered using registerEventCallback");
+
+    mEventCallback_2_4 = callback;
+    if (mEventCallback_2_4) {
+        mEventCallback_2_4->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
+    }
+}
+
+void FakeComposerClient::unregisterEventCallback_2_4() {
+    ALOGV("unregisterEventCallback_2_4");
+    mEventCallback_2_4 = nullptr;
+}
+
+V2_4::Error FakeComposerClient::getDisplayCapabilities_2_4(
+        Display /*display*/,
+        std::vector<V2_4::IComposerClient::DisplayCapability>* /*outCapabilities*/) {
+    return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error FakeComposerClient::getDisplayConnectionType(
+        Display /*display*/, V2_4::IComposerClient::DisplayConnectionType* /*outType*/) {
+    return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error FakeComposerClient::getDisplayAttribute_2_4(Display display, Config config,
+                                                        IComposerClient::Attribute attribute,
+                                                        int32_t* outValue) {
     ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display),
           static_cast<int>(config), static_cast<int>(attribute), outValue);
+    if (mMockHal) {
+        return mMockHal->getDisplayAttribute_2_4(display, config, attribute, outValue);
+    }
 
     // TODO: SOOO much fun to be had with these alone
     switch (attribute) {
@@ -276,233 +742,69 @@
     return Error::NONE;
 }
 
-Error FakeComposerClient::getDisplayConfigs(Display /*display*/, hidl_vec<Config>* outConfigs) {
-    ALOGV("getDisplayConfigs");
-    // TODO assert display == 1, outConfigs != nullptr
+V2_4::Error FakeComposerClient::getDisplayVsyncPeriod(Display display,
+                                                      V2_4::VsyncPeriodNanos* outVsyncPeriod) {
+    ALOGV("getDisplayVsyncPeriod");
+    if (mMockHal) {
+        return mMockHal->getDisplayVsyncPeriod(display, outVsyncPeriod);
+    }
 
-    outConfigs->resize(1);
-    (*outConfigs)[0] = 1;
-
-    return Error::NONE;
+    return V2_4::Error::UNSUPPORTED;
 }
 
-Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) {
-    ALOGV("getDisplayName");
-    return Error::NONE;
+V2_4::Error FakeComposerClient::setActiveConfigWithConstraints(
+        Display display, Config config,
+        const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+        VsyncPeriodChangeTimeline* timeline) {
+    ALOGV("setActiveConfigWithConstraints");
+    if (mMockHal) {
+        return mMockHal->setActiveConfigWithConstraints(display, config,
+                                                        vsyncPeriodChangeConstraints, timeline);
+    }
+    return V2_4::Error::UNSUPPORTED;
 }
 
-Error FakeComposerClient::getDisplayType(Display /*display*/,
-                                         IComposerClient::DisplayType* outType) {
-    ALOGV("getDisplayType");
-    // TODO: This setting nothing on the output had no effect on initial trials. Is first display
-    // assumed to be physical?
-    *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL);
-    return Error::NONE;
+V2_4::Error FakeComposerClient::setAutoLowLatencyMode(Display, bool) {
+    ALOGV("setAutoLowLatencyMode");
+    return V2_4::Error::UNSUPPORTED;
 }
 
-Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) {
-    ALOGV("getDozeSupport");
-    return Error::NONE;
+V2_4::Error FakeComposerClient::getSupportedContentTypes(
+        Display, std::vector<IComposerClient::ContentType>*) {
+    ALOGV("getSupportedContentTypes");
+    return V2_4::Error::UNSUPPORTED;
 }
 
-Error FakeComposerClient::getHdrCapabilities(Display /*display*/, hidl_vec<Hdr>* /*outTypes*/,
-                                             float* /*outMaxLuminance*/,
-                                             float* /*outMaxAverageLuminance*/,
-                                             float* /*outMinLuminance*/) {
-    ALOGV("getHdrCapabilities");
-    return Error::NONE;
+V2_4::Error FakeComposerClient::setContentType(Display, IComposerClient::ContentType) {
+    ALOGV("setContentType");
+    return V2_4::Error::UNSUPPORTED;
 }
 
-Error FakeComposerClient::setActiveConfig(Display /*display*/, Config config) {
-    ALOGV("setActiveConfig");
-    mCurrentConfig = config;
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setColorMode(Display /*display*/, ColorMode /*mode*/) {
-    ALOGV("setColorMode");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setPowerMode(Display /*display*/, IComposerClient::PowerMode /*mode*/) {
-    ALOGV("setPowerMode");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setVsyncEnabled(Display /*display*/, IComposerClient::Vsync enabled) {
-    mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE);
-    ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/,
-                                            int32_t /*hint*/) {
-    ALOGV("setColorTransform");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/,
-                                          int32_t /*acquireFence*/, int32_t /*dataspace*/,
-                                          const std::vector<hwc_rect_t>& /*damage*/) {
-    ALOGV("setClientTarget");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/,
-                                          int32_t /*releaseFence*/) {
-    ALOGV("setOutputBuffer");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::validateDisplay(
+V2_4::Error FakeComposerClient::validateDisplay_2_4(
         Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
         std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
         uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
-        std::vector<uint32_t>* /*outRequestMasks*/) {
-    ALOGV("validateDisplay");
-    // TODO: Assume touching nothing means All Korrekt!
-    return Error::NONE;
+        std::vector<uint32_t>* /*outRequestMasks*/,
+        IComposerClient::ClientTargetProperty* /*outClientTargetProperty*/) {
+    return V2_4::Error::NONE;
 }
 
-Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) {
-    ALOGV("acceptDisplayChanges");
-    // Didn't ask for changes because software is omnipotent.
-    return Error::NONE;
+V2_4::Error FakeComposerClient::setLayerGenericMetadata(Display, Layer, const std::string&, bool,
+                                                        const std::vector<uint8_t>&) {
+    ALOGV("setLayerGenericMetadata");
+    return V2_4::Error::UNSUPPORTED;
 }
 
-bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) {
-    return a->z <= b->z;
-}
-
-Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/,
-                                         std::vector<Layer>* /*outLayers*/,
-                                         std::vector<int32_t>* /*outReleaseFences*/) {
-    ALOGV("presentDisplay");
-    // TODO Leaving layers and their fences out for now. Doing so
-    // means that we've already processed everything. Important to
-    // test that the fences are respected, though. (How?)
-
-    std::unique_ptr<Frame> newFrame(new Frame);
-    for (uint64_t layer = 0; layer < mLayers.size(); layer++) {
-        const LayerImpl& layerImpl = *mLayers[layer];
-
-        if (!layerImpl.mValid) continue;
-
-        auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ);
-        newFrame->rectangles.push_back(std::move(rect));
-    }
-    std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering);
-    {
-        Mutex::Autolock _l(mStateMutex);
-        mFrames.push_back(std::move(newFrame));
-        mFramesAvailable.broadcast();
-    }
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
-                                                 int32_t /*x*/, int32_t /*y*/) {
-    ALOGV("setLayerCursorPosition");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer, buffer_handle_t buffer,
-                                         int32_t acquireFence) {
-    ALOGV("setLayerBuffer");
-    LayerImpl& l = getLayerImpl(layer);
-    if (buffer != l.mRenderState.mBuffer) {
-        l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not?
-    }
-    l.mRenderState.mBuffer = buffer;
-    l.mRenderState.mAcquireFence = acquireFence;
-
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/,
-                                                const std::vector<hwc_rect_t>& /*damage*/) {
-    ALOGV("setLayerSurfaceDamage");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) {
-    ALOGV("setLayerBlendMode");
-    getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode);
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer,
-                                        IComposerClient::Color color) {
-    ALOGV("setLayerColor");
-    getLayerImpl(layer).mRenderState.mLayerColor.r = color.r;
-    getLayerImpl(layer).mRenderState.mLayerColor.g = color.g;
-    getLayerImpl(layer).mRenderState.mLayerColor.b = color.b;
-    getLayerImpl(layer).mRenderState.mLayerColor.a = color.a;
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/,
-                                                  int32_t /*type*/) {
-    ALOGV("setLayerCompositionType");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/,
-                                            int32_t /*dataspace*/) {
-    ALOGV("setLayerDataspace");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer,
-                                               const hwc_rect_t& frame) {
-    ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right,
-          frame.bottom);
-    getLayerImpl(layer).mRenderState.mDisplayFrame = frame;
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) {
-    ALOGV("setLayerPlaneAlpha");
-    getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha;
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/,
-                                                 buffer_handle_t /*stream*/) {
-    ALOGV("setLayerSidebandStream");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer,
-                                             const hwc_frect_t& crop) {
-    ALOGV("setLayerSourceCrop");
-    getLayerImpl(layer).mRenderState.mSourceCrop = crop;
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer, int32_t transform) {
-    ALOGV("setLayerTransform");
-    getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform);
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer,
-                                                const std::vector<hwc_rect_t>& visible) {
-    ALOGV("setLayerVisibleRegion");
-    getLayerImpl(layer).mRenderState.mVisibleRegion = visible;
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) {
-    ALOGV("setLayerZOrder");
-    getLayerImpl(layer).mZ = z;
-    return Error::NONE;
+V2_4::Error FakeComposerClient::getLayerGenericMetadataKeys(
+        std::vector<IComposerClient::LayerGenericMetadataKey>*) {
+    ALOGV("getLayerGenericMetadataKeys");
+    return V2_4::Error::UNSUPPORTED;
 }
 
 //////////////////////////////////////////////////////////////////
 
 void FakeComposerClient::requestVSync(uint64_t vsyncTime) {
-    if (mEventCallback) {
+    if (mEventCallback || mEventCallback_2_4) {
         uint64_t timestamp = vsyncTime;
         ALOGV("Vsync");
         if (timestamp == 0) {
@@ -512,8 +814,10 @@
         }
         if (mSurfaceComposer != nullptr) {
             mSurfaceComposer->injectVSync(timestamp);
-        } else {
+        } else if (mEventCallback) {
             mEventCallback->onVsync(PRIMARY_DISPLAY, timestamp);
+        } else {
+            mEventCallback_2_4->onVsync_2_4(PRIMARY_DISPLAY, timestamp, 16'666'666);
         }
     }
 }
@@ -617,3 +921,6 @@
     // this might get more involving.
     return static_cast<Layer>(index);
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
index d115d79..600e765 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -16,22 +16,21 @@
 
 #pragma once
 
-#define HWC2_USE_CPP11
-#define HWC2_INCLUDE_STRINGIFICATION
-#include <composer-hal/2.1/ComposerClient.h>
-#undef HWC2_USE_CPP11
-#undef HWC2_INCLUDE_STRINGIFICATION
-#include "RenderState.h"
-
-// Needed for display type/ID enums
-#include <hardware/hwcomposer_defs.h>
-
-#include <utils/Condition.h>
-
 #include <chrono>
 
-using namespace android::hardware::graphics::composer::V2_1;
-using namespace android::hardware::graphics::composer::V2_1::hal;
+#include <composer-hal/2.1/ComposerClient.h>
+#include <composer-hal/2.2/ComposerClient.h>
+#include <composer-hal/2.3/ComposerClient.h>
+#include <composer-hal/2.4/ComposerClient.h>
+#include <utils/Condition.h>
+
+#include "MockComposerHal.h"
+#include "RenderState.h"
+
+using namespace android::hardware::graphics::common;
+using namespace android::hardware::graphics::composer;
+using namespace android::hardware::graphics::composer::V2_4;
+using namespace android::hardware::graphics::composer::V2_4::hal;
 using namespace android::hardware;
 using namespace std::chrono_literals;
 
@@ -46,7 +45,6 @@
 } // namespace android
 
 namespace sftest {
-
 // NOTE: The ID's need to be exactly these. VR composer and parts of
 // the SurfaceFlinger assume the display IDs to have these values
 // despite the enum being documented as a display type.
@@ -59,6 +57,8 @@
     FakeComposerClient();
     virtual ~FakeComposerClient();
 
+    void setMockHal(MockComposerHal* mockHal) { mMockHal = mockHal; }
+
     bool hasCapability(hwc2_capability_t capability) override;
 
     std::string dumpDebugInfo() override;
@@ -66,59 +66,193 @@
     void unregisterEventCallback() override;
 
     uint32_t getMaxVirtualDisplayCount() override;
-    Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
-                               Display* outDisplay) override;
-    Error destroyVirtualDisplay(Display display) override;
-    Error createLayer(Display display, Layer* outLayer) override;
-    Error destroyLayer(Display display, Layer layer) override;
+    V2_1::Error createVirtualDisplay(uint32_t width, uint32_t height, V1_0::PixelFormat* format,
+                                     Display* outDisplay) override;
+    V2_1::Error destroyVirtualDisplay(Display display) override;
+    V2_1::Error createLayer(Display display, Layer* outLayer) override;
+    V2_1::Error destroyLayer(Display display, Layer layer) override;
 
-    Error getActiveConfig(Display display, Config* outConfig) override;
-    Error getClientTargetSupport(Display display, uint32_t width, uint32_t height,
-                                 PixelFormat format, Dataspace dataspace) override;
-    Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override;
-    Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
-                              int32_t* outValue) override;
-    Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
-    Error getDisplayName(Display display, hidl_string* outName) override;
-    Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
-    Error getDozeSupport(Display display, bool* outSupport) override;
-    Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes, float* outMaxLuminance,
-                             float* outMaxAverageLuminance, float* outMinLuminance) override;
+    V2_1::Error getActiveConfig(Display display, Config* outConfig) override;
+    V2_1::Error getClientTargetSupport(Display display, uint32_t width, uint32_t height,
+                                       V1_0::PixelFormat format,
+                                       V1_0::Dataspace dataspace) override;
+    V2_1::Error getColorModes(Display display, hidl_vec<V1_0::ColorMode>* outModes) override;
+    V2_1::Error getDisplayAttribute(Display display, Config config,
+                                    V2_1::IComposerClient::Attribute attribute,
+                                    int32_t* outValue) override;
+    V2_1::Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
+    V2_1::Error getDisplayName(Display display, hidl_string* outName) override;
+    V2_1::Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
+    V2_1::Error getDozeSupport(Display display, bool* outSupport) override;
+    V2_1::Error getHdrCapabilities(Display display, hidl_vec<V1_0::Hdr>* outTypes,
+                                   float* outMaxLuminance, float* outMaxAverageLuminance,
+                                   float* outMinLuminance) override;
 
-    Error setActiveConfig(Display display, Config config) override;
-    Error setColorMode(Display display, ColorMode mode) override;
-    Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
-    Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
+    V2_1::Error setActiveConfig(Display display, Config config) override;
+    V2_1::Error setColorMode(Display display, V1_0::ColorMode mode) override;
+    V2_1::Error setPowerMode(Display display, V2_1::IComposerClient::PowerMode mode) override;
+    V2_1::Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
 
-    Error setColorTransform(Display display, const float* matrix, int32_t hint) override;
-    Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence,
-                          int32_t dataspace, const std::vector<hwc_rect_t>& damage) override;
-    Error setOutputBuffer(Display display, buffer_handle_t buffer, int32_t releaseFence) override;
-    Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers,
-                          std::vector<IComposerClient::Composition>* outCompositionTypes,
-                          uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
-                          std::vector<uint32_t>* outRequestMasks) override;
-    Error acceptDisplayChanges(Display display) override;
-    Error presentDisplay(Display display, int32_t* outPresentFence, std::vector<Layer>* outLayers,
-                         std::vector<int32_t>* outReleaseFences) override;
+    V2_1::Error setColorTransform(Display display, const float* matrix, int32_t hint) override;
+    V2_1::Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence,
+                                int32_t dataspace, const std::vector<hwc_rect_t>& damage) override;
+    V2_1::Error setOutputBuffer(Display display, buffer_handle_t buffer,
+                                int32_t releaseFence) override;
+    V2_1::Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers,
+                                std::vector<IComposerClient::Composition>* outCompositionTypes,
+                                uint32_t* outDisplayRequestMask,
+                                std::vector<Layer>* outRequestedLayers,
+                                std::vector<uint32_t>* outRequestMasks) override;
+    V2_1::Error acceptDisplayChanges(Display display) override;
+    V2_1::Error presentDisplay(Display display, int32_t* outPresentFence,
+                               std::vector<Layer>* outLayers,
+                               std::vector<int32_t>* outReleaseFences) override;
 
-    Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
-    Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
-                         int32_t acquireFence) override;
-    Error setLayerSurfaceDamage(Display display, Layer layer,
-                                const std::vector<hwc_rect_t>& damage) override;
-    Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
-    Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override;
-    Error setLayerCompositionType(Display display, Layer layer, int32_t type) override;
-    Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override;
-    Error setLayerDisplayFrame(Display display, Layer layer, const hwc_rect_t& frame) override;
-    Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
-    Error setLayerSidebandStream(Display display, Layer layer, buffer_handle_t stream) override;
-    Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override;
-    Error setLayerTransform(Display display, Layer layer, int32_t transform) override;
-    Error setLayerVisibleRegion(Display display, Layer layer,
-                                const std::vector<hwc_rect_t>& visible) override;
-    Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+    V2_1::Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
+    V2_1::Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
+                               int32_t acquireFence) override;
+    V2_1::Error setLayerSurfaceDamage(Display display, Layer layer,
+                                      const std::vector<hwc_rect_t>& damage) override;
+    V2_1::Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
+    V2_1::Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override;
+    V2_1::Error setLayerCompositionType(Display display, Layer layer, int32_t type) override;
+    V2_1::Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override;
+    V2_1::Error setLayerDisplayFrame(Display display, Layer layer,
+                                     const hwc_rect_t& frame) override;
+    V2_1::Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
+    V2_1::Error setLayerSidebandStream(Display display, Layer layer,
+                                       buffer_handle_t stream) override;
+    V2_1::Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override;
+    V2_1::Error setLayerTransform(Display display, Layer layer, int32_t transform) override;
+    V2_1::Error setLayerVisibleRegion(Display display, Layer layer,
+                                      const std::vector<hwc_rect_t>& visible) override;
+    V2_1::Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+
+    // Composer 2.2
+    V2_1::Error getPerFrameMetadataKeys(
+            Display display,
+            std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* outKeys) override;
+    V2_1::Error setLayerPerFrameMetadata(
+            Display display, Layer layer,
+            const std::vector<V2_2::IComposerClient::PerFrameMetadata>& metadata) override;
+
+    V2_1::Error getReadbackBufferAttributes(
+            Display display, graphics::common::V1_1::PixelFormat* outFormat,
+            graphics::common::V1_1::Dataspace* outDataspace) override;
+    V2_1::Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle,
+                                  android::base::unique_fd fenceFd) override;
+    V2_1::Error getReadbackBufferFence(Display display,
+                                       android::base::unique_fd* outFenceFd) override;
+    V2_1::Error createVirtualDisplay_2_2(uint32_t width, uint32_t height,
+                                         graphics::common::V1_1::PixelFormat* format,
+                                         Display* outDisplay) override;
+    V2_1::Error getClientTargetSupport_2_2(Display display, uint32_t width, uint32_t height,
+                                           graphics::common::V1_1::PixelFormat format,
+                                           graphics::common::V1_1::Dataspace dataspace) override;
+    V2_1::Error setPowerMode_2_2(Display display, V2_2::IComposerClient::PowerMode mode) override;
+
+    V2_1::Error setLayerFloatColor(Display display, Layer layer,
+                                   V2_2::IComposerClient::FloatColor color) override;
+
+    V2_1::Error getColorModes_2_2(Display display,
+                                  hidl_vec<graphics::common::V1_1::ColorMode>* outModes) override;
+    V2_1::Error getRenderIntents(
+            Display display, graphics::common::V1_1::ColorMode mode,
+            std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override;
+    V2_1::Error setColorMode_2_2(Display display, graphics::common::V1_1::ColorMode mode,
+                                 graphics::common::V1_1::RenderIntent intent) override;
+
+    std::array<float, 16> getDataspaceSaturationMatrix(
+            graphics::common::V1_1::Dataspace dataspace) override;
+
+    // Composer 2.3
+    V2_1::Error getPerFrameMetadataKeys_2_3(
+            Display display,
+            std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* outKeys) override;
+
+    V2_1::Error setColorMode_2_3(Display display, graphics::common::V1_2::ColorMode mode,
+                                 graphics::common::V1_1::RenderIntent intent) override;
+
+    V2_1::Error getRenderIntents_2_3(
+            Display display, graphics::common::V1_2::ColorMode mode,
+            std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override;
+
+    V2_1::Error getColorModes_2_3(Display display,
+                                  hidl_vec<graphics::common::V1_2::ColorMode>* outModes) override;
+
+    V2_1::Error getClientTargetSupport_2_3(Display display, uint32_t width, uint32_t height,
+                                           graphics::common::V1_2::PixelFormat format,
+                                           graphics::common::V1_2::Dataspace dataspace) override;
+    V2_1::Error getReadbackBufferAttributes_2_3(
+            Display display, graphics::common::V1_2::PixelFormat* outFormat,
+            graphics::common::V1_2::Dataspace* outDataspace) override;
+    V2_1::Error getHdrCapabilities_2_3(Display display,
+                                       hidl_vec<graphics::common::V1_2::Hdr>* outTypes,
+                                       float* outMaxLuminance, float* outMaxAverageLuminance,
+                                       float* outMinLuminance) override;
+    V2_1::Error setLayerPerFrameMetadata_2_3(
+            Display display, Layer layer,
+            const std::vector<V2_3::IComposerClient::PerFrameMetadata>& metadata) override;
+    V2_1::Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                             std::vector<uint8_t>* outData) override;
+    V2_1::Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
+    V2_1::Error getDisplayedContentSamplingAttributes(
+            uint64_t display, graphics::common::V1_2::PixelFormat& format,
+            graphics::common::V1_2::Dataspace& dataspace,
+            hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& componentMask) override;
+    V2_1::Error setDisplayedContentSamplingEnabled(
+            uint64_t display, V2_3::IComposerClient::DisplayedContentSampling enable,
+            hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> componentMask,
+            uint64_t maxFrames) override;
+    V2_1::Error getDisplayedContentSample(uint64_t display, uint64_t maxFrames, uint64_t timestamp,
+                                          uint64_t& frameCount,
+                                          hidl_vec<uint64_t>& sampleComponent0,
+                                          hidl_vec<uint64_t>& sampleComponent1,
+                                          hidl_vec<uint64_t>& sampleComponent2,
+                                          hidl_vec<uint64_t>& sampleComponent3) override;
+    V2_1::Error getDisplayCapabilities(
+            Display display,
+            std::vector<V2_3::IComposerClient::DisplayCapability>* outCapabilities) override;
+    V2_1::Error setLayerPerFrameMetadataBlobs(
+            Display display, Layer layer,
+            std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& blobs) override;
+    V2_1::Error getDisplayBrightnessSupport(Display display, bool* outSupport) override;
+    V2_1::Error setDisplayBrightness(Display display, float brightness) override;
+
+    // Composer 2.4
+    void registerEventCallback_2_4(EventCallback_2_4* callback) override;
+
+    void unregisterEventCallback_2_4() override;
+
+    V2_4::Error getDisplayCapabilities_2_4(
+            Display display,
+            std::vector<V2_4::IComposerClient::DisplayCapability>* outCapabilities) override;
+    V2_4::Error getDisplayConnectionType(
+            Display display, V2_4::IComposerClient::DisplayConnectionType* outType) override;
+    V2_4::Error getDisplayAttribute_2_4(Display display, Config config,
+                                        IComposerClient::Attribute attribute,
+                                        int32_t* outValue) override;
+    V2_4::Error getDisplayVsyncPeriod(Display display,
+                                      V2_4::VsyncPeriodNanos* outVsyncPeriod) override;
+    V2_4::Error setActiveConfigWithConstraints(
+            Display display, Config config,
+            const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+            VsyncPeriodChangeTimeline* outTimeline) override;
+    V2_4::Error setAutoLowLatencyMode(Display display, bool on) override;
+    V2_4::Error getSupportedContentTypes(
+            Display display,
+            std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+    V2_4::Error setContentType(Display display, IComposerClient::ContentType type) override;
+    V2_4::Error validateDisplay_2_4(
+            Display display, std::vector<Layer>* outChangedLayers,
+            std::vector<IComposerClient::Composition>* outCompositionTypes,
+            uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
+            std::vector<uint32_t>* outRequestMasks,
+            IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
+    V2_4::Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
+                                        bool mandatory, const std::vector<uint8_t>& value) override;
+    V2_4::Error getLayerGenericMetadataKeys(
+            std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
 
     void setClient(ComposerClient* client);
 
@@ -150,6 +284,7 @@
     LayerImpl& getLayerImpl(Layer handle);
 
     EventCallback* mEventCallback;
+    EventCallback_2_4* mEventCallback_2_4;
     Config mCurrentConfig;
     bool mVsyncEnabled;
     std::vector<std::unique_ptr<LayerImpl>> mLayers;
@@ -159,6 +294,8 @@
     android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections
     mutable android::Mutex mStateMutex;
     mutable android::Condition mFramesAvailable;
+
+    MockComposerHal* mMockHal = nullptr;
 };
 
 } // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
index f70cbdb..c656eed 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "FakeHwcService"
@@ -22,35 +26,154 @@
 #include "FakeComposerService.h"
 
 using namespace android::hardware;
+using namespace android::hardware::graphics::composer;
 
 namespace sftest {
 
-FakeComposerService::FakeComposerService(android::sp<ComposerClient>& client) : mClient(client) {}
+FakeComposerService_2_1::FakeComposerService_2_1(android::sp<ComposerClient>& client)
+      : mClient(client) {}
 
-FakeComposerService::~FakeComposerService() {
+FakeComposerService_2_1::~FakeComposerService_2_1() {
     ALOGI("Maybe killing client %p", mClient.get());
     // Rely on sp to kill the client.
 }
 
-Return<void> FakeComposerService::getCapabilities(getCapabilities_cb hidl_cb) {
+Return<void> FakeComposerService_2_1::getCapabilities(getCapabilities_cb hidl_cb) {
     ALOGI("FakeComposerService::getCapabilities");
     hidl_cb(hidl_vec<Capability>());
     return Void();
 }
 
-Return<void> FakeComposerService::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+Return<void> FakeComposerService_2_1::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
     ALOGI("FakeComposerService::dumpDebugInfo");
     hidl_cb(hidl_string());
     return Void();
 }
 
-Return<void> FakeComposerService::createClient(createClient_cb hidl_cb) {
+Return<void> FakeComposerService_2_1::createClient(createClient_cb hidl_cb) {
     ALOGI("FakeComposerService::createClient %p", mClient.get());
     if (!mClient->init()) {
         LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
     }
-    hidl_cb(Error::NONE, mClient);
+    hidl_cb(V2_1::Error::NONE, mClient);
+    return Void();
+}
+
+FakeComposerService_2_2::FakeComposerService_2_2(android::sp<ComposerClient>& client)
+      : mClient(client) {}
+
+FakeComposerService_2_2::~FakeComposerService_2_2() {
+    ALOGI("Maybe killing client %p", mClient.get());
+    // Rely on sp to kill the client.
+}
+
+Return<void> FakeComposerService_2_2::getCapabilities(getCapabilities_cb hidl_cb) {
+    ALOGI("FakeComposerService::getCapabilities");
+    hidl_cb(hidl_vec<Capability>());
+    return Void();
+}
+
+Return<void> FakeComposerService_2_2::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+    ALOGI("FakeComposerService::dumpDebugInfo");
+    hidl_cb(hidl_string());
+    return Void();
+}
+
+Return<void> FakeComposerService_2_2::createClient(createClient_cb hidl_cb) {
+    ALOGI("FakeComposerService::createClient %p", mClient.get());
+    if (!mClient->init()) {
+        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+    }
+    hidl_cb(V2_1::Error::NONE, mClient);
+    return Void();
+}
+
+FakeComposerService_2_3::FakeComposerService_2_3(android::sp<ComposerClient>& client)
+      : mClient(client) {}
+
+FakeComposerService_2_3::~FakeComposerService_2_3() {
+    ALOGI("Maybe killing client %p", mClient.get());
+    // Rely on sp to kill the client.
+}
+
+Return<void> FakeComposerService_2_3::getCapabilities(getCapabilities_cb hidl_cb) {
+    ALOGI("FakeComposerService::getCapabilities");
+    hidl_cb(hidl_vec<Capability>());
+    return Void();
+}
+
+Return<void> FakeComposerService_2_3::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+    ALOGI("FakeComposerService::dumpDebugInfo");
+    hidl_cb(hidl_string());
+    return Void();
+}
+
+Return<void> FakeComposerService_2_3::createClient(createClient_cb hidl_cb) {
+    LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_3");
+    if (!mClient->init()) {
+        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+    }
+    hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
+    return Void();
+}
+
+Return<void> FakeComposerService_2_3::createClient_2_3(createClient_2_3_cb hidl_cb) {
+    ALOGI("FakeComposerService_2_3::createClient_2_3 %p", mClient.get());
+    if (!mClient->init()) {
+        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+    }
+    hidl_cb(V2_1::Error::NONE, mClient);
+    return Void();
+}
+
+FakeComposerService_2_4::FakeComposerService_2_4(android::sp<ComposerClient>& client)
+      : mClient(client) {}
+
+FakeComposerService_2_4::~FakeComposerService_2_4() {
+    ALOGI("Maybe killing client %p", mClient.get());
+    // Rely on sp to kill the client.
+}
+
+Return<void> FakeComposerService_2_4::getCapabilities(getCapabilities_cb hidl_cb) {
+    ALOGI("FakeComposerService::getCapabilities");
+    hidl_cb(hidl_vec<Capability>());
+    return Void();
+}
+
+Return<void> FakeComposerService_2_4::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+    ALOGI("FakeComposerService::dumpDebugInfo");
+    hidl_cb(hidl_string());
+    return Void();
+}
+
+Return<void> FakeComposerService_2_4::createClient(createClient_cb hidl_cb) {
+    LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_4");
+    if (!mClient->init()) {
+        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+    }
+    hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
+    return Void();
+}
+
+Return<void> FakeComposerService_2_4::createClient_2_3(createClient_2_3_cb hidl_cb) {
+    LOG_ALWAYS_FATAL("createClient_2_3 called on FakeComposerService_2_4");
+    if (!mClient->init()) {
+        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+    }
+    hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
+    return Void();
+}
+
+Return<void> FakeComposerService_2_4::createClient_2_4(createClient_2_4_cb hidl_cb) {
+    ALOGI("FakeComposerService_2_4::createClient_2_4 %p", mClient.get());
+    if (!mClient->init()) {
+        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+    }
+    hidl_cb(V2_4::Error::NONE, mClient);
     return Void();
 }
 
 } // namespace sftest
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
index a3fb8a6..47f970f 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
@@ -16,18 +16,24 @@
 
 #pragma once
 
+#include <android/hardware/graphics/composer/2.4/IComposer.h>
 #include <composer-hal/2.1/ComposerClient.h>
+#include <composer-hal/2.2/ComposerClient.h>
+#include <composer-hal/2.3/ComposerClient.h>
+#include <composer-hal/2.4/ComposerClient.h>
 
-using namespace android::hardware::graphics::composer::V2_1;
-using namespace android::hardware::graphics::composer::V2_1::hal;
 using android::hardware::Return;
 
+using ComposerClient = android::hardware::graphics::composer::V2_4::hal::ComposerClient;
+
 namespace sftest {
 
-class FakeComposerService : public IComposer {
+using IComposer_2_1 = android::hardware::graphics::composer::V2_1::IComposer;
+
+class FakeComposerService_2_1 : public IComposer_2_1 {
 public:
-    explicit FakeComposerService(android::sp<ComposerClient>& client);
-    virtual ~FakeComposerService();
+    explicit FakeComposerService_2_1(android::sp<ComposerClient>& client);
+    virtual ~FakeComposerService_2_1();
 
     Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
     Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
@@ -37,4 +43,50 @@
     android::sp<ComposerClient> mClient;
 };
 
+using IComposer_2_2 = android::hardware::graphics::composer::V2_2::IComposer;
+class FakeComposerService_2_2 : public IComposer_2_2 {
+public:
+    explicit FakeComposerService_2_2(android::sp<ComposerClient>& client);
+    virtual ~FakeComposerService_2_2();
+
+    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+    Return<void> createClient(createClient_cb hidl_cb) override;
+
+private:
+    android::sp<ComposerClient> mClient;
+};
+
+using IComposer_2_3 = android::hardware::graphics::composer::V2_3::IComposer;
+class FakeComposerService_2_3 : public IComposer_2_3 {
+public:
+    explicit FakeComposerService_2_3(android::sp<ComposerClient>& client);
+    virtual ~FakeComposerService_2_3();
+
+    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+    Return<void> createClient(createClient_cb hidl_cb) override;
+    Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override;
+
+private:
+    android::sp<ComposerClient> mClient;
+};
+
+using IComposer_2_4 = android::hardware::graphics::composer::V2_4::IComposer;
+
+class FakeComposerService_2_4 : public IComposer_2_4 {
+public:
+    explicit FakeComposerService_2_4(android::sp<ComposerClient>& client);
+    virtual ~FakeComposerService_2_4();
+
+    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+    Return<void> createClient(createClient_cb hidl_cb) override;
+    Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override;
+    Return<void> createClient_2_4(createClient_2_4_cb hidl_cb) override;
+
+private:
+    android::sp<ComposerClient> mClient;
+};
+
 } // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
index 51956ec..1cea25a 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "FakeHwcUtil"
@@ -25,8 +29,8 @@
 #include "SurfaceFlinger.h" // Get the name of the service...
 
 #include <binder/IServiceManager.h>
-
 #include <cutils/properties.h>
+#include <hidl/ServiceManagement.h>
 
 #include <iomanip>
 #include <thread>
@@ -167,7 +171,9 @@
     }
     // TODO: Try registering the mock as the default service instead.
     property_set("debug.sf.hwc_service_name", "mock");
-    // This allows the SurfaceFlinger to load a HIDL service not listed in manifest files.
+
+    // This allows tests/SF to register/load a HIDL service not listed in manifest files.
+    android::hardware::details::setTrebleTestingOverride(true);
     property_set("debug.sf.treble_testing_override", "true");
 }
 
@@ -177,7 +183,11 @@
     // Wait for mock call signaling teardown?
     property_set("debug.sf.nobootanimation", "0");
     property_set("debug.sf.hwc_service_name", "default");
+    system("setenforce 1");
     ALOGI("Test env tear down - done");
 }
 
 } // namespace sftest
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
index 7d20d3c..383a111 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
@@ -19,11 +19,7 @@
 #include "FakeComposerClient.h"
 
 #include <gui/SurfaceComposerClient.h>
-
-#include <hardware/hwcomposer_defs.h>
-
 #include <log/log.h>
-
 #include <gtest/gtest.h>
 
 // clang-format off
@@ -108,9 +104,9 @@
         LOG_ALWAYS_FATAL_IF(android::NO_ERROR != apply());
         // Make sure that exactly one frame has been rendered.
         mComposer.waitUntilFrame(frameCount + 1);
-        LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(),
-                            "Unexpected frame advance. Delta: %d",
-                            mComposer.getFrameCount() - frameCount);
+        //        LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(),
+        //                            "Unexpected frame advance. Delta: %d",
+        //                            mComposer.getFrameCount() - frameCount);
     }
 
     FakeComposerClient& mComposer;
diff --git a/services/surfaceflinger/tests/fakehwc/MockComposerHal.h b/services/surfaceflinger/tests/fakehwc/MockComposerHal.h
new file mode 100644
index 0000000..5dc3778
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/MockComposerHal.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <composer-hal/2.4/ComposerClient.h>
+
+#include <gmock/gmock.h>
+
+using namespace android::hardware::graphics::common;
+using namespace android::hardware::graphics::composer;
+using namespace android::hardware::graphics::composer::V2_4;
+using namespace android::hardware::graphics::composer::V2_4::hal;
+using namespace android::hardware;
+using namespace std::chrono_literals;
+
+namespace sftest {
+
+// Mock class for ComposerHal. Implements only the functions used in the test.
+class MockComposerHal {
+public:
+    MOCK_METHOD2(getActiveConfig, V2_1::Error(Display, Config*));
+    MOCK_METHOD4(getDisplayAttribute_2_4,
+                 V2_4::Error(Display, Config, V2_4::IComposerClient::Attribute, int32_t*));
+    MOCK_METHOD2(getDisplayConfigs, V2_1::Error(Display, hidl_vec<Config>*));
+    MOCK_METHOD2(setActiveConfig, V2_1::Error(Display, Config));
+    MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, V2_4::VsyncPeriodNanos*));
+    MOCK_METHOD4(setActiveConfigWithConstraints,
+                 V2_4::Error(Display, Config,
+                             const V2_4::IComposerClient::VsyncPeriodChangeConstraints&,
+                             VsyncPeriodChangeTimeline*));
+};
+
+} // namespace sftest
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/fakehwc/RenderState.h b/services/surfaceflinger/tests/fakehwc/RenderState.h
index 0059289..40193f2 100644
--- a/services/surfaceflinger/tests/fakehwc/RenderState.h
+++ b/services/surfaceflinger/tests/fakehwc/RenderState.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include <hardware/hwcomposer2.h>
-
 #include <vector>
 
 namespace sftest {
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index a892a2a..a03fd89 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "FakeHwcTest"
@@ -21,6 +25,7 @@
 #include "FakeComposerClient.h"
 #include "FakeComposerService.h"
 #include "FakeComposerUtils.h"
+#include "MockComposerHal.h"
 
 #include <gui/DisplayEventReceiver.h>
 #include <gui/ISurfaceComposer.h>
@@ -36,13 +41,14 @@
 #include <hwbinder/ProcessState.h>
 #include <log/log.h>
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
 #include <utils/Looper.h>
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <limits>
+#include <thread>
 
 using namespace std::chrono_literals;
 
@@ -55,12 +61,14 @@
 
 // Mock test helpers
 using ::testing::_;
+using ::testing::AtLeast;
 using ::testing::DoAll;
 using ::testing::Invoke;
 using ::testing::Return;
 using ::testing::SetArgPointee;
 
 using Transaction = SurfaceComposerClient::Transaction;
+using Attribute = V2_4::IComposerClient::Attribute;
 
 ///////////////////////////////////////////////
 
@@ -121,319 +129,1268 @@
                           static_cast<int>(bottom));
 }
 
-////////////////////////////////////////////////
-
+///////////////////////////////////////////////
+template <typename FakeComposerService>
 class DisplayTest : public ::testing::Test {
-public:
-    class MockComposerClient : public FakeComposerClient {
-    public:
-        MOCK_METHOD2(getDisplayType, Error(Display display, ComposerClient::DisplayType* outType));
-        MOCK_METHOD4(getDisplayAttribute,
-                     Error(Display display, Config config, IComposerClient::Attribute attribute,
-                           int32_t* outValue));
-
-        // Re-routing to basic fake implementation
-        Error getDisplayAttributeFake(Display display, Config config,
-                                      IComposerClient::Attribute attribute, int32_t* outValue) {
-            return FakeComposerClient::getDisplayAttribute(display, config, attribute, outValue);
-        }
+protected:
+    struct TestConfig {
+        int32_t id;
+        int32_t w;
+        int32_t h;
+        int32_t vsyncPeriod;
+        int32_t group;
     };
 
-protected:
-    static int processDisplayEvents(int fd, int events, void* data);
+    static int processDisplayEvents(int /*fd*/, int /*events*/, void* data) {
+        auto self = static_cast<DisplayTest*>(data);
 
-    void SetUp() override;
-    void TearDown() override;
+        ssize_t n;
+        DisplayEventReceiver::Event buffer[1];
 
-    void waitForDisplayTransaction();
-    bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected);
-
-    sp<IComposer> mFakeService;
-    sp<SurfaceComposerClient> mComposerClient;
-
-    MockComposerClient* mMockComposer;
-
-    std::unique_ptr<DisplayEventReceiver> mReceiver;
-    sp<Looper> mLooper;;
-    std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents;
-};
-
-void DisplayTest::SetUp() {
-    // TODO: The mMockComposer should be a unique_ptr, but it needs to
-    // outlive the test class.  Currently ComposerClient only dies
-    // when the service is replaced. The Mock deletes itself when
-    // removeClient is called on it, which is ugly.  This can be
-    // changed if HIDL ServiceManager allows removing services or
-    // ComposerClient starts taking the ownership of the contained
-    // implementation class. Moving the fake class to the HWC2
-    // interface instead of the current Composer interface might also
-    // change the situation.
-    mMockComposer = new MockComposerClient;
-    sp<ComposerClient> client = new ComposerClient(mMockComposer);
-    mFakeService = new FakeComposerService(client);
-    (void)mFakeService->registerAsService("mock");
-
-    android::hardware::ProcessState::self()->startThreadPool();
-    android::ProcessState::self()->startThreadPool();
-
-    EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _))
-            .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
-                            Return(Error::NONE)));
-    // Primary display will be queried twice for all 5 attributes. One
-    // set of queries comes from the SurfaceFlinger proper an the
-    // other set from the VR composer.
-    // TODO: Is VR composer always present? Change to atLeast(5)?
-    EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _))
-            .Times(2 * 5)
-            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
-
-    startSurfaceFlinger();
-
-    // Fake composer wants to enable VSync injection
-    mMockComposer->onSurfaceFlingerStart();
-
-    mComposerClient = new SurfaceComposerClient;
-    ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
-    mReceiver.reset(new DisplayEventReceiver());
-    mLooper = new Looper(false);
-    mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
-}
-
-void DisplayTest::TearDown() {
-    mLooper = nullptr;
-    mReceiver = nullptr;
-
-    mComposerClient->dispose();
-    mComposerClient = nullptr;
-
-    // Fake composer needs to release SurfaceComposerClient before the stop.
-    mMockComposer->onSurfaceFlingerStop();
-    stopSurfaceFlinger();
-
-    mFakeService = nullptr;
-    // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
-    // management.
-    mMockComposer = nullptr;
-}
-
-
-int DisplayTest::processDisplayEvents(int /*fd*/, int /*events*/, void* data) {
-    auto self = static_cast<DisplayTest*>(data);
-
-    ssize_t n;
-    DisplayEventReceiver::Event buffer[1];
-
-    while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) {
-        for (int i=0 ; i<n ; i++) {
-            self->mReceivedDisplayEvents.push_back(buffer[i]);
+        while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) {
+            for (int i = 0; i < n; i++) {
+                self->mReceivedDisplayEvents.push_back(buffer[i]);
+            }
         }
+        ALOGD_IF(n < 0, "Error reading events (%s)\n", strerror(-n));
+        return 1;
     }
-    ALOGD_IF(n < 0, "Error reading events (%s)\n", strerror(-n));
-    return 1;
-}
 
-void DisplayTest::waitForDisplayTransaction() {
-    // Both a refresh and a vsync event are needed to apply pending display
-    // transactions.
-    mMockComposer->refreshDisplay(EXTERNAL_DISPLAY);
-    mMockComposer->runVSyncAndWait();
+    Error getDisplayAttributeNoMock(Display display, Config config,
+                                    V2_4::IComposerClient::Attribute attribute, int32_t* outValue) {
+        mFakeComposerClient->setMockHal(nullptr);
+        auto ret =
+                mFakeComposerClient->getDisplayAttribute_2_4(display, config, attribute, outValue);
+        mFakeComposerClient->setMockHal(mMockComposer.get());
+        return ret;
+    }
 
-    // Extra vsync and wait to avoid a 10% flake due to a race.
-    mMockComposer->runVSyncAndWait();
-}
+    void setExpectationsForConfigs(Display display, std::vector<TestConfig> testConfigs,
+                                   Config activeConfig, V2_4::VsyncPeriodNanos defaultVsyncPeriod) {
+        std::vector<Config> configIds;
+        for (int i = 0; i < testConfigs.size(); i++) {
+            configIds.push_back(testConfigs[i].id);
 
-bool DisplayTest::waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
-    int waitCount = 20;
-    while (waitCount--) {
-        while (!mReceivedDisplayEvents.empty()) {
-            auto event = mReceivedDisplayEvents.front();
-            mReceivedDisplayEvents.pop_front();
+            EXPECT_CALL(*mMockComposer,
+                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::WIDTH, _))
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].w), Return(Error::NONE)));
+            EXPECT_CALL(*mMockComposer,
+                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::HEIGHT, _))
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].h), Return(Error::NONE)));
+            EXPECT_CALL(*mMockComposer,
+                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::VSYNC_PERIOD,
+                                                _))
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].vsyncPeriod),
+                                          Return(Error::NONE)));
+            EXPECT_CALL(*mMockComposer,
+                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::CONFIG_GROUP,
+                                                _))
+                    .WillRepeatedly(
+                            DoAll(SetArgPointee<3>(testConfigs[i].group), Return(Error::NONE)));
+            EXPECT_CALL(*mMockComposer,
+                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_X, _))
+                    .WillRepeatedly(Return(Error::UNSUPPORTED));
+            EXPECT_CALL(*mMockComposer,
+                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_Y, _))
+                    .WillRepeatedly(Return(Error::UNSUPPORTED));
+        }
 
-            ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
-                     "event hotplug: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
-                     ", connected %d\t",
-                     event.header.displayId, event.hotplug.connected);
+        EXPECT_CALL(*mMockComposer, getDisplayConfigs(display, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(hidl_vec<Config>(configIds)),
+                                      Return(V2_1::Error::NONE)));
 
-            if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
-                event.header.displayId == displayId && event.hotplug.connected == connected) {
-                return true;
+        EXPECT_CALL(*mMockComposer, getActiveConfig(display, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(activeConfig), Return(V2_1::Error::NONE)));
+
+        EXPECT_CALL(*mMockComposer, getDisplayVsyncPeriod(display, _))
+                .WillRepeatedly(
+                        DoAll(SetArgPointee<1>(defaultVsyncPeriod), Return(V2_4::Error::NONE)));
+    }
+
+    void SetUp() override {
+        mMockComposer = std::make_unique<MockComposerHal>();
+        mFakeComposerClient = new FakeComposerClient();
+        mFakeComposerClient->setMockHal(mMockComposer.get());
+
+        sp<V2_4::hal::ComposerClient> client = new V2_4::hal::ComposerClient(mFakeComposerClient);
+        mFakeService = new FakeComposerService(client);
+        ASSERT_EQ(android::OK, mFakeService->registerAsService("mock"));
+
+        android::hardware::ProcessState::self()->startThreadPool();
+        android::ProcessState::self()->startThreadPool();
+
+        setExpectationsForConfigs(PRIMARY_DISPLAY,
+                                  {{
+                                          .id = 1,
+                                          .w = 1920,
+                                          .h = 1024,
+                                          .vsyncPeriod = 16'666'666,
+                                          .group = 0,
+                                  }},
+                                  1, 16'666'666);
+
+        startSurfaceFlinger();
+
+        // Fake composer wants to enable VSync injection
+        mFakeComposerClient->onSurfaceFlingerStart();
+
+        mComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+        mReceiver.reset(new DisplayEventReceiver(ISurfaceComposer::eVsyncSourceApp,
+                                                 ISurfaceComposer::eConfigChangedDispatch));
+        mLooper = new Looper(false);
+        mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
+    }
+
+    void TearDown() override {
+        mLooper = nullptr;
+        mReceiver = nullptr;
+
+        mComposerClient->dispose();
+        mComposerClient = nullptr;
+
+        // Fake composer needs to release SurfaceComposerClient before the stop.
+        mFakeComposerClient->onSurfaceFlingerStop();
+        stopSurfaceFlinger();
+
+        mFakeComposerClient->setMockHal(nullptr);
+
+        mFakeService = nullptr;
+        // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
+        // management.
+        mMockComposer = nullptr;
+    }
+
+    void waitForDisplayTransaction() {
+        // Both a refresh and a vsync event are needed to apply pending display
+        // transactions.
+        mFakeComposerClient->refreshDisplay(EXTERNAL_DISPLAY);
+        mFakeComposerClient->runVSyncAndWait();
+
+        // Extra vsync and wait to avoid a 10% flake due to a race.
+        mFakeComposerClient->runVSyncAndWait();
+    }
+
+    bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
+        int waitCount = 20;
+        while (waitCount--) {
+            while (!mReceivedDisplayEvents.empty()) {
+                auto event = mReceivedDisplayEvents.front();
+                mReceivedDisplayEvents.pop_front();
+
+                ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
+                         "event hotplug: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
+                         ", connected %d\t",
+                         event.header.displayId, event.hotplug.connected);
+
+                if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
+                    event.header.displayId == displayId && event.hotplug.connected == connected) {
+                    return true;
+                }
+            }
+
+            mLooper->pollOnce(1);
+        }
+        return false;
+    }
+
+    bool waitForConfigChangedEvent(PhysicalDisplayId displayId, int32_t configId) {
+        int waitCount = 20;
+        while (waitCount--) {
+            while (!mReceivedDisplayEvents.empty()) {
+                auto event = mReceivedDisplayEvents.front();
+                mReceivedDisplayEvents.pop_front();
+
+                ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED,
+                         "event config: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
+                         ", configId %d\t",
+                         event.header.displayId, event.config.configId);
+
+                if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED &&
+                    event.header.displayId == displayId && event.config.configId == configId) {
+                    return true;
+                }
+            }
+
+            mLooper->pollOnce(1);
+        }
+        return false;
+    }
+
+    void Test_HotplugOneConfig() {
+        ALOGD("DisplayTest::Test_Hotplug_oneConfig");
+
+        setExpectationsForConfigs(EXTERNAL_DISPLAY,
+                                  {{.id = 1,
+                                    .w = 200,
+                                    .h = 400,
+                                    .vsyncPeriod = 16'666'666,
+                                    .group = 0}},
+                                  1, 16'666'666);
+
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::CONNECTED);
+        waitForDisplayTransaction();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
+
+        {
+            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+            EXPECT_FALSE(display == nullptr);
+
+            DisplayConfig config;
+            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+            const ui::Size& resolution = config.resolution;
+            EXPECT_EQ(ui::Size(200, 400), resolution);
+            EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
         }
 
-        mLooper->pollOnce(1);
-    }
-
-    return false;
-}
-
-TEST_F(DisplayTest, Hotplug) {
-    ALOGD("DisplayTest::Hotplug");
-
-    EXPECT_CALL(*mMockComposer, getDisplayType(EXTERNAL_DISPLAY, _))
-            .Times(2)
-            .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
-                                  Return(Error::NONE)));
-    // The attribute queries will get done twice. This is for defaults
-    EXPECT_CALL(*mMockComposer, getDisplayAttribute(EXTERNAL_DISPLAY, 1, _, _))
-            .Times(2 * 3)
-            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
-    // ... and then special handling for dimensions. Specifying these
-    // rules later means that gmock will try them first, i.e.,
-    // ordering of width/height vs. the default implementation for
-    // other queries is significant.
-    EXPECT_CALL(*mMockComposer,
-                getDisplayAttribute(EXTERNAL_DISPLAY, 1, IComposerClient::Attribute::WIDTH, _))
-            .Times(2)
-            .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
-
-    EXPECT_CALL(*mMockComposer,
-                getDisplayAttribute(EXTERNAL_DISPLAY, 1, IComposerClient::Attribute::HEIGHT, _))
-            .Times(2)
-            .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
-
-    mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
-
-    waitForDisplayTransaction();
-
-    EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
-    {
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
-        ASSERT_FALSE(display == nullptr);
-
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-        ASSERT_EQ(400u, info.w);
-        ASSERT_EQ(200u, info.h);
-
-        auto surfaceControl =
-                mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w, info.h,
-                                               PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(surfaceControl != nullptr);
-        ASSERT_TRUE(surfaceControl->isValid());
-        fillSurfaceRGBA8(surfaceControl, BLUE);
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
+        waitForDisplayTransaction();
+        mFakeComposerClient->clearFrames();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
 
         {
-            TransactionScope ts(*mMockComposer);
-            ts.setDisplayLayerStack(display, 0);
+            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+            EXPECT_TRUE(display == nullptr);
 
-            ts.setLayer(surfaceControl, INT32_MAX - 2)
-                .show(surfaceControl);
+            DisplayConfig config;
+            EXPECT_NE(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
         }
     }
 
-    mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
+    void Test_HotplugTwoSeparateConfigs() {
+        ALOGD("DisplayTest::Test_HotplugTwoSeparateConfigs");
 
-    mMockComposer->clearFrames();
+        setExpectationsForConfigs(EXTERNAL_DISPLAY,
+                                  {{.id = 1,
+                                    .w = 200,
+                                    .h = 400,
+                                    .vsyncPeriod = 16'666'666,
+                                    .group = 0},
+                                   {.id = 2,
+                                    .w = 800,
+                                    .h = 1600,
+                                    .vsyncPeriod = 11'111'111,
+                                    .group = 1}},
+                                  1, 16'666'666);
 
-    mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::CONNECTED);
+        waitForDisplayTransaction();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
 
-    waitForDisplayTransaction();
-
-    EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
-    EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
-    {
         const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
-        ASSERT_FALSE(display == nullptr);
+        EXPECT_FALSE(display == nullptr);
 
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-        ASSERT_EQ(400u, info.w);
-        ASSERT_EQ(200u, info.h);
+        DisplayConfig config;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(200, 400), config.resolution);
+        EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
 
-        auto surfaceControl =
-                mComposerClient->createSurface(String8("Display Test Surface Bar"), info.w, info.h,
-                                               PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(surfaceControl != nullptr);
-        ASSERT_TRUE(surfaceControl->isValid());
-        fillSurfaceRGBA8(surfaceControl, BLUE);
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        Vector<DisplayConfig> configs;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
+        EXPECT_EQ(configs.size(), 2);
+
+        // change active config
+
+        if (mIs2_4Client) {
+            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 2, _, _))
+                    .WillOnce(Return(V2_4::Error::NONE));
+        } else {
+            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 2))
+                    .WillOnce(Return(V2_1::Error::NONE));
+        }
+
+        for (int i = 0; i < configs.size(); i++) {
+            const auto& config = configs[i];
+            if (config.resolution.getWidth() == 800) {
+                EXPECT_EQ(NO_ERROR,
+                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate));
+                waitForDisplayTransaction();
+                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                break;
+            }
+        }
+
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
+        waitForDisplayTransaction();
+        mFakeComposerClient->clearFrames();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
+    }
+
+    void Test_HotplugTwoConfigsSameGroup() {
+        ALOGD("DisplayTest::Test_HotplugTwoConfigsSameGroup");
+
+        setExpectationsForConfigs(EXTERNAL_DISPLAY,
+                                  {{.id = 2,
+                                    .w = 800,
+                                    .h = 1600,
+                                    .vsyncPeriod = 16'666'666,
+                                    .group = 31},
+                                   {.id = 3,
+                                    .w = 800,
+                                    .h = 1600,
+                                    .vsyncPeriod = 11'111'111,
+                                    .group = 31}},
+                                  2, 16'666'666);
+
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::CONNECTED);
+        waitForDisplayTransaction();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
+
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+        EXPECT_FALSE(display == nullptr);
+
+        DisplayConfig config;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        Vector<DisplayConfig> configs;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
+        EXPECT_EQ(configs.size(), 2);
+
+        // change active config
+        if (mIs2_4Client) {
+            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
+                    .WillOnce(Return(V2_4::Error::NONE));
+        } else {
+            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3))
+                    .WillOnce(Return(V2_1::Error::NONE));
+        }
+
+        for (int i = 0; i < configs.size(); i++) {
+            const auto& config = configs[i];
+            if (config.refreshRate == 1e9f / 11'111'111) {
+                EXPECT_EQ(NO_ERROR,
+                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate));
+                waitForDisplayTransaction();
+                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                break;
+            }
+        }
+
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
+        waitForDisplayTransaction();
+        mFakeComposerClient->clearFrames();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
+    }
+
+    void Test_HotplugThreeConfigsMixedGroups() {
+        ALOGD("DisplayTest::Test_HotplugThreeConfigsMixedGroups");
+
+        setExpectationsForConfigs(EXTERNAL_DISPLAY,
+                                  {{.id = 2,
+                                    .w = 800,
+                                    .h = 1600,
+                                    .vsyncPeriod = 16'666'666,
+                                    .group = 0},
+                                   {.id = 3,
+                                    .w = 800,
+                                    .h = 1600,
+                                    .vsyncPeriod = 11'111'111,
+                                    .group = 0},
+                                   {.id = 4,
+                                    .w = 1600,
+                                    .h = 3200,
+                                    .vsyncPeriod = 8'333'333,
+                                    .group = 1},
+                                   {.id = 5,
+                                    .w = 1600,
+                                    .h = 3200,
+                                    .vsyncPeriod = 11'111'111,
+                                    .group = 1}},
+                                  2, 16'666'666);
+
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::CONNECTED);
+        waitForDisplayTransaction();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
+
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+        EXPECT_FALSE(display == nullptr);
+
+        DisplayConfig config;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        Vector<DisplayConfig> configs;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
+        EXPECT_EQ(configs.size(), 4);
+
+        // change active config to 800x1600@90Hz
+        if (mIs2_4Client) {
+            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
+                    .WillOnce(Return(V2_4::Error::NONE));
+        } else {
+            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3))
+                    .WillOnce(Return(V2_1::Error::NONE));
+        }
+
+        for (int i = 0; i < configs.size(); i++) {
+            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,
+                                                               configs[i].refreshRate,
+                                                               configs[i].refreshRate));
+                waitForDisplayTransaction();
+                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                break;
+            }
+        }
+
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        // change active config to 1600x3200@120Hz
+        if (mIs2_4Client) {
+            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 4, _, _))
+                    .WillOnce(Return(V2_4::Error::NONE));
+        } else {
+            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 4))
+                    .WillOnce(Return(V2_1::Error::NONE));
+        }
+
+        for (int i = 0; i < configs.size(); i++) {
+            const auto& config = configs[i];
+            if (config.refreshRate == 1e9f / 8'333'333) {
+                EXPECT_EQ(NO_ERROR,
+                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate));
+                waitForDisplayTransaction();
+                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                break;
+            }
+        }
+
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(1600, 3200), config.resolution);
+        EXPECT_EQ(1e9f / 8'333'333, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        // change active config to 1600x3200@90Hz
+        if (mIs2_4Client) {
+            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 5, _, _))
+                    .WillOnce(Return(V2_4::Error::NONE));
+        } else {
+            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 5))
+                    .WillOnce(Return(V2_1::Error::NONE));
+        }
+
+        for (int i = 0; i < configs.size(); i++) {
+            const auto& config = configs[i];
+            if (config.resolution.getWidth() == 1600 && config.refreshRate == 1e9f / 11'111'111) {
+                EXPECT_EQ(NO_ERROR,
+                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate));
+                waitForDisplayTransaction();
+                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                break;
+            }
+        }
+
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(1600, 3200), config.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
+        waitForDisplayTransaction();
+        mFakeComposerClient->clearFrames();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
+    }
+
+    void Test_HotplugPrimaryDisplay() {
+        ALOGD("DisplayTest::HotplugPrimaryDisplay");
+
+        mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
+
+        waitForDisplayTransaction();
+
+        EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
+        {
+            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+            EXPECT_TRUE(display == nullptr);
+
+            DisplayConfig config;
+            auto result = SurfaceComposerClient::getActiveDisplayConfig(display, &config);
+            EXPECT_NE(NO_ERROR, result);
+        }
+
+        mFakeComposerClient->clearFrames();
+
+        setExpectationsForConfigs(PRIMARY_DISPLAY,
+                                  {{.id = 1,
+                                    .w = 400,
+                                    .h = 200,
+                                    .vsyncPeriod = 16'666'666,
+                                    .group = 0}},
+                                  1, 16'666'666);
+
+        mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::CONNECTED);
+
+        waitForDisplayTransaction();
+
+        EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
 
         {
-            TransactionScope ts(*mMockComposer);
-            ts.setDisplayLayerStack(display, 0);
+            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+            EXPECT_FALSE(display == nullptr);
 
-            ts.setLayer(surfaceControl, INT32_MAX - 2)
-                .show(surfaceControl);
+            DisplayConfig config;
+            auto result = SurfaceComposerClient::getActiveDisplayConfig(display, &config);
+            EXPECT_EQ(NO_ERROR, result);
+            ASSERT_EQ(ui::Size(400, 200), config.resolution);
+            EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
         }
     }
-    mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
+
+    sp<V2_1::IComposer> mFakeService;
+    sp<SurfaceComposerClient> mComposerClient;
+
+    std::unique_ptr<MockComposerHal> mMockComposer;
+    FakeComposerClient* mFakeComposerClient;
+
+    std::unique_ptr<DisplayEventReceiver> mReceiver;
+    sp<Looper> mLooper;
+    std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents;
+
+    static constexpr bool mIs2_4Client =
+            std::is_same<FakeComposerService, FakeComposerService_2_4>::value;
+};
+
+using DisplayTest_2_1 = DisplayTest<FakeComposerService_2_1>;
+
+TEST_F(DisplayTest_2_1, HotplugOneConfig) {
+    Test_HotplugOneConfig();
 }
 
-TEST_F(DisplayTest, HotplugPrimaryDisplay) {
-    ALOGD("DisplayTest::HotplugPrimaryDisplay");
+TEST_F(DisplayTest_2_1, HotplugTwoSeparateConfigs) {
+    Test_HotplugTwoSeparateConfigs();
+}
 
-    mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
+TEST_F(DisplayTest_2_1, HotplugTwoConfigsSameGroup) {
+    Test_HotplugTwoConfigsSameGroup();
+}
 
-    waitForDisplayTransaction();
+TEST_F(DisplayTest_2_1, HotplugThreeConfigsMixedGroups) {
+    Test_HotplugThreeConfigsMixedGroups();
+}
 
-    EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
+TEST_F(DisplayTest_2_1, HotplugPrimaryOneConfig) {
+    Test_HotplugPrimaryDisplay();
+}
 
-    {
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
-        EXPECT_FALSE(display == nullptr);
+using DisplayTest_2_2 = DisplayTest<FakeComposerService_2_2>;
 
-        DisplayInfo info;
-        auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
-        EXPECT_NE(NO_ERROR, result);
-    }
+TEST_F(DisplayTest_2_2, HotplugOneConfig) {
+    Test_HotplugOneConfig();
+}
 
-    mMockComposer->clearFrames();
+TEST_F(DisplayTest_2_2, HotplugTwoSeparateConfigs) {
+    Test_HotplugTwoSeparateConfigs();
+}
 
-    EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _))
-            .Times(2)
-            .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
-                                  Return(Error::NONE)));
-    // The attribute queries will get done twice. This is for defaults
-    EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _))
-            .Times(2 * 3)
-            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
-    // ... and then special handling for dimensions. Specifying these
-    // rules later means that gmock will try them first, i.e.,
-    // ordering of width/height vs. the default implementation for
-    // other queries is significant.
-    EXPECT_CALL(*mMockComposer,
-                getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::WIDTH, _))
-            .Times(2)
-            .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
+TEST_F(DisplayTest_2_2, HotplugTwoConfigsSameGroup) {
+    Test_HotplugTwoConfigsSameGroup();
+}
 
-    EXPECT_CALL(*mMockComposer,
-                getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::HEIGHT, _))
-            .Times(2)
-            .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
+TEST_F(DisplayTest_2_2, HotplugThreeConfigsMixedGroups) {
+    Test_HotplugThreeConfigsMixedGroups();
+}
 
-    mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
+TEST_F(DisplayTest_2_2, HotplugPrimaryOneConfig) {
+    Test_HotplugPrimaryDisplay();
+}
 
-    waitForDisplayTransaction();
+using DisplayTest_2_3 = DisplayTest<FakeComposerService_2_3>;
 
-    EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
+TEST_F(DisplayTest_2_3, HotplugOneConfig) {
+    Test_HotplugOneConfig();
+}
 
-    {
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
-        EXPECT_FALSE(display == nullptr);
+TEST_F(DisplayTest_2_3, HotplugTwoSeparateConfigs) {
+    Test_HotplugTwoSeparateConfigs();
+}
 
-        DisplayInfo info;
-        auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
-        EXPECT_EQ(NO_ERROR, result);
-        ASSERT_EQ(400u, info.w);
-        ASSERT_EQ(200u, info.h);
-    }
+TEST_F(DisplayTest_2_3, HotplugTwoConfigsSameGroup) {
+    Test_HotplugTwoConfigsSameGroup();
+}
+
+TEST_F(DisplayTest_2_3, HotplugThreeConfigsMixedGroups) {
+    Test_HotplugThreeConfigsMixedGroups();
+}
+
+TEST_F(DisplayTest_2_3, HotplugPrimaryOneConfig) {
+    Test_HotplugPrimaryDisplay();
+}
+
+using DisplayTest_2_4 = DisplayTest<FakeComposerService_2_4>;
+
+TEST_F(DisplayTest_2_4, HotplugOneConfig) {
+    Test_HotplugOneConfig();
+}
+
+TEST_F(DisplayTest_2_4, HotplugTwoSeparateConfigs) {
+    Test_HotplugTwoSeparateConfigs();
+}
+
+TEST_F(DisplayTest_2_4, HotplugTwoConfigsSameGroup) {
+    Test_HotplugTwoConfigsSameGroup();
+}
+
+TEST_F(DisplayTest_2_4, HotplugThreeConfigsMixedGroups) {
+    Test_HotplugThreeConfigsMixedGroups();
+}
+
+TEST_F(DisplayTest_2_4, HotplugPrimaryOneConfig) {
+    Test_HotplugPrimaryDisplay();
 }
 
 ////////////////////////////////////////////////
 
+template <typename FakeComposerService>
 class TransactionTest : public ::testing::Test {
 protected:
     // Layer array indexing constants.
     constexpr static int BG_LAYER = 0;
     constexpr static int FG_LAYER = 1;
 
-    static void SetUpTestCase();
-    static void TearDownTestCase();
+    static void SetUpTestCase() {
+        // TODO: See TODO comment at DisplayTest::SetUp for background on
+        // the lifetime of the FakeComposerClient.
+        sFakeComposer = new FakeComposerClient;
+        sp<V2_4::hal::ComposerClient> client = new V2_4::hal::ComposerClient(sFakeComposer);
+        sp<V2_1::IComposer> fakeService = new FakeComposerService(client);
+        (void)fakeService->registerAsService("mock");
 
-    void SetUp() override;
-    void TearDown() override;
+        android::hardware::ProcessState::self()->startThreadPool();
+        android::ProcessState::self()->startThreadPool();
+
+        startSurfaceFlinger();
+
+        // Fake composer wants to enable VSync injection
+        sFakeComposer->onSurfaceFlingerStart();
+    }
+
+    static void TearDownTestCase() {
+        // Fake composer needs to release SurfaceComposerClient before the stop.
+        sFakeComposer->onSurfaceFlingerStop();
+        stopSurfaceFlinger();
+        // TODO: This is deleted when the ComposerClient calls
+        // removeClient. Devise better lifetime control.
+        sFakeComposer = nullptr;
+    }
+
+    void SetUp() override {
+        ALOGI("TransactionTest::SetUp");
+        mComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+        ALOGI("TransactionTest::SetUp - display");
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+        ASSERT_FALSE(display == nullptr);
+
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+
+        const ui::Size& resolution = config.resolution;
+        mDisplayWidth = resolution.getWidth();
+        mDisplayHeight = resolution.getHeight();
+
+        // Background surface
+        mBGSurfaceControl =
+                mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
+                                               mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(mBGSurfaceControl != nullptr);
+        ASSERT_TRUE(mBGSurfaceControl->isValid());
+        fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
+
+        // Foreground surface
+        mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
+                                                           PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(mFGSurfaceControl != nullptr);
+        ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+        fillSurfaceRGBA8(mFGSurfaceControl, RED);
+
+        Transaction t;
+        t.setDisplayLayerStack(display, 0);
+
+        t.setLayer(mBGSurfaceControl, INT32_MAX - 2);
+        t.show(mBGSurfaceControl);
+
+        t.setLayer(mFGSurfaceControl, INT32_MAX - 1);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+        t.show(mFGSurfaceControl);
+
+        // Synchronous transaction will stop this thread, so we set up a
+        // delayed, off-thread vsync request before closing the
+        // transaction. In the test code this is usually done with
+        // TransactionScope. Leaving here in the 'vanilla' form for
+        // reference.
+        ASSERT_EQ(0, sFakeComposer->getFrameCount());
+        sFakeComposer->runVSyncAfter(1ms);
+        t.apply();
+        sFakeComposer->waitUntilFrame(1);
+
+        // Reference data. This is what the HWC should see.
+        static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
+        mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
+        mBaseFrame[BG_LAYER].mSwapCount = 1;
+        mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+        mBaseFrame[FG_LAYER].mSwapCount = 1;
+
+        auto frame = sFakeComposer->getFrameRects(0);
+        ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
+    }
+
+    void TearDown() override {
+        ALOGD("TransactionTest::TearDown");
+
+        mComposerClient->dispose();
+        mBGSurfaceControl = 0;
+        mFGSurfaceControl = 0;
+        mComposerClient = 0;
+
+        sFakeComposer->runVSyncAndWait();
+        mBaseFrame.clear();
+        sFakeComposer->clearFrames();
+        ASSERT_EQ(0, sFakeComposer->getFrameCount());
+
+        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+        std::vector<LayerDebugInfo> layers;
+        status_t result = sf->getLayerDebugInfo(&layers);
+        if (result != NO_ERROR) {
+            ALOGE("Failed to get layers %s %d", strerror(-result), result);
+        } else {
+            // If this fails, the test being torn down leaked layers.
+            EXPECT_EQ(0u, layers.size());
+            if (layers.size() > 0) {
+                for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
+                    std::cout << to_string(*layer).c_str();
+                }
+                // To ensure the next test has clean slate, will run the class
+                // tear down and setup here.
+                TearDownTestCase();
+                SetUpTestCase();
+            }
+        }
+        ALOGD("TransactionTest::TearDown - complete");
+    }
+
+    void Test_LayerMove() {
+        ALOGD("TransactionTest::LayerMove");
+
+        // The scope opens and closes a global transaction and, at the
+        // same time, makes sure the SurfaceFlinger progresses one frame
+        // after the transaction closes. The results of the transaction
+        // should be available in the latest frame stored by the fake
+        // composer.
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setPosition(mFGSurfaceControl, 128, 128);
+            // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
+            // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
+            //
+            // sFakeComposer->runVSyncAndWait();
+        }
+
+        fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+        sFakeComposer->runVSyncAndWait();
+
+        ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and
+                                                      // there's no extra frames.
+
+        // NOTE: Frame 0 is produced in the SetUp.
+        auto frame1Ref = mBaseFrame;
+        frame1Ref[FG_LAYER].mDisplayFrame =
+                hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
+        EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+        auto frame2Ref = frame1Ref;
+        frame2Ref[FG_LAYER].mSwapCount++;
+        EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+    }
+
+    void Test_LayerResize() {
+        ALOGD("TransactionTest::LayerResize");
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setSize(mFGSurfaceControl, 128, 128);
+        }
+
+        fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+        sFakeComposer->runVSyncAndWait();
+
+        ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and
+                                                      // there's no extra frames.
+
+        auto frame1Ref = mBaseFrame;
+        // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size
+        // posted.
+        EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+        auto frame2Ref = frame1Ref;
+        frame2Ref[FG_LAYER].mSwapCount++;
+        frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128};
+        frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f};
+        EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+    }
+
+    void Test_LayerCrop() {
+        // TODO: Add scaling to confirm that crop happens in buffer space?
+        {
+            TransactionScope ts(*sFakeComposer);
+            Rect cropRect(16, 16, 32, 32);
+            ts.setCrop_legacy(mFGSurfaceControl, cropRect);
+        }
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+        auto referenceFrame = mBaseFrame;
+        referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
+        referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerSetLayer() {
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
+        }
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+        // The layers will switch order, but both are rendered because the background layer is
+        // transparent (RGBA8888).
+        std::vector<RenderState> referenceFrame(2);
+        referenceFrame[0] = mBaseFrame[FG_LAYER];
+        referenceFrame[1] = mBaseFrame[BG_LAYER];
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerSetLayerOpaque() {
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
+            ts.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque,
+                        layer_state_t::eLayerOpaque);
+        }
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+        // The former foreground layer is now covered with opaque layer - it should have disappeared
+        std::vector<RenderState> referenceFrame(1);
+        referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_SetLayerStack() {
+        ALOGD("TransactionTest::SetLayerStack");
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setLayerStack(mFGSurfaceControl, 1);
+        }
+
+        // Foreground layer should have disappeared.
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+        std::vector<RenderState> refFrame(1);
+        refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+        EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerShowHide() {
+        ALOGD("TransactionTest::LayerShowHide");
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.hide(mFGSurfaceControl);
+        }
+
+        // Foreground layer should have disappeared.
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+        std::vector<RenderState> refFrame(1);
+        refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+        EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.show(mFGSurfaceControl);
+        }
+
+        // Foreground layer should be back
+        ASSERT_EQ(3, sFakeComposer->getFrameCount());
+        EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerSetAlpha() {
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setAlpha(mFGSurfaceControl, 0.75f);
+        }
+
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+        auto referenceFrame = mBaseFrame;
+        referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerSetFlags() {
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setFlags(mFGSurfaceControl, layer_state_t::eLayerHidden,
+                        layer_state_t::eLayerHidden);
+        }
+
+        // Foreground layer should have disappeared.
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+        std::vector<RenderState> refFrame(1);
+        refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+        EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerSetMatrix() {
+        struct matrixTestData {
+            float matrix[4];
+            hwc_transform_t expectedTransform;
+            hwc_rect_t expectedDisplayFrame;
+        };
+
+        // The matrix operates on the display frame and is applied before
+        // the position is added. So, the foreground layer rect is (0, 0,
+        // 64, 64) is first transformed, potentially yielding negative
+        // coordinates and then the position (64, 64) is added yielding
+        // the final on-screen rectangles given.
+
+        const matrixTestData MATRIX_TESTS[7] = // clang-format off
+                {{{-1.f, 0.f, 0.f, 1.f},    HWC_TRANSFORM_FLIP_H,           {0, 64, 64, 128}},
+                 {{1.f, 0.f, 0.f, -1.f},    HWC_TRANSFORM_FLIP_V,           {64, 0, 128, 64}},
+                 {{0.f, 1.f, -1.f, 0.f},    HWC_TRANSFORM_ROT_90,           {0, 64, 64, 128}},
+                 {{-1.f, 0.f, 0.f, -1.f},   HWC_TRANSFORM_ROT_180,          {0, 0, 64, 64}},
+                 {{0.f, -1.f, 1.f, 0.f},    HWC_TRANSFORM_ROT_270,          {64, 0, 128, 64}},
+                 {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_H_ROT_90,    {64, 64, 128, 128}},
+                 {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_V_ROT_90,    {64, 64, 128, 128}}};
+        // clang-format on
+        constexpr int TEST_COUNT = sizeof(MATRIX_TESTS) / sizeof(matrixTestData);
+
+        for (int i = 0; i < TEST_COUNT; i++) {
+            // TODO: How to leverage the HWC2 stringifiers?
+            const matrixTestData& xform = MATRIX_TESTS[i];
+            SCOPED_TRACE(i);
+            {
+                TransactionScope ts(*sFakeComposer);
+                ts.setMatrix(mFGSurfaceControl, xform.matrix[0], xform.matrix[1], xform.matrix[2],
+                             xform.matrix[3]);
+            }
+
+            auto referenceFrame = mBaseFrame;
+            referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
+            referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
+
+            EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+        }
+    }
+
+    void Test_DeferredTransaction() {
+        // Synchronization surface
+        constexpr static int SYNC_LAYER = 2;
+        auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
+                                                                 PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(syncSurfaceControl != nullptr);
+        ASSERT_TRUE(syncSurfaceControl->isValid());
+
+        fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setLayer(syncSurfaceControl, INT32_MAX - 1);
+            ts.setPosition(syncSurfaceControl, mDisplayWidth - 2, mDisplayHeight - 2);
+            ts.show(syncSurfaceControl);
+        }
+        auto referenceFrame = mBaseFrame;
+        referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2,
+                                                mDisplayWidth - 1, mDisplayHeight - 1));
+        referenceFrame[SYNC_LAYER].mSwapCount = 1;
+        EXPECT_EQ(2, sFakeComposer->getFrameCount());
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+        // set up two deferred transactions on different frames - these should not yield composited
+        // frames
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setAlpha(mFGSurfaceControl, 0.75);
+            ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
+                                            syncSurfaceControl->getSurface()->getNextFrameNumber());
+        }
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setPosition(mFGSurfaceControl, 128, 128);
+            ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
+                                            syncSurfaceControl->getSurface()->getNextFrameNumber() +
+                                                    1);
+        }
+        EXPECT_EQ(4, sFakeComposer->getFrameCount());
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+        // should trigger the first deferred transaction, but not the second one
+        fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+        sFakeComposer->runVSyncAndWait();
+        EXPECT_EQ(5, sFakeComposer->getFrameCount());
+
+        referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+        referenceFrame[SYNC_LAYER].mSwapCount++;
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+        // should show up immediately since it's not deferred
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setAlpha(mFGSurfaceControl, 1.0);
+        }
+        referenceFrame[FG_LAYER].mPlaneAlpha = 1.f;
+        EXPECT_EQ(6, sFakeComposer->getFrameCount());
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+        // trigger the second deferred transaction
+        fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+        sFakeComposer->runVSyncAndWait();
+        // TODO: Compute from layer size?
+        referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64};
+        referenceFrame[SYNC_LAYER].mSwapCount++;
+        EXPECT_EQ(7, sFakeComposer->getFrameCount());
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_SetRelativeLayer() {
+        constexpr int RELATIVE_LAYER = 2;
+        auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64,
+                                                                     64, PIXEL_FORMAT_RGBA_8888, 0);
+        fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
+
+        // Now we stack the surface above the foreground surface and make sure it is visible.
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setPosition(relativeSurfaceControl, 64, 64);
+            ts.show(relativeSurfaceControl);
+            ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl->getHandle(), 1);
+        }
+        auto referenceFrame = mBaseFrame;
+        // NOTE: All three layers will be visible as the surfaces are
+        // transparent because of the RGBA format.
+        referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+        referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+        // A call to setLayer will override a call to setRelativeLayer
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setLayer(relativeSurfaceControl, 0);
+        }
+
+        // Previous top layer will now appear at the bottom.
+        auto referenceFrame2 = mBaseFrame;
+        referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
+        EXPECT_EQ(3, sFakeComposer->getFrameCount());
+        EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+    }
 
     sp<SurfaceComposerClient> mComposerClient;
     sp<SurfaceControl> mBGSurfaceControl;
@@ -442,944 +1399,609 @@
     uint32_t mDisplayWidth;
     uint32_t mDisplayHeight;
 
-    static FakeComposerClient* sFakeComposer;
+    static inline FakeComposerClient* sFakeComposer;
 };
 
-FakeComposerClient* TransactionTest::sFakeComposer;
+using TransactionTest_2_1 = TransactionTest<FakeComposerService_2_1>;
 
-void TransactionTest::SetUpTestCase() {
-    // TODO: See TODO comment at DisplayTest::SetUp for background on
-    // the lifetime of the FakeComposerClient.
-    sFakeComposer = new FakeComposerClient;
-    sp<ComposerClient> client = new ComposerClient(sFakeComposer);
-    sp<IComposer> fakeService = new FakeComposerService(client);
-    (void)fakeService->registerAsService("mock");
-
-    android::hardware::ProcessState::self()->startThreadPool();
-    android::ProcessState::self()->startThreadPool();
-
-    startSurfaceFlinger();
-
-    // Fake composer wants to enable VSync injection
-    sFakeComposer->onSurfaceFlingerStart();
+TEST_F(TransactionTest_2_1, DISABLED_LayerMove) {
+    Test_LayerMove();
 }
 
-void TransactionTest::TearDownTestCase() {
-    // Fake composer needs to release SurfaceComposerClient before the stop.
-    sFakeComposer->onSurfaceFlingerStop();
-    stopSurfaceFlinger();
-    // TODO: This is deleted when the ComposerClient calls
-    // removeClient. Devise better lifetime control.
-    sFakeComposer = nullptr;
+TEST_F(TransactionTest_2_1, DISABLED_LayerResize) {
+    Test_LayerResize();
 }
 
-void TransactionTest::SetUp() {
-    ALOGI("TransactionTest::SetUp");
-    mComposerClient = new SurfaceComposerClient;
-    ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
-    ALOGI("TransactionTest::SetUp - display");
-    const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
-    ASSERT_FALSE(display == nullptr);
-
-    DisplayInfo info;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-
-    mDisplayWidth = info.w;
-    mDisplayHeight = info.h;
-
-    // Background surface
-    mBGSurfaceControl = mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
-                                                       mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
-    ASSERT_TRUE(mBGSurfaceControl != nullptr);
-    ASSERT_TRUE(mBGSurfaceControl->isValid());
-    fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
-
-    // Foreground surface
-    mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
-                                                       PIXEL_FORMAT_RGBA_8888, 0);
-    ASSERT_TRUE(mFGSurfaceControl != nullptr);
-    ASSERT_TRUE(mFGSurfaceControl->isValid());
-
-    fillSurfaceRGBA8(mFGSurfaceControl, RED);
-
-    Transaction t;
-    t.setDisplayLayerStack(display, 0);
-
-    t.setLayer(mBGSurfaceControl, INT32_MAX - 2);
-    t.show(mBGSurfaceControl);
-
-    t.setLayer(mFGSurfaceControl, INT32_MAX - 1);
-    t.setPosition(mFGSurfaceControl, 64, 64);
-    t.show(mFGSurfaceControl);
-
-    // Synchronous transaction will stop this thread, so we set up a
-    // delayed, off-thread vsync request before closing the
-    // transaction. In the test code this is usually done with
-    // TransactionScope. Leaving here in the 'vanilla' form for
-    // reference.
-    ASSERT_EQ(0, sFakeComposer->getFrameCount());
-    sFakeComposer->runVSyncAfter(1ms);
-    t.apply();
-    sFakeComposer->waitUntilFrame(1);
-
-    // Reference data. This is what the HWC should see.
-    static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
-    mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
-    mBaseFrame[BG_LAYER].mSwapCount = 1;
-    mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
-    mBaseFrame[FG_LAYER].mSwapCount = 1;
-
-    auto frame = sFakeComposer->getFrameRects(0);
-    ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
+TEST_F(TransactionTest_2_1, DISABLED_LayerCrop) {
+    Test_LayerCrop();
 }
 
-void TransactionTest::TearDown() {
-    ALOGD("TransactionTest::TearDown");
-
-    mComposerClient->dispose();
-    mBGSurfaceControl = 0;
-    mFGSurfaceControl = 0;
-    mComposerClient = 0;
-
-    sFakeComposer->runVSyncAndWait();
-    mBaseFrame.clear();
-    sFakeComposer->clearFrames();
-    ASSERT_EQ(0, sFakeComposer->getFrameCount());
-
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    std::vector<LayerDebugInfo> layers;
-    status_t result = sf->getLayerDebugInfo(&layers);
-    if (result != NO_ERROR) {
-        ALOGE("Failed to get layers %s %d", strerror(-result), result);
-    } else {
-        // If this fails, the test being torn down leaked layers.
-        EXPECT_EQ(0u, layers.size());
-        if (layers.size() > 0) {
-            for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
-                std::cout << to_string(*layer).c_str();
-            }
-            // To ensure the next test has clean slate, will run the class
-            // tear down and setup here.
-            TearDownTestCase();
-            SetUpTestCase();
-        }
-    }
-    ALOGD("TransactionTest::TearDown - complete");
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayer) {
+    Test_LayerSetLayer();
 }
 
-TEST_F(TransactionTest, LayerMove) {
-    ALOGD("TransactionTest::LayerMove");
-
-    // The scope opens and closes a global transaction and, at the
-    // same time, makes sure the SurfaceFlinger progresses one frame
-    // after the transaction closes. The results of the transaction
-    // should be available in the latest frame stored by the fake
-    // composer.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setPosition(mFGSurfaceControl, 128, 128);
-        // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
-        // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
-        //
-        // sFakeComposer->runVSyncAndWait();
-    }
-
-    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
-    sFakeComposer->runVSyncAndWait();
-
-    ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
-                                                  // no extra frames.
-
-    // NOTE: Frame 0 is produced in the SetUp.
-    auto frame1Ref = mBaseFrame;
-    frame1Ref[FG_LAYER].mDisplayFrame =
-            hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
-    EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
-
-    auto frame2Ref = frame1Ref;
-    frame2Ref[FG_LAYER].mSwapCount++;
-    EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayerOpaque) {
+    Test_LayerSetLayerOpaque();
 }
 
-TEST_F(TransactionTest, LayerResize) {
-    ALOGD("TransactionTest::LayerResize");
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 128, 128);
-    }
-
-    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
-    sFakeComposer->runVSyncAndWait();
-
-    ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
-                                                  // no extra frames.
-
-    auto frame1Ref = mBaseFrame;
-    // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size posted.
-    EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
-
-    auto frame2Ref = frame1Ref;
-    frame2Ref[FG_LAYER].mSwapCount++;
-    frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128};
-    frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f};
-    EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+TEST_F(TransactionTest_2_1, DISABLED_SetLayerStack) {
+    Test_SetLayerStack();
 }
 
-TEST_F(TransactionTest, LayerCrop) {
-    // TODO: Add scaling to confirm that crop happens in buffer space?
-    {
-        TransactionScope ts(*sFakeComposer);
-        Rect cropRect(16, 16, 32, 32);
-        ts.setCrop_legacy(mFGSurfaceControl, cropRect);
-    }
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_LayerShowHide) {
+    Test_LayerShowHide();
 }
 
-TEST_F(TransactionTest, LayerSetLayer) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
-    }
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-    // The layers will switch order, but both are rendered because the background layer is
-    // transparent (RGBA8888).
-    std::vector<RenderState> referenceFrame(2);
-    referenceFrame[0] = mBaseFrame[FG_LAYER];
-    referenceFrame[1] = mBaseFrame[BG_LAYER];
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetAlpha) {
+    Test_LayerSetAlpha();
 }
 
-TEST_F(TransactionTest, LayerSetLayerOpaque) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
-        ts.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque,
-                layer_state_t::eLayerOpaque);
-    }
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-    // The former foreground layer is now covered with opaque layer - it should have disappeared
-    std::vector<RenderState> referenceFrame(1);
-    referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetFlags) {
+    Test_LayerSetFlags();
 }
 
-TEST_F(TransactionTest, SetLayerStack) {
-    ALOGD("TransactionTest::SetLayerStack");
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setLayerStack(mFGSurfaceControl, 1);
-    }
-
-    // Foreground layer should have disappeared.
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-    std::vector<RenderState> refFrame(1);
-    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetMatrix) {
+    Test_LayerSetMatrix();
 }
 
-TEST_F(TransactionTest, LayerShowHide) {
-    ALOGD("TransactionTest::LayerShowHide");
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.hide(mFGSurfaceControl);
-    }
-
-    // Foreground layer should have disappeared.
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-    std::vector<RenderState> refFrame(1);
-    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mFGSurfaceControl);
-    }
-
-    // Foreground layer should be back
-    ASSERT_EQ(3, sFakeComposer->getFrameCount());
-    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_DeferredTransaction) {
+    Test_DeferredTransaction();
 }
 
-TEST_F(TransactionTest, LayerSetAlpha) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setAlpha(mFGSurfaceControl, 0.75f);
-    }
-
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_SetRelativeLayer) {
+    Test_SetRelativeLayer();
 }
 
-TEST_F(TransactionTest, LayerSetFlags) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setFlags(mFGSurfaceControl, layer_state_t::eLayerHidden,
-                layer_state_t::eLayerHidden);
-    }
+template <typename FakeComposerService>
+class ChildLayerTest : public TransactionTest<FakeComposerService> {
+    using Base = TransactionTest<FakeComposerService>;
 
-    // Foreground layer should have disappeared.
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-    std::vector<RenderState> refFrame(1);
-    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
-}
-
-TEST_F(TransactionTest, LayerSetMatrix) {
-    struct matrixTestData {
-        float matrix[4];
-        hwc_transform_t expectedTransform;
-        hwc_rect_t expectedDisplayFrame;
-    };
-
-    // The matrix operates on the display frame and is applied before
-    // the position is added. So, the foreground layer rect is (0, 0,
-    // 64, 64) is first transformed, potentially yielding negative
-    // coordinates and then the position (64, 64) is added yielding
-    // the final on-screen rectangles given.
-
-    const matrixTestData MATRIX_TESTS[7] = // clang-format off
-            {{{-1.f, 0.f, 0.f, 1.f},    HWC_TRANSFORM_FLIP_H,           {0, 64, 64, 128}},
-             {{1.f, 0.f, 0.f, -1.f},    HWC_TRANSFORM_FLIP_V,           {64, 0, 128, 64}},
-             {{0.f, 1.f, -1.f, 0.f},    HWC_TRANSFORM_ROT_90,           {0, 64, 64, 128}},
-             {{-1.f, 0.f, 0.f, -1.f},   HWC_TRANSFORM_ROT_180,          {0, 0, 64, 64}},
-             {{0.f, -1.f, 1.f, 0.f},    HWC_TRANSFORM_ROT_270,          {64, 0, 128, 64}},
-             {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_H_ROT_90,    {64, 64, 128, 128}},
-             {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_V_ROT_90,    {64, 64, 128, 128}}};
-    // clang-format on
-    constexpr int TEST_COUNT = sizeof(MATRIX_TESTS) / sizeof(matrixTestData);
-
-    for (int i = 0; i < TEST_COUNT; i++) {
-        // TODO: How to leverage the HWC2 stringifiers?
-        const matrixTestData& xform = MATRIX_TESTS[i];
-        SCOPED_TRACE(i);
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setMatrix(mFGSurfaceControl, xform.matrix[0], xform.matrix[1],
-                    xform.matrix[2], xform.matrix[3]);
-        }
-
-        auto referenceFrame = mBaseFrame;
-        referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
-        referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
-
-        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-    }
-}
-
-#if 0
-TEST_F(TransactionTest, LayerSetMatrix2) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        // TODO: PLEASE SPEC THE FUNCTION!
-        ts.setMatrix(mFGSurfaceControl, 0.11f, 0.123f,
-                -2.33f, 0.22f);
-    }
-    auto referenceFrame = mBaseFrame;
-    // TODO: Is this correct for sure?
-    //referenceFrame[FG_LAYER].mTransform = HWC_TRANSFORM_FLIP_V & HWC_TRANSFORM_ROT_90;
-
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-#endif
-
-TEST_F(TransactionTest, DeferredTransaction) {
-    // Synchronization surface
-    constexpr static int SYNC_LAYER = 2;
-    auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
-                                                             PIXEL_FORMAT_RGBA_8888, 0);
-    ASSERT_TRUE(syncSurfaceControl != nullptr);
-    ASSERT_TRUE(syncSurfaceControl->isValid());
-
-    fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setLayer(syncSurfaceControl, INT32_MAX - 1);
-        ts.setPosition(syncSurfaceControl, mDisplayWidth - 2, mDisplayHeight - 2);
-        ts.show(syncSurfaceControl);
-    }
-    auto referenceFrame = mBaseFrame;
-    referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2,
-                                            mDisplayWidth - 1, mDisplayHeight - 1));
-    referenceFrame[SYNC_LAYER].mSwapCount = 1;
-    EXPECT_EQ(2, sFakeComposer->getFrameCount());
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    // set up two deferred transactions on different frames - these should not yield composited
-    // frames
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setAlpha(mFGSurfaceControl, 0.75);
-        ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
-                                        syncSurfaceControl->getSurface()->getNextFrameNumber());
-    }
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setPosition(mFGSurfaceControl, 128, 128);
-        ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
-                                        syncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
-    }
-    EXPECT_EQ(4, sFakeComposer->getFrameCount());
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    // should trigger the first deferred transaction, but not the second one
-    fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
-    sFakeComposer->runVSyncAndWait();
-    EXPECT_EQ(5, sFakeComposer->getFrameCount());
-
-    referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
-    referenceFrame[SYNC_LAYER].mSwapCount++;
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    // should show up immediately since it's not deferred
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setAlpha(mFGSurfaceControl, 1.0);
-    }
-    referenceFrame[FG_LAYER].mPlaneAlpha = 1.f;
-    EXPECT_EQ(6, sFakeComposer->getFrameCount());
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    // trigger the second deferred transaction
-    fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
-    sFakeComposer->runVSyncAndWait();
-    // TODO: Compute from layer size?
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64};
-    referenceFrame[SYNC_LAYER].mSwapCount++;
-    EXPECT_EQ(7, sFakeComposer->getFrameCount());
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-
-TEST_F(TransactionTest, SetRelativeLayer) {
-    constexpr int RELATIVE_LAYER = 2;
-    auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64, 64,
-                                                                 PIXEL_FORMAT_RGBA_8888, 0);
-    fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
-
-    // Now we stack the surface above the foreground surface and make sure it is visible.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setPosition(relativeSurfaceControl, 64, 64);
-        ts.show(relativeSurfaceControl);
-        ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl->getHandle(), 1);
-    }
-    auto referenceFrame = mBaseFrame;
-    // NOTE: All three layers will be visible as the surfaces are
-    // transparent because of the RGBA format.
-    referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
-    referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    // A call to setLayer will override a call to setRelativeLayer
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setLayer(relativeSurfaceControl, 0);
-    }
-
-    // Previous top layer will now appear at the bottom.
-    auto referenceFrame2 = mBaseFrame;
-    referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
-    EXPECT_EQ(3, sFakeComposer->getFrameCount());
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
-}
-
-class ChildLayerTest : public TransactionTest {
 protected:
     constexpr static int CHILD_LAYER = 2;
 
     void SetUp() override {
-        TransactionTest::SetUp();
-        mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
-                                                PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+        Base::SetUp();
+        mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
+                                                      PIXEL_FORMAT_RGBA_8888, 0,
+                                                      Base::mFGSurfaceControl.get());
         fillSurfaceRGBA8(mChild, LIGHT_GRAY);
 
-        sFakeComposer->runVSyncAndWait();
-        mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
-        mBaseFrame[CHILD_LAYER].mSwapCount = 1;
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-        ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+        Base::sFakeComposer->runVSyncAndWait();
+        Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
+        Base::mBaseFrame[CHILD_LAYER].mSwapCount = 1;
+        ASSERT_EQ(2, Base::sFakeComposer->getFrameCount());
+        ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame()));
     }
+
     void TearDown() override {
         mChild = 0;
-        TransactionTest::TearDown();
+        Base::TearDown();
+    }
+
+    void Test_Positioning() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 10, 10);
+            // Move to the same position as in the original setup.
+            ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+        referenceFrame[CHILD_LAYER].mDisplayFrame =
+                hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+        }
+
+        auto referenceFrame2 = Base::mBaseFrame;
+        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
+        referenceFrame2[CHILD_LAYER].mDisplayFrame =
+                hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_Cropping() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 0, 0);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+            ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 5, 5));
+        }
+        // NOTE: The foreground surface would be occluded by the child
+        // now, but is included in the stack because the child is
+        // transparent.
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+        referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+        referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_Constraints() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+            ts.setPosition(mChild, 63, 63);
+        }
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
+        referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_Scaling() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+        }
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setMatrix(Base::mFGSurfaceControl, 2.0, 0, 0, 2.0);
+        }
+
+        auto referenceFrame2 = Base::mBaseFrame;
+        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+        referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerAlpha() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 0, 0);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+            ts.setAlpha(mChild, 0.5);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+        referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setAlpha(Base::mFGSurfaceControl, 0.5);
+        }
+
+        auto referenceFrame2 = referenceFrame;
+        referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f;
+        referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_ReparentChildren() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 10, 10);
+            ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+        }
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+        referenceFrame[CHILD_LAYER].mDisplayFrame =
+                hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.reparentChildren(Base::mFGSurfaceControl, Base::mBGSurfaceControl->getHandle());
+        }
+
+        auto referenceFrame2 = referenceFrame;
+        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+        referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10};
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_DetachChildrenSameClient() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 10, 10);
+            ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+        referenceFrame[CHILD_LAYER].mDisplayFrame =
+                hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+            ts.detachChildren(Base::mFGSurfaceControl);
+        }
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+            ts.hide(mChild);
+        }
+
+        std::vector<RenderState> refFrame(2);
+        refFrame[Base::BG_LAYER] = Base::mBaseFrame[Base::BG_LAYER];
+        refFrame[Base::FG_LAYER] = Base::mBaseFrame[Base::FG_LAYER];
+
+        EXPECT_TRUE(framesAreSame(refFrame, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_DetachChildrenDifferentClient() {
+        sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+        sp<SurfaceControl> childNewClient =
+                newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
+                                                 PIXEL_FORMAT_RGBA_8888, 0,
+                                                 Base::mFGSurfaceControl.get());
+        ASSERT_TRUE(childNewClient != nullptr);
+        ASSERT_TRUE(childNewClient->isValid());
+        fillSurfaceRGBA8(childNewClient, LIGHT_GRAY);
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.hide(mChild);
+            ts.show(childNewClient);
+            ts.setPosition(childNewClient, 10, 10);
+            ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+        referenceFrame[CHILD_LAYER].mDisplayFrame =
+                hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.detachChildren(Base::mFGSurfaceControl);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+        }
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+            ts.setPosition(childNewClient, 0, 0);
+            ts.hide(childNewClient);
+        }
+
+        // Nothing should have changed. The child control becomes a no-op
+        // zombie on detach. See comments for detachChildren in the
+        // SurfaceControl.h file.
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_InheritNonTransformScalingFromParent() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 0, 0);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+        }
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setOverrideScalingMode(Base::mFGSurfaceControl,
+                                      NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+            // We cause scaling by 2.
+            ts.setSize(Base::mFGSurfaceControl, 128, 128);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+        referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
+        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+        referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    // Regression test for b/37673612
+    void Test_ChildrenWithParentBufferTransform() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 0, 0);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+        }
+
+        // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
+        // the WM specified state size.
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setSize(Base::mFGSurfaceControl, 128, 64);
+        }
+
+        sp<Surface> s = Base::mFGSurfaceControl->getSurface();
+        auto anw = static_cast<ANativeWindow*>(s.get());
+        native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+        native_window_set_buffers_dimensions(anw, 64, 128);
+        fillSurfaceRGBA8(Base::mFGSurfaceControl, RED);
+        Base::sFakeComposer->runVSyncAndWait();
+
+        // The child should still be in the same place and not have any strange scaling as in
+        // b/37673612.
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64};
+        referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f};
+        referenceFrame[Base::FG_LAYER].mSwapCount++;
+        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_Bug36858924() {
+        // Destroy the child layer
+        mChild.clear();
+
+        // Now recreate it as hidden
+        mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
+                                                      PIXEL_FORMAT_RGBA_8888,
+                                                      ISurfaceComposerClient::eHidden,
+                                                      Base::mFGSurfaceControl.get());
+
+        // Show the child layer in a deferred transaction
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.deferTransactionUntil_legacy(mChild, Base::mFGSurfaceControl->getHandle(),
+                                            Base::mFGSurfaceControl->getSurface()
+                                                    ->getNextFrameNumber());
+            ts.show(mChild);
+        }
+
+        // Render the foreground surface a few times
+        //
+        // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the
+        // third frame because SurfaceFlinger would never process the deferred transaction and would
+        // therefore never acquire/release the first buffer
+        ALOGI("Filling 1");
+        fillSurfaceRGBA8(Base::mFGSurfaceControl, GREEN);
+        Base::sFakeComposer->runVSyncAndWait();
+        ALOGI("Filling 2");
+        fillSurfaceRGBA8(Base::mFGSurfaceControl, BLUE);
+        Base::sFakeComposer->runVSyncAndWait();
+        ALOGI("Filling 3");
+        fillSurfaceRGBA8(Base::mFGSurfaceControl, RED);
+        Base::sFakeComposer->runVSyncAndWait();
+        ALOGI("Filling 4");
+        fillSurfaceRGBA8(Base::mFGSurfaceControl, GREEN);
+        Base::sFakeComposer->runVSyncAndWait();
     }
 
     sp<SurfaceControl> mChild;
 };
 
-TEST_F(ChildLayerTest, Positioning) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 10, 10);
-        // Move to the same position as in the original setup.
-        ts.setPosition(mFGSurfaceControl, 64, 64);
-    }
+using ChildLayerTest_2_1 = ChildLayerTest<FakeComposerService_2_1>;
 
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
-    referenceFrame[CHILD_LAYER].mDisplayFrame =
-            hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-    }
-
-    auto referenceFrame2 = mBaseFrame;
-    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
-    referenceFrame2[CHILD_LAYER].mDisplayFrame =
-            hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_Positioning) {
+    Test_Positioning();
 }
 
-TEST_F(ChildLayerTest, Cropping) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 0, 0);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-        ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
-    }
-    // NOTE: The foreground surface would be occluded by the child
-    // now, but is included in the stack because the child is
-    // transparent.
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
-    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
-    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
-    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_Cropping) {
+    Test_Cropping();
 }
 
-TEST_F(ChildLayerTest, Constraints) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-        ts.setPosition(mChild, 63, 63);
-    }
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
-    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_Constraints) {
+    Test_Constraints();
 }
 
-TEST_F(ChildLayerTest, Scaling) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-    }
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setMatrix(mFGSurfaceControl, 2.0, 0, 0, 2.0);
-    }
-
-    auto referenceFrame2 = mBaseFrame;
-    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
-    referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_Scaling) {
+    Test_Scaling();
 }
 
-TEST_F(ChildLayerTest, LayerAlpha) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 0, 0);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-        ts.setAlpha(mChild, 0.5);
-    }
-
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-    referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setAlpha(mFGSurfaceControl, 0.5);
-    }
-
-    auto referenceFrame2 = referenceFrame;
-    referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f;
-    referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_LayerAlpha) {
+    Test_LayerAlpha();
 }
 
-TEST_F(ChildLayerTest, ReparentChildren) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 10, 10);
-        ts.setPosition(mFGSurfaceControl, 64, 64);
-    }
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
-    referenceFrame[CHILD_LAYER].mDisplayFrame =
-            hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle());
-    }
-
-    auto referenceFrame2 = referenceFrame;
-    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
-    referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10};
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_ReparentChildren) {
+    Test_ReparentChildren();
 }
 
-TEST_F(ChildLayerTest, DetachChildrenSameClient) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 10, 10);
-        ts.setPosition(mFGSurfaceControl, 64, 64);
-    }
-
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
-    referenceFrame[CHILD_LAYER].mDisplayFrame =
-            hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-        ts.detachChildren(mFGSurfaceControl);
-    }
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setPosition(mFGSurfaceControl, 64, 64);
-        ts.hide(mChild);
-    }
-
-    std::vector<RenderState> refFrame(2);
-    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-    refFrame[FG_LAYER] = mBaseFrame[FG_LAYER];
-
-    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_DetachChildrenSameClient) {
+    Test_DetachChildrenSameClient();
 }
 
-TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
-    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
-    sp<SurfaceControl> childNewClient =
-            newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    ASSERT_TRUE(childNewClient != nullptr);
-    ASSERT_TRUE(childNewClient->isValid());
-    fillSurfaceRGBA8(childNewClient, LIGHT_GRAY);
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.hide(mChild);
-        ts.show(childNewClient);
-        ts.setPosition(childNewClient, 10, 10);
-        ts.setPosition(mFGSurfaceControl, 64, 64);
-    }
-
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
-    referenceFrame[CHILD_LAYER].mDisplayFrame =
-            hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.detachChildren(mFGSurfaceControl);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-    }
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setPosition(mFGSurfaceControl, 64, 64);
-        ts.setPosition(childNewClient, 0, 0);
-        ts.hide(childNewClient);
-    }
-
-    // Nothing should have changed. The child control becomes a no-op
-    // zombie on detach. See comments for detachChildren in the
-    // SurfaceControl.h file.
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_DetachChildrenDifferentClient) {
+    Test_DetachChildrenDifferentClient();
 }
 
-TEST_F(ChildLayerTest, InheritNonTransformScalingFromParent) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 0, 0);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-    }
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-        // We cause scaling by 2.
-        ts.setSize(mFGSurfaceControl, 128, 128);
-    }
-
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
-    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
-    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
-    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_InheritNonTransformScalingFromParent) {
+    Test_InheritNonTransformScalingFromParent();
 }
 
 // Regression test for b/37673612
-TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 0, 0);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-    }
-
-    // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
-    // the WM specified state size.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 128, 64);
-    }
-
-    sp<Surface> s = mFGSurfaceControl->getSurface();
-    auto anw = static_cast<ANativeWindow*>(s.get());
-    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
-    native_window_set_buffers_dimensions(anw, 64, 128);
-    fillSurfaceRGBA8(mFGSurfaceControl, RED);
-    sFakeComposer->runVSyncAndWait();
-
-    // The child should still be in the same place and not have any strange scaling as in
-    // b/37673612.
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64};
-    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f};
-    referenceFrame[FG_LAYER].mSwapCount++;
-    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_ChildrenWithParentBufferTransform) {
+    Test_ChildrenWithParentBufferTransform();
 }
 
-TEST_F(ChildLayerTest, Bug36858924) {
-    // Destroy the child layer
-    mChild.clear();
-
-    // Now recreate it as hidden
-    mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
-                                            PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden,
-                                            mFGSurfaceControl.get());
-
-    // Show the child layer in a deferred transaction
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
-                                        mFGSurfaceControl->getSurface()->getNextFrameNumber());
-        ts.show(mChild);
-    }
-
-    // Render the foreground surface a few times
-    //
-    // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
-    // frame because SurfaceFlinger would never process the deferred transaction and would therefore
-    // never acquire/release the first buffer
-    ALOGI("Filling 1");
-    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
-    sFakeComposer->runVSyncAndWait();
-    ALOGI("Filling 2");
-    fillSurfaceRGBA8(mFGSurfaceControl, BLUE);
-    sFakeComposer->runVSyncAndWait();
-    ALOGI("Filling 3");
-    fillSurfaceRGBA8(mFGSurfaceControl, RED);
-    sFakeComposer->runVSyncAndWait();
-    ALOGI("Filling 4");
-    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
-    sFakeComposer->runVSyncAndWait();
+TEST_F(ChildLayerTest_2_1, DISABLED_Bug36858924) {
+    Test_Bug36858924();
 }
 
-class ChildColorLayerTest : public ChildLayerTest {
+template <typename FakeComposerService>
+class ChildColorLayerTest : public ChildLayerTest<FakeComposerService> {
+    using Base = ChildLayerTest<FakeComposerService>;
+
 protected:
     void SetUp() override {
-        TransactionTest::SetUp();
-        mChild = mComposerClient->createSurface(String8("Child surface"), 0, 0,
-                                                PIXEL_FORMAT_RGBA_8888,
-                                                ISurfaceComposerClient::eFXSurfaceColor,
-                                                mFGSurfaceControl.get());
+        Base::SetUp();
+        Base::mChild =
+                Base::mComposerClient->createSurface(String8("Child surface"), 0, 0,
+                                                     PIXEL_FORMAT_RGBA_8888,
+                                                     ISurfaceComposerClient::eFXSurfaceEffect,
+                                                     Base::mFGSurfaceControl.get());
         {
-            TransactionScope ts(*sFakeComposer);
-            ts.setColor(mChild,
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setColor(Base::mChild,
                         {LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f});
-            ts.setCrop_legacy(mChild, Rect(0, 0, 10, 10));
+            ts.setCrop_legacy(Base::mChild, Rect(0, 0, 10, 10));
         }
 
-        sFakeComposer->runVSyncAndWait();
-        mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
-        mBaseFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.0f, 0.0f, 0.0f, 0.0f};
-        mBaseFrame[CHILD_LAYER].mSwapCount = 0;
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-        ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+        Base::sFakeComposer->runVSyncAndWait();
+        Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
+        Base::mBaseFrame[Base::CHILD_LAYER].mSourceCrop = hwc_frect_t{0.0f, 0.0f, 0.0f, 0.0f};
+        Base::mBaseFrame[Base::CHILD_LAYER].mSwapCount = 0;
+        ASSERT_EQ(2, Base::sFakeComposer->getFrameCount());
+        ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerAlpha() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(Base::mChild);
+            ts.setPosition(Base::mChild, 0, 0);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+            ts.setAlpha(Base::mChild, 0.5);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+        referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+        referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f;
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setAlpha(Base::mFGSurfaceControl, 0.5);
+        }
+
+        auto referenceFrame2 = referenceFrame;
+        referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f;
+        referenceFrame2[Base::CHILD_LAYER].mPlaneAlpha = 0.25f;
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerZeroAlpha() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(Base::mChild);
+            ts.setPosition(Base::mChild, 0, 0);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+            ts.setAlpha(Base::mChild, 0.5);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+        referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+        referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f;
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setAlpha(Base::mFGSurfaceControl, 0.0f);
+        }
+
+        std::vector<RenderState> refFrame(1);
+        refFrame[Base::BG_LAYER] = Base::mBaseFrame[Base::BG_LAYER];
+
+        EXPECT_TRUE(framesAreSame(refFrame, Base::sFakeComposer->getLatestFrame()));
     }
 };
 
-TEST_F(ChildColorLayerTest, LayerAlpha) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 0, 0);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-        ts.setAlpha(mChild, 0.5);
-    }
+using ChildColorLayerTest_2_1 = ChildColorLayerTest<FakeComposerService_2_1>;
 
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-    referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setAlpha(mFGSurfaceControl, 0.5);
-    }
-
-    auto referenceFrame2 = referenceFrame;
-    referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f;
-    referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerAlpha) {
+    Test_LayerAlpha();
 }
 
-TEST_F(ChildColorLayerTest, LayerZeroAlpha) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 0, 0);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-        ts.setAlpha(mChild, 0.5);
-    }
-
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-    referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setAlpha(mFGSurfaceControl, 0.0f);
-    }
-
-    std::vector<RenderState> refFrame(1);
-    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-
-    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerZeroAlpha) {
+    Test_LayerZeroAlpha();
 }
 
-class LatchingTest : public TransactionTest {
+template <typename FakeComposerService>
+class LatchingTest : public TransactionTest<FakeComposerService> {
+    using Base = TransactionTest<FakeComposerService>;
+
 protected:
-    void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); }
+    void lockAndFillFGBuffer() { fillSurfaceRGBA8(Base::mFGSurfaceControl, RED, false); }
 
     void unlockFGBuffer() {
-        sp<Surface> s = mFGSurfaceControl->getSurface();
+        sp<Surface> s = Base::mFGSurfaceControl->getSurface();
         ASSERT_EQ(NO_ERROR, s->unlockAndPost());
-        sFakeComposer->runVSyncAndWait();
+        Base::sFakeComposer->runVSyncAndWait();
     }
 
     void completeFGResize() {
-        fillSurfaceRGBA8(mFGSurfaceControl, RED);
-        sFakeComposer->runVSyncAndWait();
+        fillSurfaceRGBA8(Base::mFGSurfaceControl, RED);
+        Base::sFakeComposer->runVSyncAndWait();
     }
     void restoreInitialState() {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 64, 64);
-        ts.setPosition(mFGSurfaceControl, 64, 64);
-        ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
+        TransactionScope ts(*Base::sFakeComposer);
+        ts.setSize(Base::mFGSurfaceControl, 64, 64);
+        ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+        ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 64, 64));
+    }
+
+    void Test_SurfacePositionLatching() {
+        // By default position can be updated even while
+        // a resize is pending.
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setSize(Base::mFGSurfaceControl, 32, 32);
+            ts.setPosition(Base::mFGSurfaceControl, 100, 100);
+        }
+
+        // The size should not have updated as we have not provided a new buffer.
+        auto referenceFrame1 = Base::mBaseFrame;
+        referenceFrame1[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64};
+        EXPECT_TRUE(framesAreSame(referenceFrame1, Base::sFakeComposer->getLatestFrame()));
+
+        restoreInitialState();
+
+        completeFGResize();
+
+        auto referenceFrame2 = Base::mBaseFrame;
+        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32};
+        referenceFrame2[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
+        referenceFrame2[Base::FG_LAYER].mSwapCount++;
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_CropLatching() {
+        // Normally the crop applies immediately even while a resize is pending.
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setSize(Base::mFGSurfaceControl, 128, 128);
+            ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 63, 63));
+        }
+
+        auto referenceFrame1 = Base::mBaseFrame;
+        referenceFrame1[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+        referenceFrame1[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+        EXPECT_TRUE(framesAreSame(referenceFrame1, Base::sFakeComposer->getLatestFrame()));
+
+        restoreInitialState();
+
+        completeFGResize();
+
+        auto referenceFrame2 = Base::mBaseFrame;
+        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+        referenceFrame2[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+        referenceFrame2[Base::FG_LAYER].mSwapCount++;
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
     }
 };
 
-TEST_F(LatchingTest, SurfacePositionLatching) {
-    // By default position can be updated even while
-    // a resize is pending.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 32, 32);
-        ts.setPosition(mFGSurfaceControl, 100, 100);
-    }
+using LatchingTest_2_1 = LatchingTest<FakeComposerService_2_1>;
 
-    // The size should not have updated as we have not provided a new buffer.
-    auto referenceFrame1 = mBaseFrame;
-    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64};
-    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
-
-    restoreInitialState();
-
-    // Now we repeat with setGeometryAppliesWithResize
-    // and verify the position DOESN'T latch.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setGeometryAppliesWithResize(mFGSurfaceControl);
-        ts.setSize(mFGSurfaceControl, 32, 32);
-        ts.setPosition(mFGSurfaceControl, 100, 100);
-    }
-    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
-
-    completeFGResize();
-
-    auto referenceFrame2 = mBaseFrame;
-    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32};
-    referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
-    referenceFrame2[FG_LAYER].mSwapCount++;
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(LatchingTest_2_1, DISABLED_SurfacePositionLatching) {
+    Test_SurfacePositionLatching();
 }
 
-TEST_F(LatchingTest, CropLatching) {
-    // Normally the crop applies immediately even while a resize is pending.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 128, 128);
-        ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63));
-    }
-
-    auto referenceFrame1 = mBaseFrame;
-    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
-    referenceFrame1[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
-    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
-
-    restoreInitialState();
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 128, 128);
-        ts.setGeometryAppliesWithResize(mFGSurfaceControl);
-        ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63));
-    }
-    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
-
-    completeFGResize();
-
-    auto referenceFrame2 = mBaseFrame;
-    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
-    referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
-    referenceFrame2[FG_LAYER].mSwapCount++;
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(LatchingTest_2_1, DISABLED_CropLatching) {
+    Test_CropLatching();
 }
 
 } // namespace
@@ -1387,8 +2009,11 @@
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
 
-    sftest::FakeHwcEnvironment* fakeEnvironment = new sftest::FakeHwcEnvironment;
+    auto* fakeEnvironment = new sftest::FakeHwcEnvironment;
     ::testing::AddGlobalTestEnvironment(fakeEnvironment);
     ::testing::InitGoogleMock(&argc, argv);
     return RUN_ALL_TESTS();
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/hwc2/Android.bp b/services/surfaceflinger/tests/hwc2/Android.bp
deleted file mode 100644
index 1c8e396..0000000
--- a/services/surfaceflinger/tests/hwc2/Android.bp
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-cc_test {
-    name: "test-hwc2",
-    defaults: ["surfaceflinger_defaults"],
-    cflags: [
-        "-DEGL_EGLEXT_PROTOTYPES",
-        "-DGL_GLEXT_PROTOTYPES",
-        "-fno-builtin",
-        "-fstack-protector-all",
-        "-g",
-        "-Wextra",
-    ],
-    srcs: [
-        "Hwc2Test.cpp",
-        "Hwc2TestProperties.cpp",
-        "Hwc2TestLayer.cpp",
-        "Hwc2TestLayers.cpp",
-        "Hwc2TestBuffer.cpp",
-        "Hwc2TestClientTarget.cpp",
-        "Hwc2TestVirtualDisplay.cpp",
-        "Hwc2TestPixelComparator.cpp",
-    ],
-    static_libs: [
-        "libadf",
-        "libadfhwc",
-        "libbase",
-        "libmath",
-    ],
-    shared_libs: [
-        "android.hardware.graphics.common@1.1",
-        "libcutils",
-        "libEGL",
-        "libGLESv2",
-        "libgui",
-        "libhardware",
-        "libhwui",
-        "liblog",
-        "libsync",
-        "libui",
-        "libutils",
-    ],
-}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
deleted file mode 100644
index 13774b4..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
+++ /dev/null
@@ -1,4772 +0,0 @@
-/*
- * Copyright (C) 2016 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 <array>
-#include <unordered_set>
-#include <unordered_map>
-#include <gtest/gtest.h>
-#include <dlfcn.h>
-#include <android-base/unique_fd.h>
-#include <hardware/hardware.h>
-#include <sync/sync.h>
-#include <ui/GraphicTypes.h>
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-#include "Hwc2TestLayer.h"
-#include "Hwc2TestLayers.h"
-#include "Hwc2TestClientTarget.h"
-#include "Hwc2TestVirtualDisplay.h"
-
-using android::ui::ColorMode;
-using android::ui::Dataspace;
-
-void hwc2TestHotplugCallback(hwc2_callback_data_t callbackData,
-        hwc2_display_t display, int32_t connected);
-void hwc2TestVsyncCallback(hwc2_callback_data_t callbackData,
-        hwc2_display_t display, int64_t timestamp);
-
-class Hwc2Test : public testing::Test {
-public:
-
-    virtual void SetUp()
-    {
-        hw_module_t const* hwc2Module;
-
-        int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &hwc2Module);
-        ASSERT_GE(err, 0) << "failed to get hwc hardware module: "
-                << strerror(-err);
-
-        /* The following method will fail if you have not run
-         * "adb shell stop" */
-        err = hwc2_open(hwc2Module, &mHwc2Device);
-        ASSERT_GE(err, 0) << "failed to open hwc hardware module: "
-                << strerror(-err);
-
-        populateDisplays();
-    }
-
-    virtual void TearDown()
-    {
-
-        for (auto itr = mLayers.begin(); itr != mLayers.end();) {
-            hwc2_display_t display = itr->first;
-            hwc2_layer_t layer = itr->second;
-            itr++;
-            /* Destroys and removes the layer from mLayers */
-            destroyLayer(display, layer);
-        }
-
-        for (auto itr = mActiveDisplays.begin(); itr != mActiveDisplays.end();) {
-            hwc2_display_t display = *itr;
-            itr++;
-            /* Sets power mode to off and removes the display from
-             * mActiveDisplays */
-            setPowerMode(display, HWC2_POWER_MODE_OFF);
-        }
-
-        for (auto itr = mVirtualDisplays.begin(); itr != mVirtualDisplays.end();) {
-            hwc2_display_t display = *itr;
-            itr++;
-            /* Destroys virtual displays */
-            destroyVirtualDisplay(display);
-        }
-
-        if (mHwc2Device)
-            hwc2_close(mHwc2Device);
-    }
-
-    void registerCallback(hwc2_callback_descriptor_t descriptor,
-            hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_REGISTER_CALLBACK>(
-                getFunction(HWC2_FUNCTION_REGISTER_CALLBACK));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, descriptor,
-                callbackData, pointer));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to register callback";
-        }
-    }
-
-    void getDisplayType(hwc2_display_t display, hwc2_display_type_t* outType,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_TYPE>(
-                getFunction(HWC2_FUNCTION_GET_DISPLAY_TYPE));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                    reinterpret_cast<int32_t*>(outType)));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get display type";
-        }
-    }
-
-    /* If the populateDisplays function is still receiving displays and the
-     * display is connected, the display handle is stored in mDisplays. */
-    void hotplugCallback(hwc2_display_t display, int32_t connected)
-    {
-        std::lock_guard<std::mutex> lock(mHotplugMutex);
-
-        if (mHotplugStatus != Hwc2TestHotplugStatus::Receiving)
-            return;
-
-        if (connected == HWC2_CONNECTION_CONNECTED)
-            mDisplays.insert(display);
-
-        mHotplugCv.notify_all();
-    }
-
-    void createLayer(hwc2_display_t display, hwc2_layer_t* outLayer,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_CREATE_LAYER>(
-                getFunction(HWC2_FUNCTION_CREATE_LAYER));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                outLayer));
-
-        if (err == HWC2_ERROR_NONE)
-            mLayers.insert(std::make_pair(display, *outLayer));
-
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to create layer";
-        }
-    }
-
-    void destroyLayer(hwc2_display_t display, hwc2_layer_t layer,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_DESTROY_LAYER>(
-                getFunction(HWC2_FUNCTION_DESTROY_LAYER));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer));
-
-        if (err == HWC2_ERROR_NONE)
-            mLayers.erase(std::make_pair(display, layer));
-
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to destroy layer "
-                    << layer;
-        }
-    }
-
-    void getDisplayAttribute(hwc2_display_t display, hwc2_config_t config,
-            hwc2_attribute_t attribute, int32_t* outValue,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>(
-                getFunction(HWC2_FUNCTION_GET_DISPLAY_ATTRIBUTE));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, config,
-                attribute, outValue));
-
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get display attribute "
-                    << getAttributeName(attribute) << " for config " << config;
-        }
-    }
-
-    void getDisplayConfigs(hwc2_display_t display,
-            std::vector<hwc2_config_t>* outConfigs,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_CONFIGS>(
-                getFunction(HWC2_FUNCTION_GET_DISPLAY_CONFIGS));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        uint32_t numConfigs = 0;
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                &numConfigs, nullptr));
-
-        if (err == HWC2_ERROR_NONE) {
-            outConfigs->resize(numConfigs);
-
-            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                    &numConfigs, outConfigs->data()));
-        }
-
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get configs for"
-                    " display " << display;
-        }
-    }
-
-    void getActiveConfig(hwc2_display_t display, hwc2_config_t* outConfig,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_GET_ACTIVE_CONFIG>(
-                getFunction(HWC2_FUNCTION_GET_ACTIVE_CONFIG));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                outConfig));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get active config on"
-                    " display " << display;
-        }
-    }
-
-    void setActiveConfig(hwc2_display_t display, hwc2_config_t config,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_ACTIVE_CONFIG>(
-                getFunction(HWC2_FUNCTION_SET_ACTIVE_CONFIG));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, config));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set active config "
-                    << config;
-        }
-    }
-
-    void getDozeSupport(hwc2_display_t display, int32_t* outSupport,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_GET_DOZE_SUPPORT>(
-                getFunction(HWC2_FUNCTION_GET_DOZE_SUPPORT));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                outSupport));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get doze support on"
-                    " display " << display;
-        }
-    }
-
-    void setPowerMode(hwc2_display_t display, hwc2_power_mode_t mode,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_POWER_MODE>(
-                getFunction(HWC2_FUNCTION_SET_POWER_MODE));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                mode));
-        if (outErr) {
-            *outErr = err;
-            if (err != HWC2_ERROR_NONE)
-                return;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set power mode "
-                    << getPowerModeName(mode) << " on display " << display;
-        }
-
-        if (mode == HWC2_POWER_MODE_OFF) {
-            mActiveDisplays.erase(display);
-        } else {
-            mActiveDisplays.insert(display);
-        }
-    }
-
-    void setVsyncEnabled(hwc2_display_t display, hwc2_vsync_t enabled,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_VSYNC_ENABLED>(
-                getFunction(HWC2_FUNCTION_SET_VSYNC_ENABLED));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                enabled));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set vsync enabled "
-                    << getVsyncName(enabled);
-        }
-    }
-
-    void vsyncCallback(hwc2_display_t display, int64_t timestamp)
-    {
-        std::lock_guard<std::mutex> lock(mVsyncMutex);
-        mVsyncDisplay = display;
-        mVsyncTimestamp = timestamp;
-        mVsyncCv.notify_all();
-    }
-
-    void getDisplayName(hwc2_display_t display, std::string* outName,
-                hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_NAME>(
-                getFunction(HWC2_FUNCTION_GET_DISPLAY_NAME));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        uint32_t size = 0;
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, &size,
-                nullptr));
-
-        if (err == HWC2_ERROR_NONE) {
-            std::vector<char> name(size);
-
-            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, &size,
-                    name.data()));
-
-            outName->assign(name.data());
-        }
-
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get display name for "
-                    << display;
-        }
-    }
-
-    void setLayerCompositionType(hwc2_display_t display, hwc2_layer_t layer,
-            hwc2_composition_t composition, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>(
-                getFunction(HWC2_FUNCTION_SET_LAYER_COMPOSITION_TYPE));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
-                composition));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer composition"
-                    " type " << getCompositionName(composition);
-        }
-    }
-
-    void setCursorPosition(hwc2_display_t display, hwc2_layer_t layer,
-            int32_t x, int32_t y, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_CURSOR_POSITION>(
-                getFunction(HWC2_FUNCTION_SET_CURSOR_POSITION));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer, x,
-                y));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_TRUE((err == HWC2_ERROR_NONE) ||
-                (err == HWC2_ERROR_BAD_LAYER)) <<
-                "failed to set cursor position";
-        }
-    }
-
-    void setLayerBlendMode(hwc2_display_t display, hwc2_layer_t layer,
-            hwc2_blend_mode_t mode, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_BLEND_MODE>(
-                getFunction(HWC2_FUNCTION_SET_LAYER_BLEND_MODE));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
-                mode));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer blend mode "
-                    << getBlendModeName(mode);
-        }
-    }
-
-    void setLayerBuffer(hwc2_display_t display, hwc2_layer_t layer,
-            buffer_handle_t buffer, int32_t acquireFence,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_BUFFER>(
-                getFunction(HWC2_FUNCTION_SET_LAYER_BUFFER));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
-                buffer, acquireFence));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer buffer";
-        }
-    }
-
-    void setLayerColor(hwc2_display_t display, hwc2_layer_t layer,
-            hwc_color_t color, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_COLOR>(
-                getFunction(HWC2_FUNCTION_SET_LAYER_COLOR));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
-                color));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer color";
-        }
-    }
-
-    void setLayerDataspace(hwc2_display_t display, hwc2_layer_t layer,
-            Dataspace dataspace, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_DATASPACE>(
-                getFunction(HWC2_FUNCTION_SET_LAYER_DATASPACE));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                layer, static_cast<int>(dataspace)));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer dataspace";
-        }
-    }
-
-    void setLayerDisplayFrame(hwc2_display_t display, hwc2_layer_t layer,
-            const hwc_rect_t& displayFrame, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>(
-                getFunction(HWC2_FUNCTION_SET_LAYER_DISPLAY_FRAME));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
-                displayFrame));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer display"
-                    " frame";
-        }
-    }
-
-    void setLayerPlaneAlpha(hwc2_display_t display, hwc2_layer_t layer,
-            float alpha, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_PLANE_ALPHA>(
-                getFunction(HWC2_FUNCTION_SET_LAYER_PLANE_ALPHA));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
-                alpha));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer plane alpha "
-                    << alpha;
-        }
-    }
-
-    void setLayerSourceCrop(hwc2_display_t display, hwc2_layer_t layer,
-            const hwc_frect_t& sourceCrop, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_SOURCE_CROP>(
-                getFunction(HWC2_FUNCTION_SET_LAYER_SOURCE_CROP));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
-                sourceCrop));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer source crop";
-        }
-    }
-
-    void setLayerSurfaceDamage(hwc2_display_t display, hwc2_layer_t layer,
-            const hwc_region_t& surfaceDamage, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>(
-                getFunction(HWC2_FUNCTION_SET_LAYER_SURFACE_DAMAGE));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
-                surfaceDamage));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer surface"
-                    " damage";
-        }
-    }
-
-    void setLayerTransform(hwc2_display_t display, hwc2_layer_t layer,
-            hwc_transform_t transform, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_TRANSFORM>(
-                getFunction(HWC2_FUNCTION_SET_LAYER_TRANSFORM));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
-                transform));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer transform "
-                    << getTransformName(transform);
-        }
-    }
-
-    void setLayerVisibleRegion(hwc2_display_t display, hwc2_layer_t layer,
-            const hwc_region_t& visibleRegion, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_VISIBLE_REGION>(
-                getFunction(HWC2_FUNCTION_SET_LAYER_VISIBLE_REGION));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
-                visibleRegion));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer visible"
-                    " region";
-        }
-    }
-
-    void setLayerZOrder(hwc2_display_t display, hwc2_layer_t layer,
-            uint32_t zOrder, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_LAYER_Z_ORDER>(
-                getFunction(HWC2_FUNCTION_SET_LAYER_Z_ORDER));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, layer,
-                zOrder));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set layer z order "
-                    << zOrder;
-        }
-    }
-
-    void validateDisplay(hwc2_display_t display, uint32_t* outNumTypes,
-            uint32_t* outNumRequests, hwc2_error_t* outErr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_VALIDATE_DISPLAY>(
-                getFunction(HWC2_FUNCTION_VALIDATE_DISPLAY));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        *outErr = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                outNumTypes, outNumRequests));
-    }
-
-    void validateDisplay(hwc2_display_t display, uint32_t* outNumTypes,
-            uint32_t* outNumRequests, bool* outHasChanges)
-    {
-        hwc2_error_t err = HWC2_ERROR_NONE;
-
-        EXPECT_NO_FATAL_FAILURE(validateDisplay(display, outNumTypes,
-                outNumRequests, &err));
-
-        if (err != HWC2_ERROR_HAS_CHANGES) {
-            *outHasChanges = false;
-            EXPECT_EQ(err, HWC2_ERROR_NONE) << "failed to validate display";
-        } else {
-            *outHasChanges = true;
-        }
-    }
-
-    void getDisplayRequests(hwc2_display_t display,
-            hwc2_display_request_t* outDisplayRequests,
-            std::vector<hwc2_layer_t>* outLayers,
-            std::vector<hwc2_layer_request_t>* outLayerRequests,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_GET_DISPLAY_REQUESTS>(
-                getFunction(HWC2_FUNCTION_GET_DISPLAY_REQUESTS));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        uint32_t numElements = 0;
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                reinterpret_cast<int32_t*>(outDisplayRequests), &numElements,
-                nullptr, nullptr));
-
-        if (err == HWC2_ERROR_NONE && numElements > 0) {
-            outLayers->resize(numElements);
-            outLayerRequests->resize(numElements);
-
-            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                    reinterpret_cast<int32_t*>(outDisplayRequests), &numElements,
-                    reinterpret_cast<uint64_t*>(outLayers->data()),
-                    reinterpret_cast<int32_t*>(outLayerRequests->data())));
-        }
-
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get display requests";
-        }
-    }
-
-    void handleRequests(hwc2_display_t display,
-            const std::vector<hwc2_layer_t>& layers, uint32_t numRequests,
-            std::set<hwc2_layer_t>* outClearLayers = nullptr,
-            bool* outFlipClientTarget = nullptr)
-    {
-        hwc2_display_request_t displayRequest =
-                static_cast<hwc2_display_request_t>(0);
-        std::vector<hwc2_layer_t> requestedLayers;
-        std::vector<hwc2_layer_request_t> requests;
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayRequests(display, &displayRequest,
-                &requestedLayers, &requests));
-
-        EXPECT_EQ(numRequests, requests.size()) << "validate returned "
-                << numRequests << " requests and get display requests returned "
-                << requests.size() << " requests";
-
-        for (size_t i = 0; i < requests.size(); i++) {
-            hwc2_layer_t requestedLayer = requestedLayers.at(i);
-            hwc2_layer_request_t request = requests.at(i);
-
-            EXPECT_EQ(std::count(layers.begin(), layers.end(), requestedLayer),
-                    1) << "get display requests returned an unknown layer";
-            EXPECT_NE(request, 0) << "returned empty request for layer "
-                    << requestedLayer;
-
-            if (outClearLayers && request
-                    == HWC2_LAYER_REQUEST_CLEAR_CLIENT_TARGET)
-                outClearLayers->insert(requestedLayer);
-        }
-
-        if (outFlipClientTarget)
-            *outFlipClientTarget = displayRequest
-                    & HWC2_DISPLAY_REQUEST_FLIP_CLIENT_TARGET;
-    }
-
-    void getChangedCompositionTypes(hwc2_display_t display,
-            std::vector<hwc2_layer_t>* outLayers,
-            std::vector<hwc2_composition_t>* outTypes,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>(
-                getFunction(HWC2_FUNCTION_GET_CHANGED_COMPOSITION_TYPES));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        uint32_t numElements = 0;
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                &numElements, nullptr, nullptr));
-
-        if (err == HWC2_ERROR_NONE && numElements > 0) {
-            outLayers->resize(numElements);
-            outTypes->resize(numElements);
-
-            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                    &numElements, reinterpret_cast<uint64_t*>(outLayers->data()),
-                    reinterpret_cast<int32_t*>(outTypes->data())));
-        }
-
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get changed"
-                    " composition types";
-        }
-    }
-
-    void handleCompositionChanges(hwc2_display_t display,
-            const Hwc2TestLayers& testLayers,
-            const std::vector<hwc2_layer_t>& layers, uint32_t numTypes,
-            std::set<hwc2_layer_t>* outClientLayers = nullptr)
-    {
-        std::vector<hwc2_layer_t> changedLayers;
-        std::vector<hwc2_composition_t> types;
-
-        ASSERT_NO_FATAL_FAILURE(getChangedCompositionTypes(display,
-                &changedLayers, &types));
-
-        EXPECT_EQ(numTypes, types.size()) << "validate returned "
-                << numTypes << " types and get changed composition types"
-                " returned " << types.size() << " types";
-
-        for (size_t i = 0; i < types.size(); i++) {
-
-            auto layer = std::find(layers.begin(), layers.end(),
-                    changedLayers.at(i));
-
-            EXPECT_TRUE(layer != layers.end() || !testLayers.contains(*layer))
-                    << "get changed composition types returned an unknown layer";
-
-            hwc2_composition_t requestedType = testLayers.getComposition(*layer);
-            hwc2_composition_t returnedType = types.at(i);
-
-            EXPECT_NE(returnedType, HWC2_COMPOSITION_INVALID) << "get changed"
-                    " composition types returned invalid composition";
-
-            switch (requestedType) {
-            case HWC2_COMPOSITION_CLIENT:
-                EXPECT_TRUE(false) << getCompositionName(returnedType)
-                        << " cannot be changed";
-                break;
-            case HWC2_COMPOSITION_DEVICE:
-            case HWC2_COMPOSITION_SOLID_COLOR:
-                EXPECT_EQ(returnedType, HWC2_COMPOSITION_CLIENT)
-                        << "composition of type "
-                        << getCompositionName(requestedType)
-                        << " can only be changed to "
-                        << getCompositionName(HWC2_COMPOSITION_CLIENT);
-                break;
-            case HWC2_COMPOSITION_CURSOR:
-            case HWC2_COMPOSITION_SIDEBAND:
-                EXPECT_TRUE(returnedType == HWC2_COMPOSITION_CLIENT
-                        || returnedType == HWC2_COMPOSITION_DEVICE)
-                        << "composition of type "
-                        << getCompositionName(requestedType)
-                        << " can only be changed to "
-                        << getCompositionName(HWC2_COMPOSITION_CLIENT) << " or "
-                        << getCompositionName(HWC2_COMPOSITION_DEVICE);
-                break;
-            default:
-                EXPECT_TRUE(false) << "unknown type "
-                        << getCompositionName(requestedType);
-                break;
-            }
-
-            if (outClientLayers)
-                if (returnedType == HWC2_COMPOSITION_CLIENT)
-                    outClientLayers->insert(*layer);
-        }
-
-        if (outClientLayers) {
-            for (auto layer : layers) {
-                if (testLayers.getComposition(layer) == HWC2_COMPOSITION_CLIENT)
-                    outClientLayers->insert(layer);
-            }
-        }
-    }
-
-    void acceptDisplayChanges(hwc2_display_t display,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(
-                getFunction(HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to accept display changes";
-        }
-    }
-
-    void getClientTargetSupport(hwc2_display_t display, int32_t width,
-            int32_t height, android_pixel_format_t format,
-            Dataspace dataspace, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>(
-                getFunction(HWC2_FUNCTION_GET_CLIENT_TARGET_SUPPORT));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, width,
-                height, format, static_cast<int>(dataspace)));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get client target"
-                    " support";
-        }
-    }
-
-    void setClientTarget(hwc2_display_t display, buffer_handle_t handle,
-            int32_t acquireFence, Dataspace dataspace,
-            hwc_region_t damage, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_CLIENT_TARGET>(
-                getFunction(HWC2_FUNCTION_SET_CLIENT_TARGET));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, handle,
-                acquireFence, static_cast<int>(dataspace), damage));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set client target";
-        }
-    }
-
-    void presentDisplay(hwc2_display_t display, int32_t* outPresentFence,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_PRESENT_DISPLAY>(
-                getFunction(HWC2_FUNCTION_PRESENT_DISPLAY));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                outPresentFence));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to present display";
-        }
-    }
-
-    void getReleaseFences(hwc2_display_t display,
-            std::vector<hwc2_layer_t>* outLayers,
-            std::vector<int32_t>* outFences, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_GET_RELEASE_FENCES>(
-                getFunction(HWC2_FUNCTION_GET_RELEASE_FENCES));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        uint32_t numElements = 0;
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                &numElements, nullptr, nullptr));
-
-        if (err == HWC2_ERROR_NONE) {
-            outLayers->resize(numElements);
-            outFences->resize(numElements);
-
-            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                    &numElements, outLayers->data(), outFences->data()));
-        }
-
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get release fences";
-        }
-    }
-
-    void getColorModes(hwc2_display_t display,
-            std::vector<ColorMode>* outColorModes,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_GET_COLOR_MODES>(
-                getFunction(HWC2_FUNCTION_GET_COLOR_MODES));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        uint32_t numColorModes = 0;
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                &numColorModes, nullptr));
-        if (err == HWC2_ERROR_NONE) {
-            outColorModes->resize(numColorModes);
-
-            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                    &numColorModes,
-                    reinterpret_cast<int32_t*>(outColorModes->data())));
-        }
-
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get color modes for"
-                    " display " << display;
-        }
-    }
-
-    void setColorMode(hwc2_display_t display, ColorMode colorMode,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_COLOR_MODE>(
-                getFunction(HWC2_FUNCTION_SET_COLOR_MODE));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                static_cast<int32_t>(colorMode)));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set color mode "
-                    << static_cast<int>(colorMode);
-        }
-    }
-
-    void getHdrCapabilities(hwc2_display_t display,
-            std::vector<android_hdr_t>* outTypes, float* outMaxLuminance,
-            float* outMaxAverageLuminance, float* outMinLuminance,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_GET_HDR_CAPABILITIES>(
-                getFunction(HWC2_FUNCTION_GET_HDR_CAPABILITIES));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        uint32_t numTypes = 0;
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                &numTypes, nullptr, outMaxLuminance, outMaxAverageLuminance,
-                outMinLuminance));
-
-        if (err == HWC2_ERROR_NONE) {
-            outTypes->resize(numTypes);
-
-            err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, &numTypes,
-                    reinterpret_cast<int32_t*>(outTypes->data()), outMaxLuminance,
-                    outMaxAverageLuminance, outMinLuminance));
-        }
-
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to get hdr capabilities"
-                    " for display " << display;
-        }
-    }
-
-    void setColorTransform(hwc2_display_t display,
-            const std::array<float, 16>& matrix, android_color_transform_t hint,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_COLOR_TRANSFORM>(
-                getFunction(HWC2_FUNCTION_SET_COLOR_TRANSFORM));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display,
-                matrix.data(), hint));
-
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set color transform "
-                    << hint;
-        }
-    }
-
-    void createVirtualDisplay(uint32_t width, uint32_t height,
-            android_pixel_format_t* outFormat, hwc2_display_t* outDisplay,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>(
-                getFunction(HWC2_FUNCTION_CREATE_VIRTUAL_DISPLAY));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, width, height,
-                reinterpret_cast<int32_t*>(outFormat), outDisplay));
-
-        if (err == HWC2_ERROR_NONE)
-            mVirtualDisplays.insert(*outDisplay);
-
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to create virtual display";
-        }
-    }
-
-    void destroyVirtualDisplay(hwc2_display_t display,
-            hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>(
-                getFunction(HWC2_FUNCTION_DESTROY_VIRTUAL_DISPLAY));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display));
-
-        if (err == HWC2_ERROR_NONE)
-            mVirtualDisplays.erase(display);
-
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to destroy virtual display";
-        }
-    }
-
-    void getMaxVirtualDisplayCount(uint32_t* outMaxCnt)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>(
-                getFunction(HWC2_FUNCTION_GET_MAX_VIRTUAL_DISPLAY_COUNT));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        *outMaxCnt = pfn(mHwc2Device);
-    }
-
-    void setOutputBuffer(hwc2_display_t display, buffer_handle_t buffer,
-            int32_t releaseFence, hwc2_error_t* outErr = nullptr)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_SET_OUTPUT_BUFFER>(
-                getFunction(HWC2_FUNCTION_SET_OUTPUT_BUFFER));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        auto err = static_cast<hwc2_error_t>(pfn(mHwc2Device, display, buffer,
-                releaseFence));
-        if (outErr) {
-            *outErr = err;
-        } else {
-            ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to set output buffer";
-        }
-    }
-
-    void dump(std::string* outBuffer)
-    {
-        auto pfn = reinterpret_cast<HWC2_PFN_DUMP>(
-                getFunction(HWC2_FUNCTION_DUMP));
-        ASSERT_TRUE(pfn) << "failed to get function";
-
-        uint32_t size = 0;
-
-        pfn(mHwc2Device, &size, nullptr);
-
-        std::vector<char> buffer(size);
-
-        pfn(mHwc2Device, &size, buffer.data());
-
-        outBuffer->assign(buffer.data());
-    }
-
-    void getBadDisplay(hwc2_display_t* outDisplay)
-    {
-        for (hwc2_display_t display = 0; display < UINT64_MAX; display++) {
-            if (mDisplays.count(display) == 0) {
-                *outDisplay = display;
-                return;
-            }
-        }
-        ASSERT_TRUE(false) << "Unable to find bad display. UINT64_MAX displays"
-                " are registered. This should never happen.";
-    }
-
-    void waitForVsync(hwc2_display_t* outDisplay = nullptr,
-            int64_t* outTimestamp = nullptr)
-    {
-        std::unique_lock<std::mutex> lock(mVsyncMutex);
-        ASSERT_EQ(mVsyncCv.wait_for(lock, std::chrono::seconds(3)),
-                std::cv_status::no_timeout) << "timed out attempting to get"
-                " vsync callback";
-        if (outDisplay)
-            *outDisplay = mVsyncDisplay;
-        if (outTimestamp)
-            *outTimestamp = mVsyncTimestamp;
-    }
-
-    void enableVsync(hwc2_display_t display)
-    {
-        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, this,
-                reinterpret_cast<hwc2_function_pointer_t>(
-                hwc2TestVsyncCallback)));
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
-    }
-
-    void disableVsync(hwc2_display_t display)
-    {
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
-    }
-
-protected:
-    hwc2_function_pointer_t getFunction(hwc2_function_descriptor_t descriptor)
-    {
-        return mHwc2Device->getFunction(mHwc2Device, descriptor);
-    }
-
-    void getCapabilities(std::vector<hwc2_capability_t>* outCapabilities)
-    {
-        uint32_t num = 0;
-
-        mHwc2Device->getCapabilities(mHwc2Device, &num, nullptr);
-
-        outCapabilities->resize(num);
-
-        mHwc2Device->getCapabilities(mHwc2Device, &num,
-                reinterpret_cast<int32_t*>(outCapabilities->data()));
-    }
-
-    /* Registers a hotplug callback and waits for hotplug callbacks. This
-     * function will have no effect if called more than once. */
-    void populateDisplays()
-    {
-        /* Sets the hotplug status to receiving */
-        {
-            std::lock_guard<std::mutex> lock(mHotplugMutex);
-
-            if (mHotplugStatus != Hwc2TestHotplugStatus::Init)
-                return;
-            mHotplugStatus = Hwc2TestHotplugStatus::Receiving;
-        }
-
-        /* Registers the callback. This function call cannot be locked because
-         * a callback could happen on the same thread */
-        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_HOTPLUG, this,
-                reinterpret_cast<hwc2_function_pointer_t>(
-                hwc2TestHotplugCallback)));
-
-        /* Waits for hotplug events. If a hotplug event has not come within 1
-         * second, stop waiting. */
-        std::unique_lock<std::mutex> lock(mHotplugMutex);
-
-        while (mHotplugCv.wait_for(lock, std::chrono::seconds(1)) !=
-                std::cv_status::timeout) { }
-
-        /* Sets the hotplug status to done. Future calls will have no effect */
-        mHotplugStatus = Hwc2TestHotplugStatus::Done;
-    }
-
-    /* NOTE: will create min(newlayerCnt, max supported layers) layers */
-    void createLayers(hwc2_display_t display,
-            std::vector<hwc2_layer_t>* outLayers, size_t newLayerCnt)
-    {
-        std::vector<hwc2_layer_t> newLayers;
-        hwc2_layer_t layer;
-        hwc2_error_t err = HWC2_ERROR_NONE;
-
-        for (size_t i = 0; i < newLayerCnt; i++) {
-
-            EXPECT_NO_FATAL_FAILURE(createLayer(display, &layer, &err));
-            if (err == HWC2_ERROR_NO_RESOURCES)
-                break;
-            if (err != HWC2_ERROR_NONE) {
-                newLayers.clear();
-                ASSERT_EQ(err, HWC2_ERROR_NONE) << "failed to create layer";
-            }
-            newLayers.push_back(layer);
-        }
-
-        *outLayers = std::move(newLayers);
-    }
-
-    void destroyLayers(hwc2_display_t display,
-            std::vector<hwc2_layer_t>&& layers)
-    {
-        for (hwc2_layer_t layer : layers) {
-            EXPECT_NO_FATAL_FAILURE(destroyLayer(display, layer));
-        }
-    }
-
-    void getInvalidConfig(hwc2_display_t display, hwc2_config_t* outConfig)
-    {
-        std::vector<hwc2_config_t> configs;
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-        hwc2_config_t CONFIG_MAX = UINT32_MAX;
-
-        ASSERT_LE(configs.size() - 1, CONFIG_MAX) << "every config value"
-                " (2^32 values) has been taken which shouldn't happen";
-
-        hwc2_config_t config;
-        for (config = 0; config < CONFIG_MAX; config++) {
-            if (std::count(configs.begin(), configs.end(), config) == 0)
-                break;
-        }
-
-        *outConfig = config;
-    }
-
-    /* Calls a set property function from Hwc2Test to set a property value from
-     * Hwc2TestLayer to hwc2_layer_t on hwc2_display_t */
-    using TestLayerPropertyFunction = void (*)(Hwc2Test* test,
-            hwc2_display_t display, hwc2_layer_t layer,
-            Hwc2TestLayer* testLayer, hwc2_error_t* outErr);
-
-    /* Calls a set property function from Hwc2Test to set property values from
-     * Hwc2TestLayers to hwc2_layer_t on hwc2_display_t */
-    using TestLayerPropertiesFunction = void (*)(Hwc2Test* test,
-            hwc2_display_t display, hwc2_layer_t layer,
-            Hwc2TestLayers* testLayers);
-
-    /* Calls a set property function from Hwc2Test to set a bad property value
-     * on hwc2_layer_t on hwc2_display_t */
-    using TestLayerPropertyBadLayerFunction = void (*)(Hwc2Test* test,
-            hwc2_display_t display, hwc2_layer_t layer,
-            Hwc2TestLayer* testLayer, hwc2_error_t* outErr);
-
-    /* Calls a set property function from Hwc2Test to set a bad property value
-     * on hwc2_layer_t on hwc2_display_t */
-    using TestLayerPropertyBadParameterFunction = void (*)(Hwc2Test* test,
-            hwc2_display_t display, hwc2_layer_t layer, hwc2_error_t* outErr);
-
-    /* Is called after a display is powered on and all layer properties have
-     * been set. It should be used to test functions such as validate, accepting
-     * changes, present, etc. */
-    using TestDisplayLayersFunction = void (*)(Hwc2Test* test,
-            hwc2_display_t display, const std::vector<hwc2_layer_t>& layers,
-            Hwc2TestLayers* testLayers);
-
-    /* It is called on an non validated display */
-    using TestDisplayNonValidatedLayersFunction = void (*)(Hwc2Test* test,
-            hwc2_display_t display, std::vector<hwc2_layer_t>* layers);
-
-    /* Tests client target support on a particular display and config */
-    using TestClientTargetSupportFunction = void (*)(Hwc2Test* test,
-            hwc2_display_t display,
-            const Hwc2TestClientTargetSupport& testClientTargetSupport);
-
-    /* Tests a particular active display config */
-    using TestActiveDisplayConfigFunction = void (*)(Hwc2Test* test,
-            hwc2_display_t display);
-
-    /* Tests a newly created virtual display */
-    using TestCreateVirtualDisplayFunction = void (*)(Hwc2Test* test,
-            hwc2_display_t display, Hwc2TestVirtualDisplay* testVirtualDisplay);
-
-    /* Advances a property of Hwc2TestLayer */
-    using AdvanceProperty = bool (*)(Hwc2TestLayer* testLayer);
-
-    /* Advances properties of Hwc2TestLayers */
-    using AdvanceProperties = bool (*)(Hwc2TestLayers* testLayer);
-
-    /* Advances properties of Hwc2TestClientTargetSupport */
-    using AdvanceClientTargetSupport = bool (*)(
-            Hwc2TestClientTargetSupport* testClientTargetSupport);
-
-    /* For each active display it cycles through each display config and tests
-     * each property value. It creates a layer, sets the property and then
-     * destroys the layer */
-    void setLayerProperty(Hwc2TestCoverage coverage,
-            TestLayerPropertyFunction function, AdvanceProperty advance)
-    {
-        for (auto display : mDisplays) {
-            std::vector<hwc2_config_t> configs;
-
-            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-            for (auto config : configs) {
-                hwc2_layer_t layer;
-                Area displayArea;
-
-                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
-                        &displayArea));
-                Hwc2TestLayer testLayer(coverage, displayArea);
-
-                do {
-                    ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
-
-                    ASSERT_NO_FATAL_FAILURE(function(this, display, layer,
-                            &testLayer, nullptr));
-
-                    ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
-                } while (advance(&testLayer));
-            }
-        }
-    }
-
-    /* For each active display it cycles through each display config and tests
-     * each property value. It creates a layer, cycles through each property
-     * value and updates the layer property value and then destroys the layer */
-    void setLayerPropertyUpdate(Hwc2TestCoverage coverage,
-            TestLayerPropertyFunction function, AdvanceProperty advance)
-    {
-        for (auto display : mDisplays) {
-            std::vector<hwc2_config_t> configs;
-
-            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-            for (auto config : configs) {
-                hwc2_layer_t layer;
-                Area displayArea;
-
-                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
-                        &displayArea));
-                Hwc2TestLayer testLayer(coverage, displayArea);
-
-                ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
-
-                do {
-                    ASSERT_NO_FATAL_FAILURE(function(this, display, layer,
-                            &testLayer, nullptr));
-                } while (advance(&testLayer));
-
-                ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
-            }
-        }
-    }
-
-    /* For each active display it cycles through each display config and tests
-     * each property value. It creates multiple layers, calls the
-     * TestLayerPropertiesFunction to set property values and then
-     * destroys the layers */
-    void setLayerProperties(Hwc2TestCoverage coverage, size_t layerCnt,
-            TestLayerPropertiesFunction function, AdvanceProperties advance)
-    {
-        for (auto display : mDisplays) {
-            std::vector<hwc2_config_t> configs;
-
-            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-            for (auto config : configs) {
-                std::vector<hwc2_layer_t> layers;
-                Area displayArea;
-
-                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
-                        &displayArea));
-
-                ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
-                Hwc2TestLayers testLayers(layers, coverage, displayArea);
-
-                do {
-                    for (auto layer : layers) {
-                        EXPECT_NO_FATAL_FAILURE(function(this, display, layer,
-                                &testLayers));
-                    }
-                } while (advance(&testLayers));
-
-                ASSERT_NO_FATAL_FAILURE(destroyLayers(display, std::move(layers)));
-            }
-        }
-    }
-
-    /* For each active display it cycles through each display config.
-     * 1) It attempts to set a valid property value to bad layer handle.
-     * 2) It creates a layer x and attempts to set a valid property value to
-     *    layer x + 1
-     * 3) It destroys the layer x and attempts to set a valid property value to
-     *    the destroyed layer x.
-     */
-    void setLayerPropertyBadLayer(Hwc2TestCoverage coverage,
-            TestLayerPropertyBadLayerFunction function)
-    {
-        for (auto display : mDisplays) {
-            std::vector<hwc2_config_t> configs;
-
-            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-            for (auto config : configs) {
-                hwc2_layer_t layer = 0;
-                Area displayArea;
-                hwc2_error_t err = HWC2_ERROR_NONE;
-
-                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
-                        &displayArea));
-                Hwc2TestLayer testLayer(coverage, displayArea);
-
-                ASSERT_NO_FATAL_FAILURE(function(this, display, layer,
-                        &testLayer, &err));
-                EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
-
-                ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
-
-                ASSERT_NO_FATAL_FAILURE(function(this, display, layer + 1,
-                        &testLayer, &err));
-                EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
-
-                ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
-
-                ASSERT_NO_FATAL_FAILURE(function(this, display, layer,
-                        &testLayer, &err));
-                EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
-            }
-        }
-    }
-
-    /* For each active display it cycles through each display config and tests
-     * each property value. It creates a layer, sets a bad property value and
-     * then destroys the layer */
-    void setLayerPropertyBadParameter(TestLayerPropertyBadParameterFunction function)
-    {
-        for (auto display : mDisplays) {
-            std::vector<hwc2_config_t> configs;
-
-            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-            for (auto config : configs) {
-                hwc2_layer_t layer;
-                hwc2_error_t err = HWC2_ERROR_NONE;
-
-                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-
-                ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
-
-                ASSERT_NO_FATAL_FAILURE(function(this, display, layer, &err));
-                EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong"
-                        " error code";
-
-                ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
-            }
-        }
-    }
-
-    /* For each active display it powers on the display, cycles through each
-     * config and creates a set of layers with a certain amount of coverage.
-     * For each active display, for each config and for each set of layers,
-     * it calls the TestDisplayLayersFunction */
-    void displayLayers(Hwc2TestCoverage coverage, size_t layerCnt,
-            TestDisplayLayersFunction function)
-    {
-        for (auto display : mDisplays) {
-            std::vector<hwc2_config_t> configs;
-
-            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
-
-            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-            for (auto config : configs) {
-                Area displayArea;
-                std::vector<hwc2_layer_t> layers;
-
-                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display, &displayArea));
-
-                ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
-                Hwc2TestLayers testLayers(layers, coverage, displayArea);
-
-                do {
-                    bool skip;
-
-                    ASSERT_NO_FATAL_FAILURE(setLayerProperties(display, layers,
-                            &testLayers, &skip));
-                    if (!skip)
-                        EXPECT_NO_FATAL_FAILURE(function(this, display, layers,
-                                &testLayers));
-
-                } while (testLayers.advance());
-
-                ASSERT_NO_FATAL_FAILURE(destroyLayers(display,
-                        std::move(layers)));
-            }
-
-            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-        }
-    }
-
-    /* For each active display, it calls the
-     * TestDisplayNonValidatedLayersFunction on a variety on non-validated
-     * layer combinations */
-    void displayNonValidatedLayers(size_t layerCnt,
-            TestDisplayNonValidatedLayersFunction function)
-    {
-        for (auto display : mDisplays) {
-            uint32_t numTypes, numRequests;
-            std::vector<hwc2_layer_t> layers;
-            bool hasChanges;
-
-            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
-
-            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
-
-            ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
-
-            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
-
-            for (auto layer : layers) {
-                ASSERT_NO_FATAL_FAILURE(setLayerCompositionType(display, layer,
-                        HWC2_COMPOSITION_CLIENT));
-            }
-
-            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
-
-            ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes,
-                    &numRequests, &hasChanges));
-
-            for (auto layer : layers) {
-                ASSERT_NO_FATAL_FAILURE(setLayerCompositionType(display, layer,
-                        HWC2_COMPOSITION_DEVICE));
-            }
-
-            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
-
-            ASSERT_NO_FATAL_FAILURE(destroyLayers(display, std::move(layers)));
-
-            EXPECT_NO_FATAL_FAILURE(function(this, display, &layers));
-
-            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-        }
-    }
-
-    /* Test client target support on each config on each active display */
-    void setClientTargetSupport(Hwc2TestCoverage coverage,
-            TestClientTargetSupportFunction function,
-            AdvanceClientTargetSupport advance)
-    {
-        for (auto display : mDisplays) {
-            std::vector<hwc2_config_t> configs;
-
-            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-            for (auto config : configs) {
-                Area displayArea;
-
-                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
-                        &displayArea));
-                Hwc2TestClientTargetSupport testClientTargetSupport(coverage,
-                        displayArea);
-
-                do {
-                    EXPECT_NO_FATAL_FAILURE(function(this, display,
-                            testClientTargetSupport));
-
-                } while (advance(&testClientTargetSupport));
-            }
-        }
-    }
-
-    /* Cycles through each config on each active display and calls
-     * a TestActiveDisplayConfigFunction */
-    void setActiveDisplayConfig(TestActiveDisplayConfigFunction function)
-    {
-        for (auto display : mDisplays) {
-            std::vector<hwc2_config_t> configs;
-
-            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-            for (auto config : configs) {
-                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-
-                EXPECT_NO_FATAL_FAILURE(function(this, display));
-            }
-        }
-    }
-
-    /* Creates a virtual display for testing */
-    void createVirtualDisplay(Hwc2TestCoverage coverage,
-            TestCreateVirtualDisplayFunction function)
-    {
-        Hwc2TestVirtualDisplay testVirtualDisplay(coverage);
-
-        do {
-            hwc2_display_t display;
-            hwc2_error_t err = HWC2_ERROR_NONE;
-
-            const UnsignedArea& dimension =
-                    testVirtualDisplay.getDisplayDimension();
-            android_pixel_format_t desiredFormat = HAL_PIXEL_FORMAT_RGBA_8888;
-
-            ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(dimension.width,
-                    dimension.height, &desiredFormat, &display, &err));
-
-            EXPECT_TRUE(err == HWC2_ERROR_NONE || err == HWC2_ERROR_NO_RESOURCES
-                    || err == HWC2_ERROR_UNSUPPORTED)
-                    << "returned wrong error code";
-            EXPECT_GE(desiredFormat, 0) << "invalid format";
-
-            if (err != HWC2_ERROR_NONE)
-                continue;
-
-            EXPECT_NO_FATAL_FAILURE(function(this, display,
-                    &testVirtualDisplay));
-
-            ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display));
-
-        } while (testVirtualDisplay.advance());
-    }
-
-
-    void getActiveConfigAttribute(hwc2_display_t display,
-            hwc2_attribute_t attribute, int32_t* outValue)
-    {
-        hwc2_config_t config;
-        ASSERT_NO_FATAL_FAILURE(getActiveConfig(display, &config));
-        ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
-                attribute, outValue));
-        ASSERT_GE(*outValue, 0) << "failed to get valid "
-                << getAttributeName(attribute);
-    }
-
-    void getActiveDisplayArea(hwc2_display_t display, Area* displayArea)
-    {
-        ASSERT_NO_FATAL_FAILURE(getActiveConfigAttribute(display,
-                HWC2_ATTRIBUTE_WIDTH, &displayArea->width));
-        ASSERT_NO_FATAL_FAILURE(getActiveConfigAttribute(display,
-                HWC2_ATTRIBUTE_HEIGHT, &displayArea->height));
-    }
-
-    void closeFences(hwc2_display_t display, int32_t presentFence)
-    {
-        std::vector<hwc2_layer_t> layers;
-        std::vector<int32_t> fences;
-        const int msWait = 3000;
-
-        if (presentFence >= 0) {
-            ASSERT_GE(sync_wait(presentFence, msWait), 0);
-            close(presentFence);
-        }
-
-        ASSERT_NO_FATAL_FAILURE(getReleaseFences(display, &layers, &fences));
-        EXPECT_EQ(layers.size(), fences.size());
-
-        for (int32_t fence : fences) {
-            if (fence >= 0) {
-                EXPECT_GE(sync_wait(fence, msWait), 0);
-                close(fence);
-            }
-        }
-    }
-
-    void setLayerProperties(hwc2_display_t display, hwc2_layer_t layer,
-            Hwc2TestLayers* testLayers, bool* outSkip)
-    {
-        hwc2_composition_t composition;
-        buffer_handle_t handle = nullptr;
-        int32_t acquireFence;
-        hwc2_error_t err = HWC2_ERROR_NONE;
-        *outSkip = true;
-
-        if (!testLayers->contains(layer))
-            return;
-
-        composition = testLayers->getComposition(layer);
-
-        /* If the device cannot support a buffer format, then do not continue */
-        if ((composition == HWC2_COMPOSITION_DEVICE
-                || composition == HWC2_COMPOSITION_CURSOR)
-                && testLayers->getBuffer(layer, &handle, &acquireFence) < 0)
-            return;
-
-        EXPECT_NO_FATAL_FAILURE(setLayerCompositionType(display, layer,
-                composition, &err));
-        if (err == HWC2_ERROR_UNSUPPORTED)
-            EXPECT_TRUE(composition != HWC2_COMPOSITION_CLIENT
-                    && composition != HWC2_COMPOSITION_DEVICE);
-
-        const hwc_rect_t cursor = testLayers->getCursorPosition(layer);
-
-        EXPECT_NO_FATAL_FAILURE(setLayerBuffer(display, layer, handle,
-                acquireFence));
-        EXPECT_NO_FATAL_FAILURE(setLayerBlendMode(display, layer,
-                testLayers->getBlendMode(layer)));
-        EXPECT_NO_FATAL_FAILURE(setLayerColor(display, layer,
-                testLayers->getColor(layer)));
-        if (composition == HWC2_COMPOSITION_CURSOR)
-            EXPECT_NO_FATAL_FAILURE(setCursorPosition(display, layer,
-            cursor.left, cursor.top));
-        EXPECT_NO_FATAL_FAILURE(setLayerDataspace(display, layer,
-                testLayers->getDataspace(layer)));
-        EXPECT_NO_FATAL_FAILURE(setLayerDisplayFrame(display, layer,
-                testLayers->getDisplayFrame(layer)));
-        EXPECT_NO_FATAL_FAILURE(setLayerPlaneAlpha(display, layer,
-                testLayers->getPlaneAlpha(layer)));
-        EXPECT_NO_FATAL_FAILURE(setLayerSourceCrop(display, layer,
-                testLayers->getSourceCrop(layer)));
-        EXPECT_NO_FATAL_FAILURE(setLayerSurfaceDamage(display, layer,
-                testLayers->getSurfaceDamage(layer)));
-        EXPECT_NO_FATAL_FAILURE(setLayerTransform(display, layer,
-                testLayers->getTransform(layer)));
-        EXPECT_NO_FATAL_FAILURE(setLayerVisibleRegion(display, layer,
-                testLayers->getVisibleRegion(layer)));
-        EXPECT_NO_FATAL_FAILURE(setLayerZOrder(display, layer,
-                testLayers->getZOrder(layer)));
-
-        *outSkip = false;
-    }
-
-    void setLayerProperties(hwc2_display_t display,
-            const std::vector<hwc2_layer_t>& layers,
-            Hwc2TestLayers* testLayers, bool* outSkip)
-    {
-        for (auto layer : layers) {
-            EXPECT_NO_FATAL_FAILURE(setLayerProperties(display, layer,
-                    testLayers, outSkip));
-            if (*outSkip)
-                return;
-        }
-    }
-
-    void setClientTarget(hwc2_display_t display,
-            Hwc2TestClientTarget* testClientTarget,
-            const Hwc2TestLayers& testLayers,
-            const std::set<hwc2_layer_t>& clientLayers,
-            const std::set<hwc2_layer_t>& clearLayers, bool flipClientTarget,
-            const Area& displayArea)
-    {
-        Dataspace dataspace = Dataspace::UNKNOWN;
-        hwc_region_t damage = { };
-        buffer_handle_t handle;
-        int32_t acquireFence;
-
-        ASSERT_EQ(testClientTarget->getBuffer(testLayers, clientLayers,
-                clearLayers, flipClientTarget, displayArea, &handle,
-                &acquireFence), 0);
-        EXPECT_NO_FATAL_FAILURE(setClientTarget(display, handle, acquireFence,
-                dataspace, damage));
-    }
-
-    void presentDisplays(size_t layerCnt, Hwc2TestCoverage coverage,
-            const std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>&
-            coverageExceptions, bool optimize)
-    {
-        for (auto display : mDisplays) {
-            std::vector<hwc2_config_t> configs;
-
-            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
-            ASSERT_NO_FATAL_FAILURE(enableVsync(display));
-
-            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-            for (auto config : configs) {
-                Area displayArea;
-                std::vector<hwc2_layer_t> layers;
-
-                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
-                        &displayArea));
-
-                ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
-                Hwc2TestLayers testLayers(layers, coverage, displayArea,
-                        coverageExceptions);
-
-                if (optimize && !testLayers.optimizeLayouts())
-                    continue;
-
-                std::set<hwc2_layer_t> clientLayers;
-                std::set<hwc2_layer_t> clearLayers;
-                Hwc2TestClientTarget testClientTarget;
-
-                do {
-                    uint32_t numTypes, numRequests;
-                    bool hasChanges, skip;
-                    bool flipClientTarget;
-                    int32_t presentFence;
-
-                    ASSERT_NO_FATAL_FAILURE(setLayerProperties(display, layers,
-                            &testLayers, &skip));
-                    if (skip)
-                        continue;
-
-                    ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes,
-                            &numRequests, &hasChanges));
-                    if (hasChanges)
-                        EXPECT_LE(numTypes, static_cast<uint32_t>(layers.size()))
-                                << "wrong number of requests";
-
-                    ASSERT_NO_FATAL_FAILURE(handleCompositionChanges(display,
-                            testLayers, layers, numTypes, &clientLayers));
-                    ASSERT_NO_FATAL_FAILURE(handleRequests(display, layers,
-                            numRequests, &clearLayers, &flipClientTarget));
-                    ASSERT_NO_FATAL_FAILURE(setClientTarget(display,
-                            &testClientTarget, testLayers, clientLayers,
-                            clearLayers, flipClientTarget, displayArea));
-                    ASSERT_NO_FATAL_FAILURE(acceptDisplayChanges(display));
-
-                    ASSERT_NO_FATAL_FAILURE(waitForVsync());
-
-                    EXPECT_NO_FATAL_FAILURE(presentDisplay(display,
-                            &presentFence));
-
-                    ASSERT_NO_FATAL_FAILURE(closeFences(display, presentFence));
-
-                } while (testLayers.advance());
-
-                ASSERT_NO_FATAL_FAILURE(destroyLayers(display,
-                        std::move(layers)));
-            }
-
-            ASSERT_NO_FATAL_FAILURE(disableVsync(display));
-            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-        }
-    }
-
-    void createAndPresentVirtualDisplay(size_t layerCnt,
-            Hwc2TestCoverage coverage,
-            const std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>&
-            coverageExceptions)
-    {
-        Hwc2TestVirtualDisplay testVirtualDisplay(coverage);
-        hwc2_display_t display;
-        android_pixel_format_t desiredFormat = HAL_PIXEL_FORMAT_RGBA_8888;
-
-        do {
-            // Items dependent on the display dimensions
-            hwc2_error_t err = HWC2_ERROR_NONE;
-            const UnsignedArea& dimension =
-                    testVirtualDisplay.getDisplayDimension();
-            ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(dimension.width,
-                    dimension.height, &desiredFormat, &display, &err));
-            ASSERT_TRUE(err == HWC2_ERROR_NONE)
-                    << "Cannot allocate virtual display";
-
-            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
-            ASSERT_NO_FATAL_FAILURE(enableVsync(display));
-
-            std::vector<hwc2_config_t> configs;
-            ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-            for (auto config : configs) {
-                ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-
-                Area displayArea;
-                ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
-                        &displayArea));
-
-                std::vector<hwc2_layer_t> layers;
-                ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers,
-                        layerCnt));
-                Hwc2TestLayers testLayers(layers, coverage, displayArea,
-                        coverageExceptions);
-
-                /*
-                 * Layouts that do not cover an entire virtual display will
-                 * cause undefined behavior.
-                 * Enable optimizeLayouts to avoid this.
-                 */
-                testLayers.optimizeLayouts();
-                do {
-                    // Items dependent on the testLayers properties
-                    std::set<hwc2_layer_t> clientLayers;
-                    std::set<hwc2_layer_t> clearLayers;
-                    uint32_t numTypes, numRequests;
-                    bool hasChanges, skip;
-                    bool flipClientTarget;
-                    int32_t presentFence;
-                    Hwc2TestClientTarget testClientTarget;
-                    buffer_handle_t outputBufferHandle;
-                    android::base::unique_fd outputBufferReleaseFence;
-
-                    ASSERT_NO_FATAL_FAILURE(setLayerProperties(display, layers,
-                            &testLayers, &skip));
-
-                    if (skip)
-                        continue;
-
-                    ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes,
-                            &numRequests, &hasChanges));
-
-                    if (hasChanges)
-                        EXPECT_LE(numTypes, static_cast<uint32_t>(layers.size()))
-                                << "wrong number of requests";
-
-                    ASSERT_NO_FATAL_FAILURE(handleCompositionChanges(display,
-                            testLayers, layers, numTypes, &clientLayers));
-
-                    ASSERT_NO_FATAL_FAILURE(handleRequests(display, layers,
-                            numRequests, &clearLayers, &flipClientTarget));
-                    ASSERT_NO_FATAL_FAILURE(setClientTarget(display,
-                            &testClientTarget, testLayers, clientLayers,
-                            clearLayers, flipClientTarget, displayArea));
-                    ASSERT_NO_FATAL_FAILURE(acceptDisplayChanges(display));
-
-                    ASSERT_EQ(testVirtualDisplay.getOutputBuffer(
-                            &outputBufferHandle, &outputBufferReleaseFence), 0);
-                    ASSERT_NO_FATAL_FAILURE(setOutputBuffer(display,
-                            outputBufferHandle, outputBufferReleaseFence));
-
-                    EXPECT_NO_FATAL_FAILURE(presentDisplay(display,
-                            &presentFence));
-                    ASSERT_NO_FATAL_FAILURE(closeFences(display, presentFence));
-
-                    ASSERT_EQ(testVirtualDisplay.verifyOutputBuffer(&testLayers,
-                            &layers, &clearLayers), 0);
-
-                    /*
-                     * Upscaling the image causes minor pixel differences.
-                     * Work around this by using some threshold.
-                     *
-                     * Fail test if we are off by more than 1% of our
-                     * pixels.
-                     */
-                    ComparatorResult& comparatorResult = ComparatorResult::get();
-                    int threshold = (dimension.width * dimension.height) / 100;
-                    double diffPercent = (comparatorResult.getDifferentPixelCount() * 100.0) /
-                            (dimension.width * dimension.height);
-
-                    if (comparatorResult.getDifferentPixelCount() != 0)
-                        EXPECT_TRUE(false)
-                                << comparatorResult.getDifferentPixelCount() << " pixels ("
-                                << diffPercent << "%) are different.";
-
-                    if (comparatorResult.getDifferentPixelCount() > threshold) {
-                        EXPECT_TRUE(false)
-                                << "Mismatched pixel count exceeds threshold. "
-                                << "Writing buffers to file.";
-
-                        const ::testing::TestInfo* const test_info =
-                                ::testing::UnitTest::GetInstance()
-                                ->current_test_info();
-
-                        EXPECT_EQ(testVirtualDisplay.writeBuffersToFile(
-                                test_info->name()), 0)
-                                << "Failed to write buffers.";
-                    }
-
-                    ASSERT_LE(comparatorResult.getDifferentPixelCount(), threshold)
-                            << comparatorResult.getDifferentPixelCount() << " pixels ("
-                            << diffPercent << "%) are different. "
-                            << "Exceeds 1% threshold, terminating test. "
-                            << "Test case: " << testLayers.dump();
-
-                } while (testLayers.advance());
-
-                ASSERT_NO_FATAL_FAILURE(destroyLayers(display,
-                        std::move(layers)));
-            }
-            ASSERT_NO_FATAL_FAILURE(disableVsync(display));
-            ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-            ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display));
-        } while (testVirtualDisplay.advance());
-    }
-
-    hwc2_device_t* mHwc2Device = nullptr;
-
-    enum class Hwc2TestHotplugStatus {
-        Init = 1,
-        Receiving,
-        Done,
-    };
-
-    std::mutex mHotplugMutex;
-    std::condition_variable mHotplugCv;
-    Hwc2TestHotplugStatus mHotplugStatus = Hwc2TestHotplugStatus::Init;
-    std::unordered_set<hwc2_display_t> mDisplays;
-
-    /* Store all created layers that have not been destroyed. If an ASSERT_*
-     * fails, then destroy the layers on exit */
-    std::set<std::pair<hwc2_display_t, hwc2_layer_t>> mLayers;
-
-    /* Store the power mode state. If it is not HWC2_POWER_MODE_OFF when
-     * tearing down the test cases, change it to HWC2_POWER_MODE_OFF */
-    std::set<hwc2_display_t> mActiveDisplays;
-
-    /* Store all created virtual displays that have not been destroyed. If an
-     * ASSERT_* fails, then destroy the virtual displays on exit */
-    std::set<hwc2_display_t> mVirtualDisplays;
-
-    std::mutex mVsyncMutex;
-    std::condition_variable mVsyncCv;
-    hwc2_display_t mVsyncDisplay;
-    int64_t mVsyncTimestamp = -1;
-};
-
-void hwc2TestHotplugCallback(hwc2_callback_data_t callbackData,
-        hwc2_display_t display, int32_t connection)
-{
-    if (callbackData)
-        static_cast<Hwc2Test*>(callbackData)->hotplugCallback(display,
-                connection);
-}
-
-void hwc2TestVsyncCallback(hwc2_callback_data_t callbackData,
-        hwc2_display_t display, int64_t timestamp)
-{
-    if (callbackData)
-        static_cast<Hwc2Test*>(callbackData)->vsyncCallback(display,
-                timestamp);
-}
-
-void setBlendMode(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
-{
-    EXPECT_NO_FATAL_FAILURE(test->setLayerBlendMode(display, layer,
-            testLayer->getBlendMode(), outErr));
-}
-
-void setBuffer(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
-{
-    buffer_handle_t handle;
-    android::base::unique_fd acquireFence;
-    hwc2_composition_t composition = testLayer->getComposition();
-
-    if (composition == HWC2_COMPOSITION_CLIENT
-            || composition == HWC2_COMPOSITION_SOLID_COLOR
-            || composition == HWC2_COMPOSITION_SIDEBAND)
-        return;
-
-    if (testLayer->getBuffer(&handle, &acquireFence) < 0)
-        return;
-
-    ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display, layer,
-            composition));
-    EXPECT_NO_FATAL_FAILURE(test->setLayerBuffer(display, layer,
-            handle, acquireFence, outErr));
-}
-
-void setColor(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
-{
-    ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display,
-            layer, HWC2_COMPOSITION_SOLID_COLOR));
-    ASSERT_NO_FATAL_FAILURE(test->setLayerPlaneAlpha(display,
-            layer, testLayer->getPlaneAlpha()));
-    ASSERT_NO_FATAL_FAILURE(test->setLayerBlendMode(display,
-            layer, testLayer->getBlendMode()));
-    EXPECT_NO_FATAL_FAILURE(test->setLayerColor(display, layer,
-            testLayer->getColor(), outErr));
-}
-
-void setComposition(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
-{
-    hwc2_composition_t composition = testLayer->getComposition();
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display, layer,
-            composition, &err));
-    if (outErr) {
-        *outErr = err;
-        return;
-    }
-
-    if (composition != HWC2_COMPOSITION_SIDEBAND) {
-        EXPECT_EQ(err, HWC2_ERROR_NONE) << "returned wrong error code";
-    } else {
-        EXPECT_TRUE(err == HWC2_ERROR_NONE || err == HWC2_ERROR_UNSUPPORTED)
-                 << "returned wrong error code";
-    }
-}
-
-void setCursorPosition(Hwc2Test* test, hwc2_display_t display,
-        hwc2_layer_t layer, Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
-{
-    ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display,
-            layer, HWC2_COMPOSITION_CURSOR));
-
-    const hwc_rect_t cursorPosition = testLayer->getCursorPosition();
-    EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display, layer,
-            cursorPosition.left, cursorPosition.top, outErr));
-}
-
-void setDataspace(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
-{
-    EXPECT_NO_FATAL_FAILURE(test->setLayerDataspace(display, layer,
-            testLayer->getDataspace(), outErr));
-}
-
-void setDisplayFrame(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
-{
-    EXPECT_NO_FATAL_FAILURE(test->setLayerDisplayFrame(display, layer,
-            testLayer->getDisplayFrame(), outErr));
-}
-
-void setPlaneAlpha(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-        Hwc2TestLayer* testLayer, hwc2_error_t *outErr)
-{
-    ASSERT_NO_FATAL_FAILURE(test->setLayerBlendMode(display, layer,
-            testLayer->getBlendMode()));
-    EXPECT_NO_FATAL_FAILURE(test->setLayerPlaneAlpha(display, layer,
-            testLayer->getPlaneAlpha(), outErr));
-}
-
-void setSourceCrop(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
-{
-    EXPECT_NO_FATAL_FAILURE(test->setLayerSourceCrop(display, layer,
-            testLayer->getSourceCrop(), outErr));
-}
-
-void setSurfaceDamage(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
-{
-    EXPECT_NO_FATAL_FAILURE(test->setLayerSurfaceDamage(display, layer,
-            testLayer->getSurfaceDamage(), outErr));
-}
-
-void setTransform(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
-{
-    EXPECT_NO_FATAL_FAILURE(test->setLayerTransform(display, layer,
-            testLayer->getTransform(), outErr));
-}
-
-void setVisibleRegion(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
-{
-    EXPECT_NO_FATAL_FAILURE(test->setLayerVisibleRegion(display, layer,
-            testLayer->getVisibleRegion(), outErr));
-}
-
-void setZOrder(Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-        Hwc2TestLayer* testLayer, hwc2_error_t* outErr)
-{
-    EXPECT_NO_FATAL_FAILURE(test->setLayerZOrder(display, layer,
-            testLayer->getZOrder(), outErr));
-}
-
-bool advanceBlendMode(Hwc2TestLayer* testLayer)
-{
-    return testLayer->advanceBlendMode();
-}
-
-bool advanceBuffer(Hwc2TestLayer* testLayer)
-{
-    if (testLayer->advanceComposition())
-        return true;
-    return testLayer->advanceBufferArea();
-}
-
-bool advanceColor(Hwc2TestLayer* testLayer)
-{
-    /* Color depends on blend mode so advance blend mode last so color is not
-     * force to update as often */
-    if (testLayer->advancePlaneAlpha())
-        return true;
-    if (testLayer->advanceColor())
-        return true;
-    return testLayer->advanceBlendMode();
-}
-
-bool advanceComposition(Hwc2TestLayer* testLayer)
-{
-    return testLayer->advanceComposition();
-}
-
-bool advanceCursorPosition(Hwc2TestLayer* testLayer)
-{
-    return testLayer->advanceCursorPosition();
-}
-
-bool advanceDataspace(Hwc2TestLayer* testLayer)
-{
-    return testLayer->advanceDataspace();
-}
-
-bool advanceDisplayFrame(Hwc2TestLayer* testLayer)
-{
-    return testLayer->advanceDisplayFrame();
-}
-
-bool advancePlaneAlpha(Hwc2TestLayer* testLayer)
-{
-    return testLayer->advancePlaneAlpha();
-}
-
-bool advanceSourceCrop(Hwc2TestLayer* testLayer)
-{
-    if (testLayer->advanceSourceCrop())
-        return true;
-    return testLayer->advanceBufferArea();
-}
-
-bool advanceSurfaceDamage(Hwc2TestLayer* testLayer)
-{
-    if (testLayer->advanceSurfaceDamage())
-        return true;
-    return testLayer->advanceBufferArea();
-}
-
-bool advanceTransform(Hwc2TestLayer* testLayer)
-{
-    return testLayer->advanceTransform();
-}
-
-bool advanceVisibleRegions(Hwc2TestLayers* testLayers)
-{
-    return testLayers->advanceVisibleRegions();
-}
-
-bool advanceClientTargetSupport(
-        Hwc2TestClientTargetSupport* testClientTargetSupport)
-{
-    return testClientTargetSupport->advance();
-}
-
-static const std::array<hwc2_function_descriptor_t, 42> requiredFunctions = {{
-    HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES,
-    HWC2_FUNCTION_CREATE_LAYER,
-    HWC2_FUNCTION_CREATE_VIRTUAL_DISPLAY,
-    HWC2_FUNCTION_DESTROY_LAYER,
-    HWC2_FUNCTION_DESTROY_VIRTUAL_DISPLAY,
-    HWC2_FUNCTION_DUMP,
-    HWC2_FUNCTION_GET_ACTIVE_CONFIG,
-    HWC2_FUNCTION_GET_CHANGED_COMPOSITION_TYPES,
-    HWC2_FUNCTION_GET_CLIENT_TARGET_SUPPORT,
-    HWC2_FUNCTION_GET_COLOR_MODES,
-    HWC2_FUNCTION_GET_DISPLAY_ATTRIBUTE,
-    HWC2_FUNCTION_GET_DISPLAY_CONFIGS,
-    HWC2_FUNCTION_GET_DISPLAY_NAME,
-    HWC2_FUNCTION_GET_DISPLAY_REQUESTS,
-    HWC2_FUNCTION_GET_DISPLAY_TYPE,
-    HWC2_FUNCTION_GET_DOZE_SUPPORT,
-    HWC2_FUNCTION_GET_HDR_CAPABILITIES,
-    HWC2_FUNCTION_GET_MAX_VIRTUAL_DISPLAY_COUNT,
-    HWC2_FUNCTION_GET_RELEASE_FENCES,
-    HWC2_FUNCTION_PRESENT_DISPLAY,
-    HWC2_FUNCTION_REGISTER_CALLBACK,
-    HWC2_FUNCTION_SET_ACTIVE_CONFIG,
-    HWC2_FUNCTION_SET_CLIENT_TARGET,
-    HWC2_FUNCTION_SET_COLOR_MODE,
-    HWC2_FUNCTION_SET_COLOR_TRANSFORM,
-    HWC2_FUNCTION_SET_CURSOR_POSITION,
-    HWC2_FUNCTION_SET_LAYER_BLEND_MODE,
-    HWC2_FUNCTION_SET_LAYER_BUFFER,
-    HWC2_FUNCTION_SET_LAYER_COLOR,
-    HWC2_FUNCTION_SET_LAYER_COMPOSITION_TYPE,
-    HWC2_FUNCTION_SET_LAYER_DATASPACE,
-    HWC2_FUNCTION_SET_LAYER_DISPLAY_FRAME,
-    HWC2_FUNCTION_SET_LAYER_PLANE_ALPHA,
-    HWC2_FUNCTION_SET_LAYER_SOURCE_CROP,
-    HWC2_FUNCTION_SET_LAYER_SURFACE_DAMAGE,
-    HWC2_FUNCTION_SET_LAYER_TRANSFORM,
-    HWC2_FUNCTION_SET_LAYER_VISIBLE_REGION,
-    HWC2_FUNCTION_SET_LAYER_Z_ORDER,
-    HWC2_FUNCTION_SET_OUTPUT_BUFFER,
-    HWC2_FUNCTION_SET_POWER_MODE,
-    HWC2_FUNCTION_SET_VSYNC_ENABLED,
-    HWC2_FUNCTION_VALIDATE_DISPLAY,
-}};
-
-/* TESTCASE: Tests that the HWC2 supports all required functions. */
-TEST_F(Hwc2Test, GET_FUNCTION)
-{
-    for (hwc2_function_descriptor_t descriptor : requiredFunctions) {
-        hwc2_function_pointer_t pfn = getFunction(descriptor);
-        EXPECT_TRUE(pfn) << "failed to get function "
-                << getFunctionDescriptorName(descriptor);
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 fails to retrieve and invalid function. */
-TEST_F(Hwc2Test, GET_FUNCTION_invalid_function)
-{
-    hwc2_function_pointer_t pfn = getFunction(HWC2_FUNCTION_INVALID);
-    EXPECT_FALSE(pfn) << "failed to get invalid function";
-}
-
-/* TESTCASE: Tests that the HWC2 does not return an invalid capability. */
-TEST_F(Hwc2Test, GET_CAPABILITIES)
-{
-    std::vector<hwc2_capability_t> capabilities;
-
-    getCapabilities(&capabilities);
-
-    EXPECT_EQ(std::count(capabilities.begin(), capabilities.end(),
-            HWC2_CAPABILITY_INVALID), 0);
-}
-
-static const std::array<hwc2_callback_descriptor_t, 3> callbackDescriptors = {{
-    HWC2_CALLBACK_HOTPLUG,
-    HWC2_CALLBACK_REFRESH,
-    HWC2_CALLBACK_VSYNC,
-}};
-
-/* TESTCASE: Tests that the HWC2 can successfully register all required
- * callback functions. */
-TEST_F(Hwc2Test, REGISTER_CALLBACK)
-{
-    hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
-            const_cast<char*>("data"));
-
-    for (auto descriptor : callbackDescriptors) {
-        ASSERT_NO_FATAL_FAILURE(registerCallback(descriptor, data,
-                []() { return; }));
-    }
-}
-
-/* TESTCASE: Test that the HWC2 fails to register invalid callbacks. */
-TEST_F(Hwc2Test, REGISTER_CALLBACK_bad_parameter)
-{
-    hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
-            const_cast<char*>("data"));
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_INVALID, data,
-            []() { return; }, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 can register a callback with null data. */
-TEST_F(Hwc2Test, REGISTER_CALLBACK_null_data)
-{
-    hwc2_callback_data_t data = nullptr;
-
-    for (auto descriptor : callbackDescriptors) {
-        ASSERT_NO_FATAL_FAILURE(registerCallback(descriptor, data,
-                []() { return; }));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 returns the correct display type for each
- * physical display. */
-TEST_F(Hwc2Test, GET_DISPLAY_TYPE)
-{
-    for (auto display : mDisplays) {
-        hwc2_display_type_t type;
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayType(display, &type));
-        EXPECT_EQ(type, HWC2_DISPLAY_TYPE_PHYSICAL) << "failed to return"
-                " correct display type";
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 returns an error when the display type of a bad
- * display is requested. */
-TEST_F(Hwc2Test, GET_DISPLAY_TYPE_bad_display)
-{
-    hwc2_display_t display;
-    hwc2_display_type_t type;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(getDisplayType(display, &type, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 can create and destroy layers. */
-TEST_F(Hwc2Test, CREATE_DESTROY_LAYER)
-{
-    for (auto display : mDisplays) {
-        hwc2_layer_t layer;
-
-        ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
-
-        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 cannot create a layer for a bad display */
-TEST_F(Hwc2Test, CREATE_LAYER_bad_display)
-{
-    hwc2_display_t display;
-    hwc2_layer_t layer;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 will either support a large number of resources
- * or will return no resources. */
-TEST_F(Hwc2Test, CREATE_LAYER_no_resources)
-{
-    const size_t layerCnt = 1000;
-
-    for (auto display : mDisplays) {
-        std::vector<hwc2_layer_t> layers;
-
-        ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
-
-        ASSERT_NO_FATAL_FAILURE(destroyLayers(display, std::move(layers)));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 cannot destroy a layer for a bad display */
-TEST_F(Hwc2Test, DESTROY_LAYER_bad_display)
-{
-    hwc2_display_t badDisplay;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&badDisplay));
-
-    for (auto display : mDisplays) {
-        hwc2_layer_t layer = 0;
-        hwc2_error_t err = HWC2_ERROR_NONE;
-
-        ASSERT_NO_FATAL_FAILURE(destroyLayer(badDisplay, layer, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-
-        ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
-
-        ASSERT_NO_FATAL_FAILURE(destroyLayer(badDisplay, layer, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-
-        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 cannot destory a bad layer */
-TEST_F(Hwc2Test, DESTROY_LAYER_bad_layer)
-{
-    for (auto display : mDisplays) {
-        hwc2_layer_t layer;
-        hwc2_error_t err = HWC2_ERROR_NONE;
-
-        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, UINT64_MAX / 2, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
-
-        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, 0, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
-
-        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, UINT64_MAX - 1, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
-
-        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, 1, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
-
-        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, UINT64_MAX, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
-
-        ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
-
-        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer + 1, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
-
-        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
-
-        ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_LAYER) << "returned wrong error code";
-    }
-}
-
-static const std::array<hwc2_attribute_t, 2> requiredAttributes = {{
-    HWC2_ATTRIBUTE_WIDTH,
-    HWC2_ATTRIBUTE_HEIGHT,
-}};
-
-static const std::array<hwc2_attribute_t, 3> optionalAttributes = {{
-    HWC2_ATTRIBUTE_VSYNC_PERIOD,
-    HWC2_ATTRIBUTE_DPI_X,
-    HWC2_ATTRIBUTE_DPI_Y,
-}};
-
-/* TESTCASE: Tests that the HWC2 can return display attributes for a valid
- * config. */
-TEST_F(Hwc2Test, GET_DISPLAY_ATTRIBUTE)
-{
-    for (auto display : mDisplays) {
-        std::vector<hwc2_config_t> configs;
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-        for (auto config : configs) {
-            int32_t value;
-
-            for (auto attribute : requiredAttributes) {
-                ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
-                        attribute, &value));
-                EXPECT_GE(value, 0) << "missing required attribute "
-                        << getAttributeName(attribute) << " for config "
-                        << config;
-            }
-            for (auto attribute : optionalAttributes) {
-                ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
-                        attribute, &value));
-            }
-        }
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 will return a value of -1 for an invalid
- * attribute */
-TEST_F(Hwc2Test, GET_DISPLAY_ATTRIBUTE_invalid_attribute)
-{
-    const hwc2_attribute_t attribute = HWC2_ATTRIBUTE_INVALID;
-
-    for (auto display : mDisplays) {
-        std::vector<hwc2_config_t> configs;
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-        for (auto config : configs) {
-            int32_t value;
-            hwc2_error_t err = HWC2_ERROR_NONE;
-
-            ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
-                    attribute, &value, &err));
-            EXPECT_EQ(value, -1) << "failed to return -1 for an invalid"
-                    " attribute for config " << config;
-        }
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 will fail to get attributes for a bad display */
-TEST_F(Hwc2Test, GET_DISPLAY_ATTRIBUTE_bad_display)
-{
-    hwc2_display_t display;
-    const hwc2_config_t config = 0;
-    int32_t value;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    for (auto attribute : requiredAttributes) {
-        ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config, attribute,
-                &value, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-    }
-
-    for (auto attribute : optionalAttributes) {
-        ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config, attribute,
-                &value, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 will fail to get attributes for a bad config */
-TEST_F(Hwc2Test, GET_DISPLAY_ATTRIBUTE_bad_config)
-{
-    for (auto display : mDisplays) {
-        hwc2_config_t config;
-        int32_t value;
-        hwc2_error_t err = HWC2_ERROR_NONE;
-
-        ASSERT_NO_FATAL_FAILURE(getInvalidConfig(display, &config));
-
-        for (auto attribute : requiredAttributes) {
-            ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
-                    attribute, &value, &err));
-            EXPECT_EQ(err, HWC2_ERROR_BAD_CONFIG) << "returned wrong error code";
-        }
-
-        for (auto attribute : optionalAttributes) {
-            ASSERT_NO_FATAL_FAILURE(getDisplayAttribute(display, config,
-                    attribute, &value, &err));
-            EXPECT_EQ(err, HWC2_ERROR_BAD_CONFIG) << "returned wrong error code";
-        }
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 will get display configs for active displays */
-TEST_F(Hwc2Test, GET_DISPLAY_CONFIGS)
-{
-    for (auto display : mDisplays) {
-        std::vector<hwc2_config_t> configs;
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 will not get display configs for bad displays */
-TEST_F(Hwc2Test, GET_DISPLAY_CONFIGS_bad_display)
-{
-    hwc2_display_t display;
-    std::vector<hwc2_config_t> configs;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs, &err));
-
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-    EXPECT_TRUE(configs.empty()) << "returned configs for bad display";
-}
-
-/* TESTCASE: Tests that the HWC2 will return the same config list multiple
- * times in a row. */
-TEST_F(Hwc2Test, GET_DISPLAY_CONFIGS_same)
-{
-    for (auto display : mDisplays) {
-        std::vector<hwc2_config_t> configs1, configs2;
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs1));
-        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs2));
-
-        EXPECT_TRUE(std::is_permutation(configs1.begin(), configs1.end(),
-                configs2.begin())) << "returned two different config sets";
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 does not return duplicate display configs */
-TEST_F(Hwc2Test, GET_DISPLAY_CONFIGS_duplicate)
-{
-    for (auto display : mDisplays) {
-        std::vector<hwc2_config_t> configs;
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-        std::unordered_set<hwc2_config_t> configsSet(configs.begin(),
-                configs.end());
-        EXPECT_EQ(configs.size(), configsSet.size()) << "returned duplicate"
-                " configs";
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 returns the active config for a display */
-TEST_F(Hwc2Test, GET_ACTIVE_CONFIG)
-{
-    for (auto display : mDisplays) {
-        std::vector<hwc2_config_t> configs;
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-        for (auto config : configs) {
-            hwc2_config_t activeConfig;
-
-            ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-            ASSERT_NO_FATAL_FAILURE(getActiveConfig(display, &activeConfig));
-
-            EXPECT_EQ(activeConfig, config) << "failed to get active config";
-        }
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 does not return an active config for a bad
- * display. */
-TEST_F(Hwc2Test, GET_ACTIVE_CONFIG_bad_display)
-{
-    hwc2_display_t display;
-    hwc2_config_t activeConfig;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(getActiveConfig(display, &activeConfig, &err));
-
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 either begins with a valid active config
- * or returns an error when getActiveConfig is called. */
-TEST_F(Hwc2Test, GET_ACTIVE_CONFIG_bad_config)
-{
-    for (auto display : mDisplays) {
-        std::vector<hwc2_config_t> configs;
-        hwc2_config_t activeConfig;
-        hwc2_error_t err = HWC2_ERROR_NONE;
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-        if (configs.empty())
-            return;
-
-        ASSERT_NO_FATAL_FAILURE(getActiveConfig(display, &activeConfig, &err));
-        if (err == HWC2_ERROR_NONE) {
-            EXPECT_NE(std::count(configs.begin(), configs.end(),
-                    activeConfig), 0) << "active config is not found in "
-                    " configs for display";
-        } else {
-            EXPECT_EQ(err, HWC2_ERROR_BAD_CONFIG) << "returned wrong error code";
-        }
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 can set every display config as an active
- * config */
-TEST_F(Hwc2Test, SET_ACTIVE_CONFIG)
-{
-    for (auto display : mDisplays) {
-        std::vector<hwc2_config_t> configs;
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-        for (auto config : configs) {
-            EXPECT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-        }
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set an active config for a bad display */
-TEST_F(Hwc2Test, SET_ACTIVE_CONFIG_bad_display)
-{
-    hwc2_display_t display;
-    const hwc2_config_t config = 0;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set an invalid active config */
-TEST_F(Hwc2Test, SET_ACTIVE_CONFIG_bad_config)
-{
-    for (auto display : mDisplays) {
-        hwc2_config_t config;
-        hwc2_error_t err = HWC2_ERROR_NONE;
-
-        ASSERT_NO_FATAL_FAILURE(getInvalidConfig(display, &config));
-
-        ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_CONFIG) << "returned wrong error code";
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 returns a valid value for getDozeSupport. */
-TEST_F(Hwc2Test, GET_DOZE_SUPPORT)
-{
-    for (auto display : mDisplays) {
-        int32_t support = -1;
-
-        ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support));
-
-        EXPECT_TRUE(support == 0 || support == 1) << "invalid doze support value";
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 cannot get doze support for a bad display. */
-TEST_F(Hwc2Test, GET_DOZE_SUPPORT_bad_display)
-{
-    hwc2_display_t display;
-    int32_t support = -1;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support, &err));
-
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 can set all supported power modes */
-TEST_F(Hwc2Test, SET_POWER_MODE)
-{
-    for (auto display : mDisplays) {
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-
-        int32_t support = -1;
-        ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support));
-        if (support != 1)
-            return;
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE));
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display,
-                HWC2_POWER_MODE_DOZE_SUSPEND));
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set a power mode for a bad display. */
-TEST_F(Hwc2Test, SET_POWER_MODE_bad_display)
-{
-    hwc2_display_t display;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-
-    ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-
-    int32_t support = -1;
-    ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support, &err));
-    if (support != 1)
-        return;
-
-    ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-
-    ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE_SUSPEND,
-            &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set an invalid power mode value. */
-TEST_F(Hwc2Test, SET_POWER_MODE_bad_parameter)
-{
-    for (auto display : mDisplays) {
-        hwc2_power_mode_t mode = static_cast<hwc2_power_mode_t>(
-                HWC2_POWER_MODE_DOZE_SUSPEND + 1);
-        hwc2_error_t err = HWC2_ERROR_NONE;
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, mode, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code "
-                << mode;
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 will return unsupported if it does not support
- * an optional power mode. */
-TEST_F(Hwc2Test, SET_POWER_MODE_unsupported)
-{
-    for (auto display : mDisplays) {
-        int32_t support = -1;
-        hwc2_error_t err = HWC2_ERROR_NONE;
-
-        ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support, &err));
-        if (support == 1)
-            return;
-
-        ASSERT_EQ(support, 0) << "invalid doze support value";
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE,
-                &err));
-        EXPECT_EQ(err, HWC2_ERROR_UNSUPPORTED) << "returned wrong error code";
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display,
-                HWC2_POWER_MODE_DOZE_SUSPEND, &err));
-        EXPECT_EQ(err, HWC2_ERROR_UNSUPPORTED) <<  "returned wrong error code";
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 can set the same power mode multiple times. */
-TEST_F(Hwc2Test, SET_POWER_MODE_stress)
-{
-    for (auto display : mDisplays) {
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-
-        int32_t support = -1;
-        ASSERT_NO_FATAL_FAILURE(getDozeSupport(display, &support));
-        if (support != 1)
-            return;
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE));
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_DOZE));
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display,
-                HWC2_POWER_MODE_DOZE_SUSPEND));
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display,
-                HWC2_POWER_MODE_DOZE_SUSPEND));
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 can enable and disable vsync on active
- * displays */
-TEST_F(Hwc2Test, SET_VSYNC_ENABLED)
-{
-    for (auto display : mDisplays) {
-        hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
-                const_cast<char*>("data"));
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
-
-        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, data,
-                []() { return; }));
-
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
-
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 issues a valid vsync callback. */
-TEST_F(Hwc2Test, SET_VSYNC_ENABLED_callback)
-{
-    for (auto display : mDisplays) {
-        hwc2_display_t receivedDisplay;
-        int64_t receivedTimestamp;
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
-
-        ASSERT_NO_FATAL_FAILURE(enableVsync(display));
-
-        ASSERT_NO_FATAL_FAILURE(waitForVsync(&receivedDisplay,
-                &receivedTimestamp));
-
-        EXPECT_EQ(receivedDisplay, display) << "failed to get correct display";
-        EXPECT_GE(receivedTimestamp, 0) << "failed to get valid timestamp";
-
-        ASSERT_NO_FATAL_FAILURE(disableVsync(display));
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 cannot enable a vsync for a bad display */
-TEST_F(Hwc2Test, SET_VSYNC_ENABLED_bad_display)
-{
-    hwc2_display_t display;
-    hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
-            const_cast<char*>("data"));
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, data,
-            []() { return; }));
-
-    ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-
-    ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 cannot enable an invalid vsync value */
-TEST_F(Hwc2Test, SET_VSYNC_ENABLED_bad_parameter)
-{
-    for (auto display : mDisplays) {
-        hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
-                const_cast<char*>("data"));
-        hwc2_error_t err = HWC2_ERROR_NONE;
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
-
-        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, data,
-                []() { return; }));
-
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_INVALID,
-                &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 can enable and disable a vsync value multiple
- * times. */
-TEST_F(Hwc2Test, SET_VSYNC_ENABLED_stress)
-{
-    for (auto display : mDisplays) {
-        hwc2_callback_data_t data = reinterpret_cast<hwc2_callback_data_t>(
-                const_cast<char*>("data"));
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
-
-        ASSERT_NO_FATAL_FAILURE(registerCallback(HWC2_CALLBACK_VSYNC, data,
-                []() { return; }));
-
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
-
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
-
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 can set a vsync enable value when the display
- * is off and no callback is registered. */
-TEST_F(Hwc2Test, SET_VSYNC_ENABLED_no_callback_no_power)
-{
-    const uint secs = 1;
-
-    for (auto display : mDisplays) {
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
-
-        sleep(secs);
-
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 can set a vsync enable value when no callback
- * is registered. */
-TEST_F(Hwc2Test, SET_VSYNC_ENABLED_no_callback)
-{
-    const uint secs = 1;
-
-    for (auto display : mDisplays) {
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
-
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_ENABLE));
-
-        sleep(secs);
-
-        ASSERT_NO_FATAL_FAILURE(setVsyncEnabled(display, HWC2_VSYNC_DISABLE));
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 returns a display name for each display */
-TEST_F(Hwc2Test, GET_DISPLAY_NAME)
-{
-    for (auto display : mDisplays) {
-        std::string name;
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayName(display, &name));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 does not return a display name for a bad
- * display */
-TEST_F(Hwc2Test, GET_DISPLAY_NAME_bad_display)
-{
-    hwc2_display_t display;
-    std::string name;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(getDisplayName(display, &name, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 can set basic composition types. */
-TEST_F(Hwc2Test, SET_LAYER_COMPOSITION_TYPE)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
-            setComposition, advanceComposition));
-}
-
-/* TESTCASE: Tests that the HWC2 can update a basic composition type on a
- * layer. */
-TEST_F(Hwc2Test, SET_LAYER_COMPOSITION_TYPE_update)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
-            setComposition, advanceComposition));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set a composition type for a bad layer */
-TEST_F(Hwc2Test, SET_LAYER_COMPOSITION_TYPE_bad_layer)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
-            setComposition));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set a bad composition type */
-TEST_F(Hwc2Test, SET_LAYER_COMPOSITION_TYPE_bad_parameter)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadParameter(
-            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-                    hwc2_error_t* outErr) {
-
-                ASSERT_NO_FATAL_FAILURE(test->setLayerCompositionType(display,
-                        layer, HWC2_COMPOSITION_INVALID, outErr));
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 can set the cursor position of a layer. */
-TEST_F(Hwc2Test, SET_CURSOR_POSITION)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
-            ::setCursorPosition, advanceCursorPosition));
-}
-
-/* TESTCASE: Tests that the HWC2 can update the cursor position of a layer. */
-TEST_F(Hwc2Test, SET_CURSOR_POSITION_update)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
-            ::setCursorPosition, advanceCursorPosition));
-}
-
-/* TESTCASE: Tests that the HWC2 can set the cursor position of a layer when the
- * composition type has not been set to HWC2_COMPOSITION_CURSOR. */
-TEST_F(Hwc2Test, SET_CURSOR_POSITION_composition_type_unset)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
-            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
-                const hwc_rect_t cursorPosition = testLayer->getCursorPosition();
-                EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display, layer,
-                        cursorPosition.left, cursorPosition.top, outErr));
-            },
-
-            advanceCursorPosition));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set the cursor position of a bad
- * display. */
-TEST_F(Hwc2Test, SET_CURSOR_POSITION_bad_display)
-{
-    hwc2_display_t display;
-    hwc2_layer_t layer = 0;
-    int32_t x = 0, y = 0;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(setCursorPosition(display, layer, x, y, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set the cursor position of a bad layer. */
-TEST_F(Hwc2Test, SET_CURSOR_POSITION_bad_layer)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
-            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t badLayer,
-                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
-
-                const hwc_rect_t cursorPosition = testLayer->getCursorPosition();
-                EXPECT_NO_FATAL_FAILURE(test->setCursorPosition(display,
-                        badLayer, cursorPosition.left, cursorPosition.top,
-                        outErr));
-            }
-   ));
-}
-
-/* TESTCASE: Tests that the HWC2 can set a blend mode value of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_BLEND_MODE)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
-            setBlendMode, advanceBlendMode));
-}
-
-/* TESTCASE: Tests that the HWC2 can update a blend mode value of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_BLEND_MODE_update)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
-            setBlendMode, advanceBlendMode));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set a blend mode for a bad layer. */
-TEST_F(Hwc2Test, SET_LAYER_BLEND_MODE_bad_layer)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
-            setBlendMode));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set an invalid blend mode. */
-TEST_F(Hwc2Test, SET_LAYER_BLEND_MODE_bad_parameter)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadParameter(
-            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-                    hwc2_error_t* outErr) {
-
-                ASSERT_NO_FATAL_FAILURE(test->setLayerBlendMode(display,
-                        layer, HWC2_BLEND_MODE_INVALID, outErr));
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 can set the buffer of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_BUFFER)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
-            setBuffer, advanceBuffer));
-}
-
-/* TESTCASE: Tests that the HWC2 can update the buffer of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_BUFFER_update)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
-            setBuffer, advanceBuffer));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set the buffer of a bad layer. */
-TEST_F(Hwc2Test, SET_LAYER_BUFFER_bad_layer)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
-            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t badLayer,
-                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
-
-                buffer_handle_t handle = nullptr;
-                android::base::unique_fd acquireFence;
-
-                /* If there is not available buffer for the given buffer
-                 * properties, it should not fail this test case */
-                if (testLayer->getBuffer(&handle, &acquireFence) == 0) {
-                    *outErr = HWC2_ERROR_BAD_LAYER;
-                    return;
-                }
-
-                ASSERT_NO_FATAL_FAILURE(test->setLayerBuffer(display, badLayer,
-                        handle, acquireFence, outErr));
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 can set an invalid buffer for a layer. */
-TEST_F(Hwc2Test, SET_LAYER_BUFFER_bad_parameter)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadParameter(
-            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-                    hwc2_error_t* outErr) {
-
-                buffer_handle_t handle = nullptr;
-                int32_t acquireFence = -1;
-
-                ASSERT_NO_FATAL_FAILURE(test->setLayerBuffer(display, layer,
-                        handle, acquireFence, outErr));
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 can set the color of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_COLOR)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
-            setColor, advanceColor));
-}
-
-/* TESTCASE: Tests that the HWC2 can update the color of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_COLOR_update)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
-            setColor, advanceColor));
-}
-
-/* TESTCASE: Tests that the HWC2 can set the color of a layer when the
- * composition type has not been set to HWC2_COMPOSITION_SOLID_COLOR. */
-TEST_F(Hwc2Test, SET_LAYER_COLOR_composition_type_unset)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Basic,
-            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
-
-                EXPECT_NO_FATAL_FAILURE(test->setLayerColor(display, layer,
-                        testLayer->getColor(), outErr));
-            },
-
-            advanceColor));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set the color of a bad layer. */
-TEST_F(Hwc2Test, SET_LAYER_COLOR_bad_layer)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
-            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t badLayer,
-                    Hwc2TestLayer* testLayer, hwc2_error_t* outErr) {
-
-                EXPECT_NO_FATAL_FAILURE(test->setLayerColor(display, badLayer,
-                        testLayer->getColor(), outErr));
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 can set the dataspace of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_DATASPACE)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
-            setDataspace, advanceDataspace));
-}
-
-/* TESTCASE: Tests that the HWC2 can update the dataspace of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_DATASPACE_update)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
-            setDataspace, advanceDataspace));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set a dataspace for a bad layer. */
-TEST_F(Hwc2Test, SET_LAYER_DATASPACE_bad_layer)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
-            setDataspace));
-}
-
-/* TESTCASE: Tests that the HWC2 can set the display frame of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_DISPLAY_FRAME)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
-            setDisplayFrame, advanceDisplayFrame));
-}
-
-/* TESTCASE: Tests that the HWC2 can update the display frame of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_DISPLAY_FRAME_update)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
-            setDisplayFrame, advanceDisplayFrame));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set the display frame of a bad layer. */
-TEST_F(Hwc2Test, SET_LAYER_DISPLAY_FRAME_bad_layer)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
-            setDisplayFrame));
-}
-
-/* TESTCASE: Tests that the HWC2 can set the plane alpha of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_PLANE_ALPHA)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
-            setPlaneAlpha, advancePlaneAlpha));
-}
-
-/* TESTCASE: Tests that the HWC2 can update the plane alpha of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_PLANE_ALPHA_update)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
-            setPlaneAlpha, advancePlaneAlpha));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set a plane alpha for a bad layer. */
-TEST_F(Hwc2Test, SET_LAYER_PLANE_ALPHA_bad_layer)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
-            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t badLayer,
-                    Hwc2TestLayer* testLayer, hwc2_error_t *outErr) {
-
-                    EXPECT_NO_FATAL_FAILURE(test->setLayerPlaneAlpha(display,
-                            badLayer, testLayer->getPlaneAlpha(), outErr));
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 can set the source crop of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_SOURCE_CROP)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
-            setSourceCrop, advanceSourceCrop));
-}
-
-/* TESTCASE: Tests that the HWC2 can update the source crop of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_SOURCE_CROP_update)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
-            setSourceCrop, advanceSourceCrop));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set the source crop of a bad layer. */
-TEST_F(Hwc2Test, SET_LAYER_SOURCE_CROP_bad_layer)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
-            setSourceCrop));
-}
-
-/* TESTCASE: Tests that the HWC2 can set the surface damage of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_SURFACE_DAMAGE)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
-            setSurfaceDamage, advanceSurfaceDamage));
-}
-
-/* TESTCASE: Tests that the HWC2 can update the surface damage of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_SURFACE_DAMAGE_update)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
-            setSurfaceDamage, advanceSurfaceDamage));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set the surface damage of a bad layer. */
-TEST_F(Hwc2Test, SET_LAYER_SURFACE_DAMAGE_bad_layer)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
-            setSurfaceDamage));
-}
-
-/* TESTCASE: Tests that the HWC2 can set the transform value of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_TRANSFORM)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperty(Hwc2TestCoverage::Complete,
-            setTransform, advanceTransform));
-}
-
-/* TESTCASE: Tests that the HWC2 can update the transform value of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_TRANSFORM_update)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyUpdate(Hwc2TestCoverage::Complete,
-            setTransform, advanceTransform));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set the transform for a bad layer. */
-TEST_F(Hwc2Test, SET_LAYER_TRANSFORM_bad_layer)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
-            setTransform));
-}
-
-/* TESTCASE: Tests that the HWC2 can set the visible region of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_VISIBLE_REGION)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperties(Hwc2TestCoverage::Basic, 5,
-            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-                    Hwc2TestLayers* testLayers) {
-
-                EXPECT_NO_FATAL_FAILURE(test->setLayerVisibleRegion(display,
-                        layer, testLayers->getVisibleRegion(layer)));
-            },
-
-            advanceVisibleRegions));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set the visible region of a bad layer. */
-TEST_F(Hwc2Test, SET_LAYER_VISIBLE_REGION_bad_layer)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
-            setVisibleRegion));
-}
-
-/* TESTCASE: Tests that the HWC2 can set the z order of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_Z_ORDER)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerProperties(Hwc2TestCoverage::Complete, 10,
-            [] (Hwc2Test* test, hwc2_display_t display, hwc2_layer_t layer,
-                    Hwc2TestLayers* testLayers) {
-
-                EXPECT_NO_FATAL_FAILURE(test->setLayerZOrder(display, layer,
-                        testLayers->getZOrder(layer)));
-            },
-
-            /* TestLayer z orders are set during the construction of TestLayers
-             * and cannot be updated. There is no need (or ability) to cycle
-             * through additional z order configurations. */
-            [] (Hwc2TestLayers* /*testLayers*/) {
-                return false;
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 can update the z order of a layer. */
-TEST_F(Hwc2Test, SET_LAYER_Z_ORDER_update)
-{
-    const std::vector<uint32_t> zOrders = { static_cast<uint32_t>(0),
-            static_cast<uint32_t>(1), static_cast<uint32_t>(UINT32_MAX / 4),
-            static_cast<uint32_t>(UINT32_MAX / 2),
-            static_cast<uint32_t>(UINT32_MAX) };
-
-    for (auto display : mDisplays) {
-        std::vector<hwc2_config_t> configs;
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-        for (auto config : configs) {
-            hwc2_layer_t layer;
-
-            ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-
-            ASSERT_NO_FATAL_FAILURE(createLayer(display, &layer));
-
-            for (uint32_t zOrder : zOrders) {
-                EXPECT_NO_FATAL_FAILURE(setLayerZOrder(display, layer, zOrder));
-            }
-
-            ASSERT_NO_FATAL_FAILURE(destroyLayer(display, layer));
-        }
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set the z order of a bad layer. */
-TEST_F(Hwc2Test, SET_LAYER_Z_ORDER_bad_layer)
-{
-    ASSERT_NO_FATAL_FAILURE(setLayerPropertyBadLayer(Hwc2TestCoverage::Default,
-            setZOrder));
-}
-
-/* TESTCASE: Tests that the HWC2 can display a layer with basic property
- * coverage */
-TEST_F(Hwc2Test, VALIDATE_DISPLAY_basic)
-{
-    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Basic, 1,
-            [] (Hwc2Test* test, hwc2_display_t display,
-                    const std::vector<hwc2_layer_t>& layers,
-                    Hwc2TestLayers* /*testLayers*/) {
-
-                uint32_t numTypes, numRequests;
-                bool hasChanges = false;
-
-                EXPECT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
-                        &numRequests, &hasChanges));
-                if (hasChanges)
-                    EXPECT_LE(numTypes, static_cast<uint32_t>(layers.size()))
-                            << "wrong number of requests";
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 can display 5 layers with default coverage. */
-TEST_F(Hwc2Test, VALIDATE_DISPLAY_default_5)
-{
-    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Default, 5,
-            [] (Hwc2Test* test, hwc2_display_t display,
-                    const std::vector<hwc2_layer_t>& layers,
-                    Hwc2TestLayers* /*testLayers*/) {
-
-                uint32_t numTypes, numRequests;
-                bool hasChanges = false;
-
-                EXPECT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
-                        &numRequests, &hasChanges));
-                if (hasChanges)
-                    EXPECT_LE(numTypes, static_cast<uint32_t>(layers.size()))
-                            << "wrong number of requests";
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot validate a bad display */
-TEST_F(Hwc2Test, VALIDATE_DISPLAY_bad_display)
-{
-    hwc2_display_t display;
-    uint32_t numTypes, numRequests;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes, &numRequests,
-            &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 can get display requests after validating a
- * basic layer. */
-TEST_F(Hwc2Test, GET_DISPLAY_REQUESTS_basic)
-{
-    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Basic, 1,
-            [] (Hwc2Test* test, hwc2_display_t display,
-                    const std::vector<hwc2_layer_t>& layers,
-                    Hwc2TestLayers* /*testLayers*/) {
-
-                uint32_t numTypes, numRequests;
-                bool hasChanges = false;
-
-                ASSERT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
-                        &numRequests, &hasChanges));
-                if (hasChanges)
-                    EXPECT_LE(numTypes, layers.size())
-                            << "wrong number of requests";
-
-                EXPECT_NO_FATAL_FAILURE(test->handleRequests(display, layers,
-                        numRequests));
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot get display requests from a bad display */
-TEST_F(Hwc2Test, GET_DISPLAY_REQUESTS_bad_display)
-{
-    hwc2_display_t display;
-    hwc2_display_request_t displayRequests;
-    std::vector<hwc2_layer_t> layers;
-    std::vector<hwc2_layer_request_t> layerRequests;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    EXPECT_NO_FATAL_FAILURE(getDisplayRequests(display, &displayRequests,
-            &layers, &layerRequests, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 cannot get display requests from an non
- * validated display. */
-TEST_F(Hwc2Test, GET_DISPLAY_REQUESTS_not_validated)
-{
-    ASSERT_NO_FATAL_FAILURE(displayNonValidatedLayers(5,
-            [] (Hwc2Test* test, hwc2_display_t display,
-                    std::vector<hwc2_layer_t>* layers) {
-
-                hwc2_display_request_t displayRequests;
-                std::vector<hwc2_layer_request_t> layerRequests;
-                hwc2_error_t err = HWC2_ERROR_NONE;
-
-                ASSERT_NO_FATAL_FAILURE(test->getDisplayRequests(display,
-                        &displayRequests, layers, &layerRequests, &err));
-                EXPECT_EQ(err, HWC2_ERROR_NOT_VALIDATED)
-                        << "returned wrong error code";
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 can get changed composition types after
- * validating a basic layer. */
-TEST_F(Hwc2Test, GET_CHANGED_COMPOSITION_TYPES_basic)
-{
-    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Basic, 1,
-            [] (Hwc2Test* test, hwc2_display_t display,
-                    const std::vector<hwc2_layer_t>& layers,
-                    Hwc2TestLayers* testLayers) {
-
-                uint32_t numTypes, numRequests;
-                bool hasChanges = false;
-
-                ASSERT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
-                        &numRequests, &hasChanges));
-                if (hasChanges)
-                    EXPECT_LE(numTypes, layers.size())
-                            << "wrong number of requests";
-
-                EXPECT_NO_FATAL_FAILURE(test->handleCompositionChanges(display,
-                        *testLayers, layers, numTypes));
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot get changed composition types from a bad
- * display */
-TEST_F(Hwc2Test, GET_CHANGED_COMPOSITION_TYPES_bad_display)
-{
-    hwc2_display_t display;
-    std::vector<hwc2_layer_t> layers;
-    std::vector<hwc2_composition_t> types;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    EXPECT_NO_FATAL_FAILURE(getChangedCompositionTypes(display, &layers,
-            &types, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 cannot get changed composition types from an non
- * validated display. */
-TEST_F(Hwc2Test, GET_CHANGED_COMPOSITION_TYPES_not_validated)
-{
-    ASSERT_NO_FATAL_FAILURE(displayNonValidatedLayers(5,
-            [] (Hwc2Test* test, hwc2_display_t display,
-                    std::vector<hwc2_layer_t>* layers) {
-
-                std::vector<hwc2_composition_t> types;
-                hwc2_error_t err = HWC2_ERROR_NONE;
-
-                ASSERT_NO_FATAL_FAILURE(test->getChangedCompositionTypes(
-                        display, layers, &types, &err));
-                EXPECT_EQ(err, HWC2_ERROR_NOT_VALIDATED)
-                        << "returned wrong error code";
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 can accept display changes after validating a
- * basic layer. */
-TEST_F(Hwc2Test, ACCEPT_DISPLAY_CHANGES_basic)
-{
-    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Basic, 1,
-            [] (Hwc2Test* test, hwc2_display_t display,
-                    const std::vector<hwc2_layer_t>& layers,
-                    Hwc2TestLayers* testLayers) {
-
-                uint32_t numTypes, numRequests;
-                bool hasChanges = false;
-
-                ASSERT_NO_FATAL_FAILURE(test->validateDisplay(display, &numTypes,
-                        &numRequests, &hasChanges));
-                if (hasChanges)
-                    EXPECT_LE(numTypes, layers.size())
-                            << "wrong number of requests";
-
-                ASSERT_NO_FATAL_FAILURE(test->handleCompositionChanges(display,
-                        *testLayers, layers, numTypes));
-
-                EXPECT_NO_FATAL_FAILURE(test->acceptDisplayChanges(display));
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot accept display changes from a bad
- * display */
-TEST_F(Hwc2Test, ACCEPT_DISPLAY_CHANGES_bad_display)
-{
-    hwc2_display_t display;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    EXPECT_NO_FATAL_FAILURE(acceptDisplayChanges(display, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 cannot accept display changes from an non
- * validated display. */
-TEST_F(Hwc2Test, ACCEPT_DISPLAY_CHANGES_not_validated)
-{
-    ASSERT_NO_FATAL_FAILURE(displayNonValidatedLayers(5,
-            [] (Hwc2Test* test, hwc2_display_t display,
-                    std::vector<hwc2_layer_t>* /*layers*/) {
-
-                hwc2_error_t err = HWC2_ERROR_NONE;
-
-                ASSERT_NO_FATAL_FAILURE(test->acceptDisplayChanges(display, &err));
-                EXPECT_EQ(err, HWC2_ERROR_NOT_VALIDATED)
-                        << "returned wrong error code";
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 supports client target with required values */
-TEST_F(Hwc2Test, GET_CLIENT_TARGET_SUPPORT)
-{
-    ASSERT_NO_FATAL_FAILURE(setClientTargetSupport(Hwc2TestCoverage::Default,
-            [] (Hwc2Test* test, hwc2_display_t display,
-                    const Hwc2TestClientTargetSupport& testClientTargetSupport) {
-
-                const Area bufferArea = testClientTargetSupport.getBufferArea();
-                const android_pixel_format_t format = HAL_PIXEL_FORMAT_RGBA_8888;
-
-                ASSERT_NO_FATAL_FAILURE(test->getClientTargetSupport(display,
-                        bufferArea.width, bufferArea.height, format,
-                        testClientTargetSupport.getDataspace()));
-            },
-
-            advanceClientTargetSupport));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot get client target support for a bad
- * display. */
-TEST_F(Hwc2Test, GET_CLIENT_TARGET_SUPPORT_bad_display)
-{
-    ASSERT_NO_FATAL_FAILURE(setClientTargetSupport(Hwc2TestCoverage::Default,
-            [] (Hwc2Test* test, hwc2_display_t /*display*/,
-                    const Hwc2TestClientTargetSupport& testClientTargetSupport) {
-
-                const Area bufferArea = testClientTargetSupport.getBufferArea();
-                const android_pixel_format_t format = HAL_PIXEL_FORMAT_RGBA_8888;
-                hwc2_display_t badDisplay;
-                hwc2_error_t err = HWC2_ERROR_NONE;
-
-                ASSERT_NO_FATAL_FAILURE(test->getBadDisplay(&badDisplay));
-
-                ASSERT_NO_FATAL_FAILURE(test->getClientTargetSupport(badDisplay,
-                        bufferArea.width, bufferArea.height, format,
-                        testClientTargetSupport.getDataspace(), &err));
-                EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-            },
-
-            advanceClientTargetSupport));
-}
-
-/* TESTCASE: Tests that the HWC2 either supports or returns error unsupported
- * for a variety of client target values. */
-TEST_F(Hwc2Test, GET_CLIENT_TARGET_SUPPORT_unsupported)
-{
-    ASSERT_NO_FATAL_FAILURE(setClientTargetSupport(Hwc2TestCoverage::Complete,
-            [] (Hwc2Test* test, hwc2_display_t display,
-                    const Hwc2TestClientTargetSupport& testClientTargetSupport) {
-
-                const Area bufferArea = testClientTargetSupport.getBufferArea();
-                const android_pixel_format_t format = HAL_PIXEL_FORMAT_RGBA_8888;
-                hwc2_error_t err = HWC2_ERROR_NONE;
-
-                ASSERT_NO_FATAL_FAILURE(test->getClientTargetSupport(display,
-                        bufferArea.width, bufferArea.height, format,
-                        testClientTargetSupport.getDataspace(), &err));
-                EXPECT_TRUE(err == HWC2_ERROR_NONE
-                        || err == HWC2_ERROR_UNSUPPORTED)
-                        << "returned wrong error code";
-            },
-
-            advanceClientTargetSupport));
-}
-
-/* TESTCASE: Tests that the HWC2 can set a client target buffer for a basic
- * layer. */
-TEST_F(Hwc2Test, SET_CLIENT_TARGET_basic)
-{
-    const Dataspace dataspace = Dataspace::UNKNOWN;
-    const hwc_region_t damage = { };
-    const size_t layerCnt = 1;
-
-    for (auto display : mDisplays) {
-        std::vector<hwc2_config_t> configs;
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
-
-        ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
-
-        for (auto config : configs) {
-            Area displayArea;
-            std::vector<hwc2_layer_t> layers;
-
-            ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
-            ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display, &displayArea));
-
-            ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, layerCnt));
-            Hwc2TestLayers testLayers(layers, Hwc2TestCoverage::Basic,
-                    displayArea);
-
-            if (!testLayers.optimizeLayouts())
-                continue;
-
-            Hwc2TestClientTarget testClientTarget;
-
-            do {
-                std::set<hwc2_layer_t> clientLayers;
-                std::set<hwc2_layer_t> clearLayers;
-                uint32_t numTypes, numRequests;
-                bool hasChanges, skip;
-                bool flipClientTarget;
-                buffer_handle_t handle;
-                int32_t acquireFence;
-
-                ASSERT_NO_FATAL_FAILURE(setLayerProperties(display, layers,
-                        &testLayers, &skip));
-                if (skip)
-                    continue;
-
-                ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes,
-                        &numRequests, &hasChanges));
-                if (hasChanges)
-                    EXPECT_LE(numTypes, layers.size())
-                            << "wrong number of requests";
-
-                ASSERT_NO_FATAL_FAILURE(handleCompositionChanges(display,
-                        testLayers, layers, numTypes, &clientLayers));
-                ASSERT_NO_FATAL_FAILURE(handleRequests(display, layers,
-                        numRequests, &clearLayers, &flipClientTarget));
-                ASSERT_EQ(testClientTarget.getBuffer(testLayers, clientLayers,
-                        clearLayers, flipClientTarget, displayArea, &handle,
-                        &acquireFence), 0);
-                EXPECT_NO_FATAL_FAILURE(setClientTarget(display, handle,
-                        acquireFence, dataspace, damage));
-
-                if (acquireFence >= 0)
-                    close(acquireFence);
-
-            } while (testLayers.advance());
-
-            ASSERT_NO_FATAL_FAILURE(destroyLayers(display, std::move(layers)));
-        }
-
-        ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set a client target for a bad display. */
-TEST_F(Hwc2Test, SET_CLIENT_TARGET_bad_display)
-{
-    hwc2_display_t display;
-    std::vector<hwc2_layer_t> layers;
-    const Area displayArea = {0, 0};
-    Hwc2TestLayers testLayers(layers, Hwc2TestCoverage::Default, displayArea);
-    std::set<hwc2_layer_t> clientLayers;
-    std::set<hwc2_layer_t> flipClientTargetLayers;
-    bool flipClientTarget = true;
-    const Dataspace dataspace = Dataspace::UNKNOWN;
-    const hwc_region_t damage = { };
-    buffer_handle_t handle;
-    int32_t acquireFence;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    Hwc2TestClientTarget testClientTarget;
-
-    ASSERT_EQ(testClientTarget.getBuffer(testLayers, clientLayers,
-            flipClientTargetLayers, flipClientTarget, displayArea, &handle,
-            &acquireFence), 0);
-
-    EXPECT_NO_FATAL_FAILURE(setClientTarget(display, handle, acquireFence,
-            dataspace, damage, &err));
-
-    if (acquireFence >= 0)
-        close(acquireFence);
-
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 can present 1 default layer. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_default_1)
-{
-    const size_t layerCnt = 1;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
-    bool optimize = false;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 2 default layers. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_default_2)
-{
-    const size_t layerCnt = 2;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
-    bool optimize = false;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 3 default layers. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_default_3)
-{
-    const size_t layerCnt = 3;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
-    bool optimize = false;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 4 default layers. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_default_4)
-{
-    const size_t layerCnt = 4;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
-    bool optimize = false;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 5 default layers. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_default_5)
-{
-    const size_t layerCnt = 5;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
-    bool optimize = false;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 6 default layers. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_default_6)
-{
-    const size_t layerCnt = 6;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
-    bool optimize = false;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
- * blend mode. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_blend_mode_1)
-{
-    const size_t layerCnt = 1;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Complete},
-            {Hwc2TestPropertyName::Transform, Hwc2TestCoverage::Basic},
-            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Basic}};
-    bool optimize = false;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
- * blend mode. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_blend_mode_2)
-{
-    const size_t layerCnt = 2;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Complete},
-            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Basic}};
-    bool optimize = false;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
- * buffer. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_buffer_1)
-{
-    const size_t layerCnt = 1;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::BufferArea, Hwc2TestCoverage::Complete}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
- * color. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_color_1)
-{
-    const size_t layerCnt = 1;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete},
-            {Hwc2TestPropertyName::Color, Hwc2TestCoverage::Complete}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
- * color. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_color_2)
-{
-    const size_t layerCnt = 2;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete},
-            {Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Basic},
-            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Basic},
-            {Hwc2TestPropertyName::Color, Hwc2TestCoverage::Basic}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
- * composition. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_composition_1)
-{
-    const size_t layerCnt = 1;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
- * cursor. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_cursor_1)
-{
-    const size_t layerCnt = 1;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete},
-            {Hwc2TestPropertyName::CursorPosition, Hwc2TestCoverage::Complete}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
- * cursor. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_cursor_2)
-{
-    const size_t layerCnt = 2;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::Composition, Hwc2TestCoverage::Complete},
-            {Hwc2TestPropertyName::CursorPosition, Hwc2TestCoverage::Complete},
-            {Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Basic}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
- * dataspace. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_dataspace_1)
-{
-    const size_t layerCnt = 1;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::Dataspace, Hwc2TestCoverage::Complete}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
- * display frame. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_display_frame_1)
-{
-    const size_t layerCnt = 1;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
- * display frame. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_display_frame_2)
-{
-    const size_t layerCnt = 2;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 3 layers with complete coverage of
- * display frame. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_display_frame_3)
-{
-    const size_t layerCnt = 3;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 4 layers with complete coverage of
- * display frame. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_display_frame_4)
-{
-    const size_t layerCnt = 4;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
- * plane alpha. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_plane_alpha_1)
-{
-    const size_t layerCnt = 1;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Basic},
-            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Complete}};
-    bool optimize = false;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
- * plane alpha. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_plane_alpha_2)
-{
-    const size_t layerCnt = 2;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::BlendMode, Hwc2TestCoverage::Basic},
-            {Hwc2TestPropertyName::PlaneAlpha, Hwc2TestCoverage::Complete}};
-    bool optimize = false;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
- * source crop. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_source_crop_1)
-{
-    const size_t layerCnt = 1;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete},
-            {Hwc2TestPropertyName::SourceCrop, Hwc2TestCoverage::Complete}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
- * source crop. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_source_crop_2)
-{
-    const size_t layerCnt = 2;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Complete},
-            {Hwc2TestPropertyName::SourceCrop, Hwc2TestCoverage::Complete}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
- * surface damage. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_surface_damage_1)
-{
-    const size_t layerCnt = 1;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::SurfaceDamage, Hwc2TestCoverage::Complete}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
- * transform. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_transform_1)
-{
-    const size_t layerCnt = 1;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::Transform, Hwc2TestCoverage::Complete}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 2 layers with complete coverage of
- * transform. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_transform_2)
-{
-    const size_t layerCnt = 2;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions =
-            {{Hwc2TestPropertyName::Transform, Hwc2TestCoverage::Complete},
-            {Hwc2TestPropertyName::DisplayFrame, Hwc2TestCoverage::Basic}};
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with complete coverage of
- * basic. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_basic_1)
-{
-    const size_t layerCnt = 1;
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Basic;
-    std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> exceptions;
-    bool optimize = true;
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplays(layerCnt, coverage, exceptions,
-            optimize));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot present a bad display.  */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_bad_display)
-{
-    hwc2_display_t display;
-    int32_t presentFence;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(presentDisplay(display, &presentFence, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 cannot present an unvalidated display. */
-TEST_F(Hwc2Test, PRESENT_DISPLAY_not_validated)
-{
-    ASSERT_NO_FATAL_FAILURE(displayLayers(Hwc2TestCoverage::Default, 1,
-            [] (Hwc2Test* test, hwc2_display_t display,
-                    const std::vector<hwc2_layer_t>& /*layers*/,
-                    Hwc2TestLayers* /*testLayers*/) {
-
-                int32_t presentFence;
-                hwc2_error_t err = HWC2_ERROR_NONE;
-
-                ASSERT_NO_FATAL_FAILURE(test->setPowerMode(display,
-                        HWC2_POWER_MODE_ON));
-                ASSERT_NO_FATAL_FAILURE(test->enableVsync(display));
-
-                ASSERT_NO_FATAL_FAILURE(test->waitForVsync());
-
-                ASSERT_NO_FATAL_FAILURE(test->presentDisplay(display,
-                        &presentFence, &err));
-                EXPECT_EQ(err, HWC2_ERROR_NOT_VALIDATED)
-                        << "returned wrong error code";
-
-                ASSERT_NO_FATAL_FAILURE(test->disableVsync(display));
-                ASSERT_NO_FATAL_FAILURE(test->setPowerMode(display,
-                        HWC2_POWER_MODE_OFF));
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot get release fences from a bad display. */
-TEST_F(Hwc2Test, GET_RELEASE_FENCES_bad_display)
-{
-    hwc2_display_t display;
-    std::vector<hwc2_layer_t> layers;
-    std::vector<int32_t> fences;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(getReleaseFences(display, &layers, &fences, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-static const std::array<ColorMode, 9> androidColorModes = {{
-    ColorMode::NATIVE,
-    ColorMode::STANDARD_BT601_625,
-    ColorMode::STANDARD_BT601_625_UNADJUSTED,
-    ColorMode::STANDARD_BT601_525,
-    ColorMode::STANDARD_BT601_525_UNADJUSTED,
-    ColorMode::STANDARD_BT709,
-    ColorMode::DCI_P3,
-    ColorMode::SRGB,
-    ColorMode::ADOBE_RGB,
-}};
-
-/* TESTCASE: Tests that the HWC2 can get the color modes for a display. The
- * display must support ColorMode::NATIVE */
-TEST_F(Hwc2Test, GET_COLOR_MODES)
-{
-    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
-            [] (Hwc2Test* test, hwc2_display_t display) {
-
-                std::vector<ColorMode> colorModes;
-
-                ASSERT_NO_FATAL_FAILURE(test->getColorModes(display,
-                        &colorModes));
-
-                EXPECT_NE(std::count(colorModes.begin(), colorModes.end(),
-                        ColorMode::NATIVE), 0) << "all displays"
-                        " must support ColorMode::NATIVE";
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot get color modes from a bad display. */
-TEST_F(Hwc2Test, GET_COLOR_MODES_bad_display)
-{
-    hwc2_display_t display;
-    std::vector<ColorMode> colorModes;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(getColorModes(display, &colorModes, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 can set the required color mode on a display. */
-TEST_F(Hwc2Test, SET_COLOR_MODES)
-{
-    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
-            [] (Hwc2Test* test, hwc2_display_t display) {
-
-                const ColorMode colorMode = ColorMode::NATIVE;
-
-                EXPECT_NO_FATAL_FAILURE(test->setColorMode(display, colorMode));
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set a color mode on a bad display. */
-TEST_F(Hwc2Test, SET_COLOR_MODES_bad_display)
-{
-    hwc2_display_t display;
-    const ColorMode colorMode = ColorMode::NATIVE;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(setColorMode(display, colorMode, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set an invalid color mode. */
-TEST_F(Hwc2Test, SET_COLOR_MODES_bad_parameter)
-{
-    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
-            [] (Hwc2Test* test, hwc2_display_t display) {
-
-                const ColorMode colorMode = static_cast<ColorMode>(-1);
-                hwc2_error_t err = HWC2_ERROR_NONE;
-
-                ASSERT_NO_FATAL_FAILURE(test->setColorMode(display, colorMode,
-                        &err));
-                EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER)
-                        << "returned wrong error code";
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 either supports or returns error unsupported
- * for all valid color modes. */
-TEST_F(Hwc2Test, SET_COLOR_MODES_unsupported)
-{
-    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
-            [] (Hwc2Test* test, hwc2_display_t display) {
-
-                for (auto colorMode : androidColorModes) {
-                    hwc2_error_t err = HWC2_ERROR_NONE;
-
-                    ASSERT_NO_FATAL_FAILURE(test->setColorMode(display,
-                            colorMode, &err));
-
-                    EXPECT_TRUE(err == HWC2_ERROR_NONE
-                            || err == HWC2_ERROR_UNSUPPORTED)
-                            << "returned wrong error code";
-                }
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 gets the HDR capabilities for a display and
- * test if they are valid. */
-TEST_F(Hwc2Test, GET_HDR_CAPABILITIES)
-{
-    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
-            [] (Hwc2Test* test, hwc2_display_t display) {
-
-                std::vector<android_hdr_t> hdrCapabilities;
-                float maxLuminance, maxAverageLuminance, minLuminance;
-
-                EXPECT_NO_FATAL_FAILURE(test->getHdrCapabilities(display,
-                        &hdrCapabilities, &maxLuminance, &maxAverageLuminance,
-                        &minLuminance));
-
-                if (hdrCapabilities.empty())
-                    return;
-
-                EXPECT_GE(maxLuminance, maxAverageLuminance);
-                EXPECT_GE(maxAverageLuminance, minLuminance);
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot get hdr capabilities from a bad display */
-TEST_F(Hwc2Test, GET_HDR_CAPABILITIES_bad_display)
-{
-    hwc2_display_t display;
-    std::vector<android_hdr_t> hdrCapabilities;
-    float maxLuminance, maxAverageLuminance, minLuminance;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(getHdrCapabilities(display, &hdrCapabilities,
-            &maxLuminance, &maxAverageLuminance, &minLuminance, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-static const std::array<float, 16> identityMatrix = {{
-    1.0,  0.0,  0.0,  0.0,
-    0.0,  1.0,  0.0,  0.0,
-    0.0,  0.0,  1.0,  0.0,
-    0.0,  0.0,  0.0,  1.0,
-}};
-
-/* Values for the color transform matrices were precomputed using the source code
- * in surfaceflinger/Effects/Daltonizer.cpp. */
-
-static const std::array<const std::array<float, 16>, 5> exampleMatrices = {{
-    identityMatrix,
-    /* Converts RGB color to the XYZ space */
-    {{ 0.4124, 0.2126, 0.0193, 0,
-       0.3576, 0.7152, 0.1192, 0,
-       0.1805, 0.0722, 0.9505, 0,
-       0     , 0     , 0     , 1 }},
-    /* Protanomaly */
-    {{ 0.068493,  0.931506,  0,  0,
-       0.068493,  0.931507,  0,  0,
-       0.013626, -0.013626,  1,  0,
-       0,         0,         0,  1 }},
-    /* Deuteranomaly */
-    {{ 0.288299, 0.711701,  0,  0,
-       0.052709, 0.947291,  0,  0,
-      -0.257912, 0.257912,  1,  0,
-       0,        0,         0,  1 }},
-    /* Tritanomaly */
-    {{ 1, -0.805712, 0.805712,  0,
-       0,  0.378838, 0.621162,  0,
-       0,  0.104823, 0.895177,  0,
-       0,  0,        0,         1 }},
-}};
-
-/* TESTCASE: Tests that the HWC2 can set the identity color transform */
-TEST_F(Hwc2Test, SET_COLOR_TRANSFORM)
-{
-    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
-            [] (Hwc2Test* test, hwc2_display_t display) {
-
-                EXPECT_NO_FATAL_FAILURE(test->setColorTransform(display,
-                        identityMatrix, HAL_COLOR_TRANSFORM_IDENTITY));
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set the color transform for a bad
- * display. */
-TEST_F(Hwc2Test, SET_COLOR_TRANSFORM_bad_display)
-{
-    hwc2_display_t display;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(setColorTransform(display, identityMatrix,
-            HAL_COLOR_TRANSFORM_IDENTITY, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set an invalid color transform. */
-TEST_F(Hwc2Test, SET_COLOR_TRANSFORM_bad_parameter)
-{
-    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
-            [] (Hwc2Test* test, hwc2_display_t display) {
-
-                const android_color_transform_t hint =
-                        static_cast<android_color_transform_t>(-1);
-                hwc2_error_t err = HWC2_ERROR_NONE;
-
-                ASSERT_NO_FATAL_FAILURE(test->setColorTransform(display,
-                        identityMatrix, hint, &err));
-                EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER)
-                        << "returned wrong error code";
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 can set an arbitrary color matrix. */
-TEST_F(Hwc2Test, SET_COLOR_TRANSFORM_arbitrary_matrix)
-{
-    ASSERT_NO_FATAL_FAILURE(setActiveDisplayConfig(
-            [] (Hwc2Test* test, hwc2_display_t display) {
-
-                const android_color_transform_t hint =
-                        HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
-
-                for (const std::array<float, 16>& matrix : exampleMatrices) {
-                    EXPECT_NO_FATAL_FAILURE(test->setColorTransform(display,
-                            matrix, hint));
-                }
-            }
-    ));
-}
-
-/* TESTCASE: Tests that the HWC2 create an destory virtual displays. */
-TEST_F(Hwc2Test, CREATE_DESTROY_VIRTUAL_DISPLAY)
-{
-    ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(Hwc2TestCoverage::Complete,
-            [] (Hwc2Test* /*test*/, hwc2_display_t /*display*/,
-                    Hwc2TestVirtualDisplay* /*testVirtualDisplay*/) { }));
-}
-
-/* TESTCASE: Tests that the HWC2 can create and destroy multiple virtual
- * displays. */
-TEST_F(Hwc2Test, CREATE_DESTROY_VIRTUAL_DISPLAY_multiple)
-{
-    Hwc2TestVirtualDisplay testVirtualDisplay(Hwc2TestCoverage::Complete);
-    std::vector<hwc2_display_t> displays;
-
-    do {
-        const UnsignedArea& dimension =
-                testVirtualDisplay.getDisplayDimension();
-        android_pixel_format_t desiredFormat = HAL_PIXEL_FORMAT_RGBA_8888;
-        hwc2_display_t display;
-        hwc2_error_t err = HWC2_ERROR_NONE;
-
-        ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(dimension.width,
-                dimension.height, &desiredFormat, &display, &err));
-
-        EXPECT_TRUE(err == HWC2_ERROR_NONE || err == HWC2_ERROR_NO_RESOURCES
-                || err == HWC2_ERROR_UNSUPPORTED) << "returned wrong error code";
-        EXPECT_GE(desiredFormat, 0) << "invalid format";
-
-        if (err == HWC2_ERROR_NONE)
-            displays.push_back(display);
-
-    } while (testVirtualDisplay.advance());
-
-    for (hwc2_display_t display : displays) {
-        EXPECT_NO_FATAL_FAILURE(destroyVirtualDisplay(display));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 cannot destroy a bad virtual displays.  */
-TEST_F(Hwc2Test, DESTROY_VIRTUAL_DISPLAY_bad_display)
-{
-    hwc2_display_t display;
-    hwc2_error_t err = HWC2_ERROR_NONE;
-
-    ASSERT_NO_FATAL_FAILURE(getBadDisplay(&display));
-
-    ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err));
-    EXPECT_EQ(err, HWC2_ERROR_BAD_DISPLAY) << "returned wrong error code";
-}
-
-/* TESTCASE: Tests that the HWC2 cannot destroy a physical display. */
-TEST_F(Hwc2Test, DESTROY_VIRTUAL_DISPLAY_bad_parameter)
-{
-    hwc2_error_t err = HWC2_ERROR_NONE;
-    for (auto display : mDisplays) {
-        ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display, &err));
-        EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER) << "returned wrong error code";
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 can get the max virtual display count. */
-TEST_F(Hwc2Test, GET_MAX_VIRTUAL_DISPLAY_COUNT)
-{
-    uint32_t maxCnt;
-
-    ASSERT_NO_FATAL_FAILURE(getMaxVirtualDisplayCount(&maxCnt));
-}
-
-/* TESTCASE: Tests that the HWC2 returns the same max virtual display count for
- * each call. */
-TEST_F(Hwc2Test, GET_MAX_VIRTUAL_DISPLAY_COUNT_duplicate)
-{
-    uint32_t maxCnt1, maxCnt2;
-
-    ASSERT_NO_FATAL_FAILURE(getMaxVirtualDisplayCount(&maxCnt1));
-    ASSERT_NO_FATAL_FAILURE(getMaxVirtualDisplayCount(&maxCnt2));
-
-    EXPECT_EQ(maxCnt1, maxCnt2) << "returned two different max virtual display"
-            " counts";
-}
-
-/* TESTCASE: Tests that the HWC2 can create the max number of virtual displays
- * that it reports. */
-TEST_F(Hwc2Test, GET_MAX_VIRTUAL_DISPLAY_COUNT_create_max)
-{
-    std::vector<hwc2_display_t> displays;
-    uint32_t maxCnt;
-
-    ASSERT_NO_FATAL_FAILURE(getMaxVirtualDisplayCount(&maxCnt));
-
-    while (displays.size() < maxCnt) {
-        uint32_t width = 1920, height = 1080;
-        android_pixel_format_t desiredFormat = HAL_PIXEL_FORMAT_RGBA_8888;
-        hwc2_display_t display;
-        hwc2_error_t err = HWC2_ERROR_NONE;
-
-        ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(width, height,
-                    &desiredFormat, &display, &err));
-
-        EXPECT_TRUE(err == HWC2_ERROR_NONE || err == HWC2_ERROR_UNSUPPORTED)
-                << "returned wrong error code";
-        if (err != HWC2_ERROR_NONE)
-            break;
-
-        displays.push_back(display);
-    }
-
-    for (hwc2_display_t display : displays) {
-        EXPECT_NO_FATAL_FAILURE(destroyVirtualDisplay(display));
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 can set an output buffer for a virtual
- * display. */
-TEST_F(Hwc2Test, SET_OUTPUT_BUFFER)
-{
-    ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(Hwc2TestCoverage::Complete,
-            [] (Hwc2Test* test, hwc2_display_t display,
-                    Hwc2TestVirtualDisplay* testVirtualDisplay) {
-
-                buffer_handle_t handle;
-                android::base::unique_fd acquireFence;
-
-                if (testVirtualDisplay->getOutputBuffer(&handle, &acquireFence) >= 0)
-                    EXPECT_NO_FATAL_FAILURE(test->setOutputBuffer(display,
-                            handle, acquireFence));
-            }));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set an output buffer for a bad display */
-TEST_F(Hwc2Test, SET_OUTPUT_BUFFER_bad_display)
-{
-    ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(Hwc2TestCoverage::Default,
-            [] (Hwc2Test* test, hwc2_display_t /*display*/,
-                    Hwc2TestVirtualDisplay* testVirtualDisplay) {
-
-                hwc2_display_t badDisplay;
-                buffer_handle_t handle;
-                android::base::unique_fd acquireFence;
-                hwc2_error_t err = HWC2_ERROR_NONE;
-
-                ASSERT_NO_FATAL_FAILURE(test->getBadDisplay(&badDisplay));
-
-                if (testVirtualDisplay->getOutputBuffer(&handle, &acquireFence) < 0)
-                    return;
-
-                ASSERT_NO_FATAL_FAILURE(test->setOutputBuffer(badDisplay,
-                        handle, acquireFence, &err));
-                EXPECT_TRUE(err == HWC2_ERROR_BAD_DISPLAY)
-                        << "returned wrong error code";
-            }));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set an invalid output buffer. */
-TEST_F(Hwc2Test, SET_OUTPUT_BUFFER_bad_parameter)
-{
-    ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(Hwc2TestCoverage::Default,
-            [] (Hwc2Test* test, hwc2_display_t display,
-                    Hwc2TestVirtualDisplay* /*testVirtualDisplay*/) {
-
-                const buffer_handle_t handle = nullptr;
-                uint32_t releaseFence = -1;
-                hwc2_error_t err = HWC2_ERROR_NONE;
-
-                ASSERT_NO_FATAL_FAILURE(test->setOutputBuffer(display, handle,
-                        releaseFence, &err));
-                EXPECT_EQ(err, HWC2_ERROR_BAD_PARAMETER)
-                        << "returned wrong error code";
-            }));
-}
-
-/* TESTCASE: Tests that the HWC2 cannot set an output buffer for non virtual
- * display */
-TEST_F(Hwc2Test, SET_OUTPUT_BUFFER_unsupported)
-{
-    for (auto display : mDisplays) {
-        Hwc2TestVirtualDisplay testVirtualDisplay(Hwc2TestCoverage::Complete);
-
-        do {
-            buffer_handle_t handle;
-            android::base::unique_fd acquireFence;
-            hwc2_error_t err = HWC2_ERROR_NONE;
-
-            if (testVirtualDisplay.getOutputBuffer(&handle, &acquireFence) < 0)
-                continue;
-
-            ASSERT_NO_FATAL_FAILURE(setOutputBuffer(display, handle,
-                    acquireFence, &err));
-            EXPECT_EQ(err, HWC2_ERROR_UNSUPPORTED) << "returned wrong error code";
-
-        } while (testVirtualDisplay.advance());
-    }
-}
-
-/* TESTCASE: Tests that the HWC2 can dump debug information. */
-TEST_F(Hwc2Test, DUMP)
-{
-    std::string buffer;
-
-    ASSERT_NO_FATAL_FAILURE(dump(&buffer));
-}
-
-/*
- * TODO(b/64724708): Hwc2TestPropertyName::BufferArea MUST be default for all
- * virtual display tests as we don't handle this case correctly.
- *
- * Only default dataspace is supported in our drawing code.
- */
-const std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>
-        virtualDisplayExceptions =
-        {{Hwc2TestPropertyName::BufferArea, Hwc2TestCoverage::Default},
-        {Hwc2TestPropertyName::Dataspace, Hwc2TestCoverage::Default}};
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with default coverage on a
- * virtual display. */
-TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_1)
-{
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    const size_t layerCnt = 1;
-    ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage,
-            virtualDisplayExceptions));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 1 layer with basic coverage on a
- * virtual display. */
-TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_basic_1)
-{
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Basic;
-    const size_t layerCnt = 1;
-    ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage,
-            virtualDisplayExceptions));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 2 layers with default coverage on a
- * virtual display. */
-TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_2)
-{
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    const size_t layerCnt = 2;
-    ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage,
-            virtualDisplayExceptions));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 3 layers with default coverage on a
- * virtual display. */
-TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_3)
-{
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    const size_t layerCnt = 3;
-    ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage,
-            virtualDisplayExceptions));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 4 layers with default coverage on a
- * virtual display. */
-TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_4)
-{
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    const size_t layerCnt = 4;
-    ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage,
-            virtualDisplayExceptions));
-}
-
-/* TESTCASE: Tests that the HWC2 can present 5 layers with default coverage on a
- * virtual display. */
-TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_5)
-{
-    Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
-    const size_t layerCnt = 5;
-    ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage,
-            virtualDisplayExceptions));
-}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
deleted file mode 100644
index 6484562..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
+++ /dev/null
@@ -1,791 +0,0 @@
-/*
- * Copyright (C) 2016 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 <mutex>
-#include <array>
-#include <sstream>
-#include <algorithm>
-
-#include <gui/Surface.h>
-#include <gui/BufferItemConsumer.h>
-
-#include <ui/GraphicBuffer.h>
-#include <android/hardware/graphics/common/1.0/types.h>
-#include <math/vec4.h>
-
-#include <GLES3/gl3.h>
-#include <SkImageEncoder.h>
-#include <SkStream.h>
-#include "Hwc2TestBuffer.h"
-#include "Hwc2TestLayers.h"
-
-using namespace android;
-using android::hardware::graphics::common::V1_0::BufferUsage;
-
-/* Returns a fence from egl */
-typedef void (*FenceCallback)(int32_t fence, void* callbackArgs);
-
-/* Returns fence to fence generator */
-static void setFence(int32_t fence, void* fenceGenerator);
-
-
-/* Used to receive the surfaces and fences from egl. The egl buffers are thrown
- * away. The fences are sent to the requester via a callback */
-class Hwc2TestSurfaceManager {
-public:
-    /* Listens for a new frame, detaches the buffer and returns the fence
-     * through saved callback. */
-    class BufferListener : public ConsumerBase::FrameAvailableListener {
-    public:
-        BufferListener(sp<IGraphicBufferConsumer> consumer,
-                FenceCallback callback, void* callbackArgs)
-            : mConsumer(consumer),
-              mCallback(callback),
-              mCallbackArgs(callbackArgs) { }
-
-        void onFrameAvailable(const BufferItem& /*item*/)
-        {
-            BufferItem item;
-
-            if (mConsumer->acquireBuffer(&item, 0))
-                return;
-            if (mConsumer->detachBuffer(item.mSlot))
-                return;
-
-            mCallback(item.mFence->dup(), mCallbackArgs);
-        }
-
-    private:
-        sp<IGraphicBufferConsumer> mConsumer;
-        FenceCallback mCallback;
-        void* mCallbackArgs;
-    };
-
-    /* Creates a buffer listener that waits on a new frame from the buffer
-     * queue. */
-    void initialize(const Area& bufferArea, android_pixel_format_t format,
-            FenceCallback callback, void* callbackArgs)
-    {
-        sp<IGraphicBufferProducer> producer;
-        sp<IGraphicBufferConsumer> consumer;
-        BufferQueue::createBufferQueue(&producer, &consumer);
-
-        consumer->setDefaultBufferSize(bufferArea.width, bufferArea.height);
-        consumer->setDefaultBufferFormat(format);
-
-        mBufferItemConsumer = new BufferItemConsumer(consumer, 0);
-
-        mListener = new BufferListener(consumer, callback, callbackArgs);
-        mBufferItemConsumer->setFrameAvailableListener(mListener);
-
-        mSurface = new Surface(producer, true);
-    }
-
-    /* Used by Egl manager. The surface is never displayed. */
-    sp<Surface> getSurface() const
-    {
-        return mSurface;
-    }
-
-private:
-    sp<BufferItemConsumer> mBufferItemConsumer;
-    sp<BufferListener> mListener;
-    /* Used by Egl manager. The surface is never displayed */
-    sp<Surface> mSurface;
-};
-
-
-/* Used to generate valid fences. It is not possible to create a dummy sync
- * fence for testing. Egl can generate buffers along with a valid fence.
- * The buffer cannot be guaranteed to be the same format across all devices so
- * a CPU filled buffer is used instead. The Egl fence is used along with the
- * CPU filled buffer. */
-class Hwc2TestEglManager {
-public:
-    Hwc2TestEglManager()
-        : mEglDisplay(EGL_NO_DISPLAY),
-          mEglSurface(EGL_NO_SURFACE),
-          mEglContext(EGL_NO_CONTEXT) { }
-
-    ~Hwc2TestEglManager()
-    {
-        cleanup();
-    }
-
-    int initialize(sp<Surface> surface)
-    {
-        mSurface = surface;
-
-        mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-        if (mEglDisplay == EGL_NO_DISPLAY) return false;
-
-        EGLint major;
-        EGLint minor;
-        if (!eglInitialize(mEglDisplay, &major, &minor)) {
-            ALOGW("Could not initialize EGL");
-            return false;
-        }
-
-        /* We're going to use a 1x1 pbuffer surface later on
-         * The configuration distance doesn't really matter for what we're
-         * trying to do */
-        EGLint configAttrs[] = {
-                EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
-                EGL_RED_SIZE, 8,
-                EGL_GREEN_SIZE, 8,
-                EGL_BLUE_SIZE, 8,
-                EGL_ALPHA_SIZE, 0,
-                EGL_DEPTH_SIZE, 24,
-                EGL_STENCIL_SIZE, 0,
-                EGL_NONE
-        };
-
-        EGLConfig configs[1];
-        EGLint configCnt;
-        if (!eglChooseConfig(mEglDisplay, configAttrs, configs, 1,
-                &configCnt)) {
-            ALOGW("Could not select EGL configuration");
-            eglReleaseThread();
-            eglTerminate(mEglDisplay);
-            return false;
-        }
-
-        if (configCnt <= 0) {
-            ALOGW("Could not find EGL configuration");
-            eglReleaseThread();
-            eglTerminate(mEglDisplay);
-            return false;
-        }
-
-        /* These objects are initialized below but the default "null" values are
-         * used to cleanup properly at any point in the initialization sequence */
-        EGLint attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
-        mEglContext = eglCreateContext(mEglDisplay, configs[0], EGL_NO_CONTEXT,
-                attrs);
-        if (mEglContext == EGL_NO_CONTEXT) {
-            ALOGW("Could not create EGL context");
-            cleanup();
-            return false;
-        }
-
-        EGLint surfaceAttrs[] = { EGL_NONE };
-        mEglSurface = eglCreateWindowSurface(mEglDisplay, configs[0],
-                mSurface.get(), surfaceAttrs);
-        if (mEglSurface == EGL_NO_SURFACE) {
-            ALOGW("Could not create EGL surface");
-            cleanup();
-            return false;
-        }
-
-        if (!eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
-            ALOGW("Could not change current EGL context");
-            cleanup();
-            return false;
-        }
-
-        return true;
-    }
-
-    void makeCurrent() const
-    {
-        eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
-    }
-
-    void present() const
-    {
-        eglSwapBuffers(mEglDisplay, mEglSurface);
-    }
-
-private:
-    void cleanup()
-    {
-        if (mEglDisplay == EGL_NO_DISPLAY)
-            return;
-        if (mEglSurface != EGL_NO_SURFACE)
-            eglDestroySurface(mEglDisplay, mEglSurface);
-        if (mEglContext != EGL_NO_CONTEXT)
-            eglDestroyContext(mEglDisplay, mEglContext);
-
-        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
-                EGL_NO_CONTEXT);
-        eglReleaseThread();
-        eglTerminate(mEglDisplay);
-    }
-
-    sp<Surface> mSurface;
-    EGLDisplay mEglDisplay;
-    EGLSurface mEglSurface;
-    EGLContext mEglContext;
-};
-
-
-static const std::array<vec2, 4> triangles = {{
-    {  1.0f,  1.0f },
-    { -1.0f,  1.0f },
-    {  1.0f, -1.0f },
-    { -1.0f, -1.0f },
-}};
-
-class Hwc2TestFenceGenerator {
-public:
-
-    Hwc2TestFenceGenerator()
-    {
-        mSurfaceManager.initialize({1, 1}, HAL_PIXEL_FORMAT_RGBA_8888,
-                setFence, this);
-
-        if (!mEglManager.initialize(mSurfaceManager.getSurface()))
-            return;
-
-        mEglManager.makeCurrent();
-
-        glClearColor(0.0, 0.0, 0.0, 1.0);
-        glEnableVertexAttribArray(0);
-    }
-
-    ~Hwc2TestFenceGenerator()
-    {
-        if (mFence >= 0)
-            close(mFence);
-        mFence = -1;
-
-        mEglManager.makeCurrent();
-    }
-
-    /* It is not possible to simply generate a fence. The easiest way is to
-     * generate a buffer using egl and use the associated fence. The buffer
-     * cannot be guaranteed to be a certain format across all devices using this
-     * method. Instead the buffer is generated using the CPU */
-    int32_t get()
-    {
-        if (mFence >= 0) {
-            return dup(mFence);
-        }
-
-        std::unique_lock<std::mutex> lock(mMutex);
-
-        /* If the pending is still set to false and times out, we cannot recover.
-         * Set an error and return */
-        while (mPending != false) {
-            if (mCv.wait_for(lock, std::chrono::seconds(2)) == std::cv_status::timeout)
-                return -ETIME;
-        }
-
-        /* Generate a fence. The fence will be returned through the setFence
-         * callback */
-        mEglManager.makeCurrent();
-
-        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, triangles.data());
-        glClear(GL_COLOR_BUFFER_BIT);
-
-        mEglManager.present();
-
-        /* Wait for the setFence callback */
-        while (mPending != true) {
-            if (mCv.wait_for(lock, std::chrono::seconds(2)) == std::cv_status::timeout)
-                return -ETIME;
-        }
-
-        mPending = false;
-
-        return dup(mFence);
-    }
-
-    /* Callback that sets the fence */
-    void set(int32_t fence)
-    {
-        mFence = fence;
-        mPending = true;
-
-        mCv.notify_all();
-    }
-
-private:
-
-    Hwc2TestSurfaceManager mSurfaceManager;
-    Hwc2TestEglManager mEglManager;
-
-    std::mutex mMutex;
-    std::condition_variable mCv;
-
-    int32_t mFence = -1;
-    bool mPending = false;
-};
-
-
-static void setFence(int32_t fence, void* fenceGenerator)
-{
-    static_cast<Hwc2TestFenceGenerator*>(fenceGenerator)->set(fence);
-}
-
-
-/* Sets the pixel of a buffer given the location, format, stride and color.
- * Currently only supports RGBA_8888 */
-static void setColor(int32_t x, int32_t y,
-        android_pixel_format_t format, uint32_t stride, uint8_t* img, uint8_t r,
-        uint8_t g, uint8_t b, uint8_t a)
-{
-       switch (format) {
-       case HAL_PIXEL_FORMAT_RGBA_8888:
-           img[(y * stride + x) * 4 + 0] = r;
-           img[(y * stride + x) * 4 + 1] = g;
-           img[(y * stride + x) * 4 + 2] = b;
-           img[(y * stride + x) * 4 + 3] = a;
-           break;
-       default:
-           break;
-       }
-}
-
-Hwc2TestBuffer::Hwc2TestBuffer()
-    : mFenceGenerator(new Hwc2TestFenceGenerator()) { }
-
-Hwc2TestBuffer::~Hwc2TestBuffer() = default;
-
-/* When the buffer changes sizes, save the new size and invalidate the current
- * buffer */
-void Hwc2TestBuffer::updateBufferArea(const Area& bufferArea)
-{
-    if (mBufferArea.width == bufferArea.width
-            && mBufferArea.height == bufferArea.height)
-        return;
-
-    mBufferArea.width = bufferArea.width;
-    mBufferArea.height = bufferArea.height;
-
-    mValidBuffer = false;
-}
-
-/* Returns a valid buffer handle and fence. The handle is filled using the CPU
- * to ensure the correct format across all devices. The fence is created using
- * egl. */
-int Hwc2TestBuffer::get(buffer_handle_t* outHandle, int32_t* outFence)
-{
-    if (mBufferArea.width == -1 || mBufferArea.height == -1)
-        return -EINVAL;
-
-    /* If the current buffer is valid, the previous buffer can be reused.
-     * Otherwise, create new buffer */
-    if (!mValidBuffer) {
-        int ret = generateBuffer();
-        if (ret)
-            return ret;
-    }
-
-    *outFence = mFenceGenerator->get();
-    *outHandle = mHandle;
-
-    mValidBuffer = true;
-
-    return 0;
-}
-
-/* CPU fills a buffer to guarantee the correct buffer format across all
- * devices */
-int Hwc2TestBuffer::generateBuffer()
-{
-    /* Create new graphic buffer with correct dimensions */
-    mGraphicBuffer = new GraphicBuffer(mBufferArea.width, mBufferArea.height,
-            mFormat, BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-            BufferUsage::COMPOSER_OVERLAY, "hwc2_test_buffer");
-
-    int ret = mGraphicBuffer->initCheck();
-    if (ret) {
-        return ret;
-    }
-    if (!mGraphicBuffer->handle) {
-        return -EINVAL;
-    }
-
-    /* Locks the buffer for writing */
-    uint8_t* img;
-    mGraphicBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_WRITE_OFTEN),
-            (void**)(&img));
-
-    uint32_t stride = mGraphicBuffer->getStride();
-
-    /* Iterate from the top row of the buffer to the bottom row */
-    for (int32_t y = 0; y < mBufferArea.height; y++) {
-
-        /* Will be used as R, G and B values for pixel colors */
-        uint8_t max = 255;
-        uint8_t min = 0;
-
-        /* Divide the rows into 3 sections. The first section will contain
-         * the lighest colors. The last section will contain the darkest
-         * colors. */
-        if (y < mBufferArea.height * 1.0 / 3.0) {
-            min = 255 / 2;
-        } else if (y >= mBufferArea.height * 2.0 / 3.0) {
-            max = 255 / 2;
-        }
-
-        /* Divide the columns into 3 sections. The first section is red,
-         * the second is green and the third is blue */
-        int32_t x = 0;
-        for (; x < mBufferArea.width / 3; x++) {
-            setColor(x, y, mFormat, stride, img, max, min, min, 255);
-        }
-
-        for (; x < mBufferArea.width * 2 / 3; x++) {
-            setColor(x, y, mFormat, stride, img, min, max, min, 255);
-        }
-
-        for (; x < mBufferArea.width; x++) {
-            setColor(x, y, mFormat, stride, img, min, min, max, 255);
-        }
-    }
-
-    /* Unlock the buffer for reading */
-    mGraphicBuffer->unlock();
-
-    mHandle = mGraphicBuffer->handle;
-
-    return 0;
-}
-
-
-Hwc2TestClientTargetBuffer::Hwc2TestClientTargetBuffer()
-    : mFenceGenerator(new Hwc2TestFenceGenerator()) { }
-
-Hwc2TestClientTargetBuffer::~Hwc2TestClientTargetBuffer() { }
-
-/* Generates a buffer from layersToDraw.
- * Takes into account the individual layer properties such as
- * transform, blend mode, source crop, etc. */
-static void compositeBufferFromLayers(
-        const android::sp<android::GraphicBuffer>& graphicBuffer,
-        android_pixel_format_t format, const Area& bufferArea,
-        const Hwc2TestLayers* testLayers,
-        const std::set<hwc2_layer_t>* layersToDraw,
-        const std::set<hwc2_layer_t>* clearLayers)
-{
-    /* Locks the buffer for writing */
-    uint8_t* img;
-    graphicBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_WRITE_OFTEN),
-            (void**)(&img));
-
-    uint32_t stride = graphicBuffer->getStride();
-
-    float bWDiv3 = bufferArea.width / 3;
-    float bW2Div3 = bufferArea.width * 2 / 3;
-    float bHDiv3 = bufferArea.height / 3;
-    float bH2Div3 = bufferArea.height * 2 / 3;
-
-    /* Cycle through every pixel in the buffer and determine what color it
-     * should be. */
-    for (int32_t y = 0; y < bufferArea.height; y++) {
-        for (int32_t x = 0; x < bufferArea.width; x++) {
-
-            uint8_t r = 0, g = 0, b = 0;
-            float a = 0.0f;
-
-            /* Cycle through each layer from back to front and
-             * update the pixel color. */
-            for (auto layer = layersToDraw->rbegin();
-                    layer != layersToDraw->rend(); ++layer) {
-
-                const hwc_rect_t df = testLayers->getDisplayFrame(*layer);
-
-                float dfL = df.left;
-                float dfT = df.top;
-                float dfR = df.right;
-                float dfB = df.bottom;
-
-                /* If the pixel location falls outside of the layer display
-                 * frame, skip the layer. */
-                if (x < dfL || x >= dfR || y < dfT || y >= dfB)
-                    continue;
-
-                /* If the device has requested the layer be clear, clear
-                 * the pixel and continue. */
-                if (clearLayers->count(*layer) != 0) {
-                    r = 0;
-                    g = 0;
-                    b = 0;
-                    a = 0.0f;
-                    continue;
-                }
-
-                float planeAlpha = testLayers->getPlaneAlpha(*layer);
-
-                /* If the layer is a solid color, fill the color and
-                 * continue. */
-                if (testLayers->getComposition(*layer)
-                        == HWC2_COMPOSITION_SOLID_COLOR) {
-                    const auto color = testLayers->getColor(*layer);
-                    r = color.r;
-                    g = color.g;
-                    b = color.b;
-                    a = color.a * planeAlpha;
-                    continue;
-                }
-
-                float xPos = x;
-                float yPos = y;
-
-                hwc_transform_t transform = testLayers->getTransform(*layer);
-
-                float dfW = dfR - dfL;
-                float dfH = dfB - dfT;
-
-                /* If a layer has a transform, find which location on the
-                 * layer will end up in the current pixel location. We
-                 * can calculate the color of the current pixel using that
-                 * location. */
-                if (transform > 0) {
-                    /* Change origin to be the center of the layer. */
-                    xPos = xPos - dfL - dfW / 2.0;
-                    yPos = yPos - dfT - dfH / 2.0;
-
-                    /* Flip Horizontal by reflecting across the y axis. */
-                    if (transform & HWC_TRANSFORM_FLIP_H)
-                        xPos = -xPos;
-
-                    /* Flip vertical by reflecting across the x axis. */
-                    if (transform & HWC_TRANSFORM_FLIP_V)
-                        yPos = -yPos;
-
-                    /* Rotate 90 by using a basic linear algebra rotation
-                     * and scaling the result so the display frame remains
-                     * the same. For example, a buffer of size 100x50 should
-                     * rotate 90 degress but remain the same dimension
-                     * (100x50) at the end of the transformation. */
-                    if (transform & HWC_TRANSFORM_ROT_90) {
-                        float tmp = xPos;
-                        xPos = yPos * dfW / dfH;
-                        yPos = -tmp * dfH / dfW;
-                    }
-
-                    /* Change origin back to the top left corner of the
-                     * layer. */
-                    xPos = xPos + dfL + dfW / 2.0;
-                    yPos = yPos + dfT + dfH / 2.0;
-                }
-
-                hwc_frect_t sc = testLayers->getSourceCrop(*layer);
-                float scL = sc.left, scT = sc.top;
-
-                float dfWDivScW = dfW / (sc.right - scL);
-                float dfHDivScH = dfH / (sc.bottom - scT);
-
-                float max = 255, min = 0;
-
-                /* Choose the pixel color. Similar to generateBuffer,
-                 * each layer will be divided into 3x3 colors. Because
-                 * both the source crop and display frame must be taken into
-                 * account, the formulas are more complicated.
-                 *
-                 * If the source crop and display frame were not taken into
-                 * account, we would simply divide the buffer into three
-                 * sections by height. Each section would get one color.
-                 * For example the formula for the first section would be:
-                 *
-                 * if (yPos < bufferArea.height / 3)
-                 *        //Select first section color
-                 *
-                 * However the pixel color is chosen based on the source
-                 * crop and displayed based on the display frame.
-                 *
-                 * If the display frame top was 0 and the source crop height
-                 * and display frame height were the same. The only factor
-                 * would be the source crop top. To calculate the new
-                 * section boundary, the section boundary would be moved up
-                 * by the height of the source crop top. The formula would
-                 * be:
-                 * if (yPos < (bufferArea.height / 3 - sourceCrop.top)
-                 *        //Select first section color
-                 *
-                 * If the display frame top could also vary but source crop
-                 * and display frame heights were the same, the formula
-                 * would be:
-                 * if (yPos < (bufferArea.height / 3 - sourceCrop.top
-                 *              + displayFrameTop)
-                 *        //Select first section color
-                 *
-                 * If the heights were not the same, the conversion between
-                 * the source crop and display frame dimensions must be
-                 * taken into account. The formula would be:
-                 * if (yPos < ((bufferArea.height / 3) - sourceCrop.top)
-                 *              * displayFrameHeight / sourceCropHeight
-                 *              + displayFrameTop)
-                 *        //Select first section color
-                 */
-                if (yPos < ((bHDiv3) - scT) * dfHDivScH + dfT) {
-                    min = 255 / 2;
-                } else if (yPos >= ((bH2Div3) - scT) * dfHDivScH + dfT) {
-                    max = 255 / 2;
-                }
-
-                uint8_t rCur = min, gCur = min, bCur = min;
-                float aCur = 1.0f;
-
-                /* This further divides the color sections from 3 to 3x3.
-                 * The math behind it follows the same logic as the previous
-                 * comment */
-                if (xPos < ((bWDiv3) - scL) * (dfWDivScW) + dfL) {
-                    rCur = max;
-                } else if (xPos < ((bW2Div3) - scL) * (dfWDivScW) + dfL) {
-                    gCur = max;
-                } else {
-                    bCur = max;
-                }
-
-
-                /* Blend the pixel color with the previous layers' pixel
-                 * colors using the plane alpha and blend mode. The final
-                 * pixel color is chosen using the plane alpha and blend
-                 * mode formulas found in hwcomposer2.h */
-                hwc2_blend_mode_t blendMode = testLayers->getBlendMode(*layer);
-
-                if (blendMode == HWC2_BLEND_MODE_PREMULTIPLIED) {
-                    rCur *= planeAlpha;
-                    gCur *= planeAlpha;
-                    bCur *= planeAlpha;
-                }
-
-                aCur *= planeAlpha;
-
-                if (blendMode == HWC2_BLEND_MODE_PREMULTIPLIED) {
-                    r = rCur + r * (1.0 - aCur);
-                    g = gCur + g * (1.0 - aCur);
-                    b = bCur + b * (1.0 - aCur);
-                    a = aCur + a * (1.0 - aCur);
-                } else if (blendMode == HWC2_BLEND_MODE_COVERAGE) {
-                    r = rCur * aCur + r * (1.0 - aCur);
-                    g = gCur * aCur + g * (1.0 - aCur);
-                    b = bCur * aCur + b * (1.0 - aCur);
-                    a = aCur * aCur + a * (1.0 - aCur);
-                } else {
-                    r = rCur;
-                    g = gCur;
-                    b = bCur;
-                    a = aCur;
-                }
-            }
-
-            /* Set the pixel color */
-            setColor(x, y, format, stride, img, r, g, b, a * 255);
-        }
-    }
-
-    graphicBuffer->unlock();
-}
-
-/* Generates a client target buffer using the layers assigned for client
- * composition. Takes into account the individual layer properties such as
- * transform, blend mode, source crop, etc. */
-int Hwc2TestClientTargetBuffer::get(buffer_handle_t* outHandle,
-        int32_t* outFence, const Area& bufferArea,
-        const Hwc2TestLayers* testLayers,
-        const std::set<hwc2_layer_t>* clientLayers,
-        const std::set<hwc2_layer_t>* clearLayers)
-{
-    /* Create new graphic buffer with correct dimensions */
-    mGraphicBuffer = new GraphicBuffer(bufferArea.width, bufferArea.height,
-            mFormat, BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-            BufferUsage::COMPOSER_OVERLAY, "hwc2_test_buffer");
-
-    int ret = mGraphicBuffer->initCheck();
-    if (ret)
-        return ret;
-
-    if (!mGraphicBuffer->handle)
-        return -EINVAL;
-
-    compositeBufferFromLayers(mGraphicBuffer, mFormat, bufferArea, testLayers,
-            clientLayers, clearLayers);
-
-    *outFence = mFenceGenerator->get();
-    *outHandle = mGraphicBuffer->handle;
-
-    return 0;
-}
-
-void Hwc2TestVirtualBuffer::updateBufferArea(const Area& bufferArea)
-{
-    mBufferArea.width = bufferArea.width;
-    mBufferArea.height = bufferArea.height;
-}
-
-bool Hwc2TestVirtualBuffer::writeBufferToFile(std::string path)
-{
-    SkFILEWStream file(path.c_str());
-    const SkImageInfo info = SkImageInfo::Make(mBufferArea.width,
-            mBufferArea.height, SkColorType::kRGBA_8888_SkColorType,
-            SkAlphaType::kPremul_SkAlphaType);
-
-    uint8_t* img;
-    mGraphicBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_WRITE_OFTEN),
-            (void**)(&img));
-
-    SkPixmap pixmap(info, img, mGraphicBuffer->getStride());
-    bool result = file.isValid() && SkEncodeImage(&file, pixmap,
-            SkEncodedImageFormat::kPNG, 100);
-
-    mGraphicBuffer->unlock();
-    return result;
-}
-
-/* Generates a buffer that holds the expected result of compositing all of our
- * layers */
-int Hwc2TestExpectedBuffer::generateExpectedBuffer(
-        const Hwc2TestLayers* testLayers,
-        const std::vector<hwc2_layer_t>* allLayers,
-        const std::set<hwc2_layer_t>* clearLayers)
-{
-    mGraphicBuffer = new GraphicBuffer(mBufferArea.width, mBufferArea.height,
-            mFormat, BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
-            "hwc2_test_buffer");
-
-    int ret = mGraphicBuffer->initCheck();
-    if (ret)
-        return ret;
-
-    if (!mGraphicBuffer->handle)
-        return -EINVAL;
-
-    const std::set<hwc2_layer_t> allLayerSet(allLayers->begin(),
-            allLayers->end());
-
-    compositeBufferFromLayers(mGraphicBuffer, mFormat, mBufferArea, testLayers,
-            &allLayerSet, clearLayers);
-
-    return 0;
-}
-
-int Hwc2TestOutputBuffer::getOutputBuffer(buffer_handle_t* outHandle,
-        int32_t* outFence)
-{
-    if (mBufferArea.width == -1 || mBufferArea.height == -1)
-        return -EINVAL;
-
-    mGraphicBuffer = new GraphicBuffer(mBufferArea.width, mBufferArea.height,
-            mFormat, BufferUsage::CPU_READ_OFTEN |
-            BufferUsage::GPU_RENDER_TARGET, "hwc2_test_buffer");
-
-    int ret = mGraphicBuffer->initCheck();
-    if (ret)
-        return ret;
-
-    if (!mGraphicBuffer->handle)
-        return -EINVAL;
-
-    *outFence = -1;
-    *outHandle = mGraphicBuffer->handle;
-
-    return 0;
-}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h
deleted file mode 100644
index fd54fef..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2016 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 _HWC2_TEST_BUFFER_H
-#define _HWC2_TEST_BUFFER_H
-
-#include <android-base/unique_fd.h>
-#include <set>
-
-#include <hardware/hwcomposer2.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include "Hwc2TestProperties.h"
-
-class Hwc2TestFenceGenerator;
-class Hwc2TestLayers;
-
-class Hwc2TestBuffer {
-public:
-    Hwc2TestBuffer();
-    ~Hwc2TestBuffer();
-
-    void updateBufferArea(const Area& bufferArea);
-
-    int  get(buffer_handle_t* outHandle, int32_t* outFence);
-
-protected:
-    int generateBuffer();
-
-    android::sp<android::GraphicBuffer> mGraphicBuffer;
-
-    std::unique_ptr<Hwc2TestFenceGenerator> mFenceGenerator;
-
-    Area mBufferArea = {-1, -1};
-    const android_pixel_format_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
-
-    bool mValidBuffer = false;
-    buffer_handle_t mHandle = nullptr;
-};
-
-
-class Hwc2TestClientTargetBuffer {
-public:
-    Hwc2TestClientTargetBuffer();
-    ~Hwc2TestClientTargetBuffer();
-
-    int  get(buffer_handle_t* outHandle, int32_t* outFence,
-            const Area& bufferArea, const Hwc2TestLayers* testLayers,
-            const std::set<hwc2_layer_t>* clientLayers,
-            const std::set<hwc2_layer_t>* clearLayers);
-
-protected:
-    android::sp<android::GraphicBuffer> mGraphicBuffer;
-
-    std::unique_ptr<Hwc2TestFenceGenerator> mFenceGenerator;
-
-    const android_pixel_format_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
-};
-
-
-class Hwc2TestVirtualBuffer {
-public:
-    void updateBufferArea(const Area& bufferArea);
-
-    bool writeBufferToFile(std::string path);
-
-    android::sp<android::GraphicBuffer>& graphicBuffer()
-    {
-        return mGraphicBuffer;
-    }
-
-protected:
-    android::sp<android::GraphicBuffer> mGraphicBuffer;
-
-    Area mBufferArea = {-1, -1};
-
-    const android_pixel_format_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
-};
-
-
-class Hwc2TestExpectedBuffer : public Hwc2TestVirtualBuffer {
-public:
-    int generateExpectedBuffer(const Hwc2TestLayers* testLayers,
-            const std::vector<hwc2_layer_t>* allLayers,
-            const std::set<hwc2_layer_t>* clearLayers);
-};
-
-
-class Hwc2TestOutputBuffer : public Hwc2TestVirtualBuffer {
-public:
-    int getOutputBuffer(buffer_handle_t* outHandle, int32_t* outFence);
-};
-
-#endif /* ifndef _HWC2_TEST_BUFFER_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.cpp
deleted file mode 100644
index 14c60a7..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2016 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 <sstream>
-
-#include <ui/Rect.h>
-
-#include "Hwc2TestClientTarget.h"
-
-int Hwc2TestClientTarget::getBuffer(const Hwc2TestLayers& testLayers,
-        const std::set<hwc2_layer_t>& clientLayers,
-        const std::set<hwc2_layer_t>& clearLayers, bool flipClientTarget,
-        const Area& displayArea, buffer_handle_t* outHandle,
-        int32_t* outAcquireFence)
-{
-    if (!flipClientTarget) {
-        bool needsClientTarget = false;
-
-        for (auto clientLayer : clientLayers) {
-            if (testLayers.getVisibleRegion(clientLayer).numRects > 0) {
-                needsClientTarget = true;
-                break;
-            }
-        }
-
-        if (!needsClientTarget) {
-           *outHandle = nullptr;
-           *outAcquireFence = -1;
-           return 0;
-        }
-    }
-
-    return mBuffer.get(outHandle, outAcquireFence, displayArea,
-            &testLayers, &clientLayers, &clearLayers);
-}
-
-
-Hwc2TestClientTargetSupport::Hwc2TestClientTargetSupport(
-        Hwc2TestCoverage coverage, const Area& displayArea)
-    : mBufferArea(coverage, displayArea),
-      mDataspace(coverage),
-      mSurfaceDamage(coverage)
-{
-    mBufferArea.setDependent(&mSurfaceDamage);
-}
-
-std::string Hwc2TestClientTargetSupport::dump() const
-{
-    std::stringstream dmp;
-
-    dmp << "client target: \n";
-
-    for (auto property : properties) {
-        dmp << property->dump();
-    }
-
-    return dmp.str();
-}
-
-void Hwc2TestClientTargetSupport::reset()
-{
-    for (auto property : properties) {
-        property->reset();
-    }
-}
-
-bool Hwc2TestClientTargetSupport::advance()
-{
-    for (auto property : properties) {
-        if (property->advance())
-            return true;
-    }
-    return false;
-}
-
-Area Hwc2TestClientTargetSupport::getBufferArea() const
-{
-    return mBufferArea.get();
-}
-
-android::ui::Dataspace Hwc2TestClientTargetSupport::getDataspace() const
-{
-    return mDataspace.get();
-}
-
-const hwc_region_t Hwc2TestClientTargetSupport::getSurfaceDamage() const
-{
-    return mSurfaceDamage.get();
-}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.h b/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.h
deleted file mode 100644
index 6f4090f..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestClientTarget.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2016 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 _HWC2_TEST_CLIENT_TARGET_H
-#define _HWC2_TEST_CLIENT_TARGET_H
-
-#include <set>
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-#include "Hwc2TestProperties.h"
-#include "Hwc2TestLayers.h"
-
-/* Generates client target buffers from client composition layers */
-class Hwc2TestClientTarget {
-public:
-    int getBuffer(const Hwc2TestLayers& layers,
-            const std::set<hwc2_layer_t>& clientLayers,
-            const std::set<hwc2_layer_t>& clearLayers,
-            bool clearClientTarget, const Area& displayArea,
-            buffer_handle_t* outHandle, int32_t* outAcquireFence);
-
-private:
-    Hwc2TestClientTargetBuffer mBuffer;
-};
-
-/* Generates valid client targets to test which ones the device will support */
-class Hwc2TestClientTargetSupport {
-public:
-    Hwc2TestClientTargetSupport(Hwc2TestCoverage coverage,
-            const Area& displayArea);
-
-    std::string dump() const;
-
-    void reset();
-    bool advance();
-
-    Area getBufferArea() const;
-    android::ui::Dataspace getDataspace() const;
-    const hwc_region_t getSurfaceDamage() const;
-
-private:
-    std::array<Hwc2TestContainer*, 3> properties = {{
-        &mDataspace, &mSurfaceDamage, &mBufferArea
-    }};
-
-    Hwc2TestBufferArea mBufferArea;
-    Hwc2TestDataspace mDataspace;
-    Hwc2TestSurfaceDamage mSurfaceDamage;
-};
-
-#endif /* ifndef _HWC2_TEST_CLIENT_TARGET_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.cpp
deleted file mode 100644
index c1c9cc8..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.cpp
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright (C) 2016 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 <sstream>
-
-#include "Hwc2TestLayer.h"
-
-Hwc2TestCoverage getCoverage(Hwc2TestPropertyName property,
-        Hwc2TestCoverage coverage, const std::unordered_map<Hwc2TestPropertyName,
-        Hwc2TestCoverage>& coverageExceptions) {
-    auto exception = coverageExceptions.find(property);
-    return (exception != coverageExceptions.end())? exception->second : coverage;
-}
-
-Hwc2TestLayer::Hwc2TestLayer(Hwc2TestCoverage coverage,
-        const Area& displayArea)
-    : Hwc2TestLayer(coverage, displayArea,
-            std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>()) { }
-
-Hwc2TestLayer::Hwc2TestLayer(Hwc2TestCoverage coverage,
-        const Area& displayArea, const std::unordered_map<Hwc2TestPropertyName,
-        Hwc2TestCoverage>& coverageExceptions)
-    : mBlendMode(getCoverage(Hwc2TestPropertyName::BlendMode, coverage,
-           coverageExceptions)),
-      mBufferArea(getCoverage(Hwc2TestPropertyName::BufferArea, coverage,
-           coverageExceptions), displayArea),
-      mColor(getCoverage(Hwc2TestPropertyName::Color, coverage,
-           coverageExceptions)),
-      mComposition(getCoverage(Hwc2TestPropertyName::Composition, coverage,
-           coverageExceptions)),
-      mDataspace(getCoverage(Hwc2TestPropertyName::Dataspace, coverage,
-           coverageExceptions)),
-      mDisplayFrame(getCoverage(Hwc2TestPropertyName::DisplayFrame, coverage,
-           coverageExceptions), displayArea),
-      mPlaneAlpha(getCoverage(Hwc2TestPropertyName::PlaneAlpha, coverage,
-           coverageExceptions)),
-      mSourceCrop(getCoverage(Hwc2TestPropertyName::SourceCrop, coverage,
-           coverageExceptions)),
-      mSurfaceDamage(getCoverage(Hwc2TestPropertyName::SurfaceDamage, coverage,
-           coverageExceptions)),
-      mTransform(getCoverage(Hwc2TestPropertyName::Transform, coverage,
-           coverageExceptions))
-{
-    mBufferArea.setDependent(&mBuffer);
-    mBufferArea.setDependent(&mSourceCrop);
-    mBufferArea.setDependent(&mSurfaceDamage);
-    mBlendMode.setDependent(&mColor);
-}
-
-std::string Hwc2TestLayer::dump() const
-{
-    std::stringstream dmp;
-
-    dmp << "layer: \n";
-
-    for (auto property : mProperties) {
-        dmp << property->dump();
-    }
-
-    dmp << mVisibleRegion.dump();
-    dmp << "\tz order: " << mZOrder << "\n";
-
-    return dmp.str();
-}
-
-int Hwc2TestLayer::getBuffer(buffer_handle_t* outHandle,
-        android::base::unique_fd* outAcquireFence)
-{
-    int32_t acquireFence;
-    int ret = mBuffer.get(outHandle, &acquireFence);
-    outAcquireFence->reset(acquireFence);
-    return ret;
-}
-
-int Hwc2TestLayer::getBuffer(buffer_handle_t* outHandle,
-        int32_t* outAcquireFence)
-{
-    return mBuffer.get(outHandle, outAcquireFence);
-}
-
-void Hwc2TestLayer::setZOrder(uint32_t zOrder)
-{
-    mZOrder = zOrder;
-}
-
-void Hwc2TestLayer::setVisibleRegion(const android::Region& region)
-{
-    return mVisibleRegion.set(region);
-}
-
-void Hwc2TestLayer::reset()
-{
-    mVisibleRegion.release();
-
-    for (auto property : mProperties) {
-        property->reset();
-    }
-}
-
-bool Hwc2TestLayer::advance()
-{
-    for (auto property : mProperties) {
-        if (property->isSupported(mComposition.get()))
-            if (property->advance())
-                return true;
-    }
-    return false;
-}
-
-hwc2_blend_mode_t Hwc2TestLayer::getBlendMode() const
-{
-    return mBlendMode.get();
-}
-
-Area Hwc2TestLayer::getBufferArea() const
-{
-    return mBufferArea.get();
-}
-
-hwc_color_t Hwc2TestLayer::getColor() const
-{
-    return mColor.get();
-}
-
-hwc2_composition_t Hwc2TestLayer::getComposition() const
-{
-    return mComposition.get();
-}
-
-/* The cursor position corresponds to {displayFrame.left, displayFrame.top} */
-hwc_rect_t Hwc2TestLayer::getCursorPosition() const
-{
-    return mDisplayFrame.get();
-}
-
-android::ui::Dataspace Hwc2TestLayer::getDataspace() const
-{
-    return mDataspace.get();
-}
-
-hwc_rect_t Hwc2TestLayer::getDisplayFrame() const
-{
-    return mDisplayFrame.get();
-}
-
-float Hwc2TestLayer::getPlaneAlpha() const
-{
-    return mPlaneAlpha.get();
-}
-
-hwc_frect_t Hwc2TestLayer::getSourceCrop() const
-{
-    return mSourceCrop.get();
-}
-
-hwc_region_t Hwc2TestLayer::getSurfaceDamage() const
-{
-    return mSurfaceDamage.get();
-}
-
-hwc_transform_t Hwc2TestLayer::getTransform() const
-{
-    return mTransform.get();
-}
-
-hwc_region_t Hwc2TestLayer::getVisibleRegion() const
-{
-    return mVisibleRegion.get();
-}
-
-uint32_t Hwc2TestLayer::getZOrder() const
-{
-    return mZOrder;
-}
-
-bool Hwc2TestLayer::advanceBlendMode()
-{
-    return mBlendMode.advance();
-}
-
-bool Hwc2TestLayer::advanceBufferArea()
-{
-    return mBufferArea.advance();
-}
-
-bool Hwc2TestLayer::advanceColor()
-{
-    return mColor.advance();
-}
-
-bool Hwc2TestLayer::advanceComposition()
-{
-    return mComposition.advance();
-}
-
-bool Hwc2TestLayer::advanceCursorPosition()
-{
-    return mDisplayFrame.advance();
-}
-
-bool Hwc2TestLayer::advanceDataspace()
-{
-    return mDataspace.advance();
-}
-
-bool Hwc2TestLayer::advanceDisplayFrame()
-{
-    return mDisplayFrame.advance();
-}
-
-bool Hwc2TestLayer::advancePlaneAlpha()
-{
-    return mPlaneAlpha.advance();
-}
-
-bool Hwc2TestLayer::advanceSourceCrop()
-{
-    return mSourceCrop.advance();
-}
-
-bool Hwc2TestLayer::advanceSurfaceDamage()
-{
-    return mSurfaceDamage.advance();
-}
-
-bool Hwc2TestLayer::advanceTransform()
-{
-    return mTransform.advance();
-}
-
-bool Hwc2TestLayer::advanceVisibleRegion()
-{
-    if (mPlaneAlpha.advance())
-        return true;
-    return mDisplayFrame.advance();
-}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.h b/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.h
deleted file mode 100644
index 29ae521..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestLayer.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2016 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 _HWC2_TEST_LAYER_H
-#define _HWC2_TEST_LAYER_H
-
-#include <android-base/unique_fd.h>
-#include <unordered_map>
-
-#include "Hwc2TestBuffer.h"
-#include "Hwc2TestProperties.h"
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-class Hwc2TestLayer {
-public:
-    Hwc2TestLayer(Hwc2TestCoverage coverage, const Area& displayArea);
-
-    Hwc2TestLayer(Hwc2TestCoverage coverage, const Area& displayArea,
-            const std::unordered_map<Hwc2TestPropertyName,
-            Hwc2TestCoverage>& coverage_exceptions);
-
-    std::string dump() const;
-
-    int getBuffer(buffer_handle_t* outHandle,
-            android::base::unique_fd* outAcquireFence);
-    int getBuffer(buffer_handle_t* outHandle, int32_t* outAcquireFence);
-
-    void setZOrder(uint32_t zOrder);
-    void setVisibleRegion(const android::Region& region);
-
-    void reset();
-    bool advance();
-
-    hwc2_blend_mode_t      getBlendMode() const;
-    Area                   getBufferArea() const;
-    hwc_color_t            getColor() const;
-    hwc2_composition_t     getComposition() const;
-    hwc_rect_t             getCursorPosition() const;
-    android::ui::Dataspace     getDataspace() const;
-    hwc_rect_t             getDisplayFrame() const;
-    float                  getPlaneAlpha() const;
-    hwc_frect_t            getSourceCrop() const;
-    hwc_region_t           getSurfaceDamage() const;
-    hwc_transform_t        getTransform() const;
-    hwc_region_t           getVisibleRegion() const;
-    uint32_t               getZOrder() const;
-
-    bool advanceBlendMode();
-    bool advanceBufferArea();
-    bool advanceColor();
-    bool advanceComposition();
-    bool advanceCursorPosition();
-    bool advanceDataspace();
-    bool advanceDisplayFrame();
-    bool advancePlaneAlpha();
-    bool advanceSourceCrop();
-    bool advanceSurfaceDamage();
-    bool advanceTransform();
-    bool advanceVisibleRegion();
-
-private:
-    std::array<Hwc2TestContainer*, 10> mProperties = {{
-        &mTransform, &mColor, &mDataspace, &mPlaneAlpha, &mSourceCrop,
-        &mSurfaceDamage, &mBlendMode, &mBufferArea, &mDisplayFrame,
-        &mComposition
-    }};
-
-    Hwc2TestBuffer mBuffer;
-
-    Hwc2TestBlendMode mBlendMode;
-    Hwc2TestBufferArea mBufferArea;
-    Hwc2TestColor mColor;
-    Hwc2TestComposition mComposition;
-    Hwc2TestDataspace mDataspace;
-    Hwc2TestDisplayFrame mDisplayFrame;
-    Hwc2TestPlaneAlpha mPlaneAlpha;
-    Hwc2TestSourceCrop mSourceCrop;
-    Hwc2TestSurfaceDamage mSurfaceDamage;
-    Hwc2TestTransform mTransform;
-    Hwc2TestVisibleRegion mVisibleRegion;
-
-    uint32_t mZOrder = UINT32_MAX;
-};
-
-#endif /* ifndef _HWC2_TEST_LAYER_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.cpp
deleted file mode 100644
index 90127a1..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.cpp
+++ /dev/null
@@ -1,281 +0,0 @@
-/* * Copyright (C) 2016 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 <sstream>
-#include <gtest/gtest.h>
-
-#include "Hwc2TestLayers.h"
-
-Hwc2TestLayers::Hwc2TestLayers(const std::vector<hwc2_layer_t>& layers,
-        Hwc2TestCoverage coverage, const Area& displayArea)
-    : Hwc2TestLayers(layers, coverage, displayArea,
-            std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>()) { }
-
-Hwc2TestLayers::Hwc2TestLayers(const std::vector<hwc2_layer_t>& layers,
-        Hwc2TestCoverage coverage, const Area& displayArea,
-        const std::unordered_map<Hwc2TestPropertyName,
-        Hwc2TestCoverage>& coverageExceptions)
-    : mDisplayArea(displayArea)
-{
-    for (auto layer : layers) {
-        mTestLayers.emplace(std::piecewise_construct,
-                std::forward_as_tuple(layer),
-                std::forward_as_tuple(coverage, displayArea, coverageExceptions));
-    }
-
-    /* Iterate over the layers in order and assign z orders in the same order.
-     * This allows us to iterate over z orders in the same way when computing
-     * visible regions */
-    uint32_t nextZOrder = layers.size();
-
-    for (auto& testLayer : mTestLayers) {
-        testLayer.second.setZOrder(nextZOrder--);
-    }
-
-    setVisibleRegions();
-}
-
-std::string Hwc2TestLayers::dump() const
-{
-    std::stringstream dmp;
-    for (auto& testLayer : mTestLayers) {
-        dmp << testLayer.second.dump();
-    }
-    return dmp.str();
-}
-
-void Hwc2TestLayers::reset()
-{
-    for (auto& testLayer : mTestLayers) {
-        testLayer.second.reset();
-    }
-
-    setVisibleRegions();
-}
-
-bool Hwc2TestLayers::advance()
-{
-    auto itr = mTestLayers.begin();
-    bool optimized;
-
-    while (itr != mTestLayers.end()) {
-        if (itr->second.advance()) {
-            optimized = setVisibleRegions();
-            if (!mOptimize || optimized)
-                return true;
-            itr = mTestLayers.begin();
-        } else {
-            itr->second.reset();
-            ++itr;
-        }
-    }
-    return false;
-}
-
-bool Hwc2TestLayers::advanceVisibleRegions()
-{
-    auto itr = mTestLayers.begin();
-    bool optimized;
-
-    while (itr != mTestLayers.end()) {
-        if (itr->second.advanceVisibleRegion()) {
-            optimized = setVisibleRegions();
-            if (!mOptimize || optimized)
-                return true;
-            itr = mTestLayers.begin();
-        } else {
-            itr->second.reset();
-            ++itr;
-        }
-    }
-    return false;
-}
-
-/* Removes layouts that do not cover the entire display.
- * Also removes layouts where a layer is completely blocked from view.
- */
-bool Hwc2TestLayers::optimizeLayouts()
-{
-    mOptimize = true;
-
-    if (setVisibleRegions())
-        return true;
-    return advance();
-}
-
-bool Hwc2TestLayers::contains(hwc2_layer_t layer) const
-{
-    return mTestLayers.count(layer) != 0;
-}
-
-int Hwc2TestLayers::getBuffer(hwc2_layer_t layer, buffer_handle_t* outHandle,
-        int32_t* outAcquireFence)
-{
-    if (mTestLayers.count(layer) == 0) {
-        []() { GTEST_FAIL(); }();
-    }
-    return mTestLayers.at(layer).getBuffer(outHandle, outAcquireFence);
-}
-
-hwc2_blend_mode_t Hwc2TestLayers::getBlendMode(hwc2_layer_t layer) const
-{
-    if (mTestLayers.count(layer) == 0) {
-        []() { GTEST_FAIL(); }();
-    }
-    return mTestLayers.at(layer).getBlendMode();
-}
-
-Area Hwc2TestLayers::getBufferArea(hwc2_layer_t layer) const
-{
-    auto testLayer = mTestLayers.find(layer);
-    if (testLayer == mTestLayers.end())
-        [] () { GTEST_FAIL(); }();
-    return testLayer->second.getBufferArea();
-}
-
-hwc_color_t Hwc2TestLayers::getColor(hwc2_layer_t layer) const
-{
-    if (mTestLayers.count(layer) == 0) {
-        []() { GTEST_FAIL(); }();
-    }
-    return mTestLayers.at(layer).getColor();
-}
-
-hwc2_composition_t Hwc2TestLayers::getComposition(hwc2_layer_t layer) const
-{
-    if (mTestLayers.count(layer) == 0) {
-        []() { GTEST_FAIL(); }();
-    }
-    return mTestLayers.at(layer).getComposition();
-}
-
-hwc_rect_t Hwc2TestLayers::getCursorPosition(hwc2_layer_t layer) const
-{
-    if (mTestLayers.count(layer) == 0) {
-        []() { GTEST_FAIL(); }();
-    }
-    return mTestLayers.at(layer).getCursorPosition();
-}
-
-android::ui::Dataspace Hwc2TestLayers::getDataspace(hwc2_layer_t layer) const
-{
-    if (mTestLayers.count(layer) == 0) {
-        []() { GTEST_FAIL(); }();
-    }
-    return mTestLayers.at(layer).getDataspace();
-}
-
-hwc_rect_t Hwc2TestLayers::getDisplayFrame(hwc2_layer_t layer) const
-{
-    if (mTestLayers.count(layer) == 0) {
-        []() { GTEST_FAIL(); }();
-    }
-    return mTestLayers.at(layer).getDisplayFrame();
-}
-
-float Hwc2TestLayers::getPlaneAlpha(hwc2_layer_t layer) const
-{
-    if (mTestLayers.count(layer) == 0) {
-        []() { GTEST_FAIL(); }();
-    }
-    return mTestLayers.at(layer).getPlaneAlpha();
-}
-
-hwc_frect_t Hwc2TestLayers::getSourceCrop(hwc2_layer_t layer) const
-{
-    if (mTestLayers.count(layer) == 0) {
-        []() { GTEST_FAIL(); }();
-    }
-    return mTestLayers.at(layer).getSourceCrop();
-}
-
-hwc_region_t Hwc2TestLayers::getSurfaceDamage(hwc2_layer_t layer) const
-{
-    if (mTestLayers.count(layer) == 0) {
-        []() { GTEST_FAIL(); }();
-    }
-    return mTestLayers.at(layer).getSurfaceDamage();
-}
-
-hwc_transform_t Hwc2TestLayers::getTransform(hwc2_layer_t layer) const
-{
-    if (mTestLayers.count(layer) == 0) {
-        []() { GTEST_FAIL(); }();
-    }
-    return mTestLayers.at(layer).getTransform();
-}
-
-hwc_region_t Hwc2TestLayers::getVisibleRegion(hwc2_layer_t layer) const
-{
-    if (mTestLayers.count(layer) == 0) {
-        []() { GTEST_FAIL(); }();
-    }
-    return mTestLayers.at(layer).getVisibleRegion();
-}
-
-uint32_t Hwc2TestLayers::getZOrder(hwc2_layer_t layer) const
-{
-    if (mTestLayers.count(layer) == 0) {
-        []() { GTEST_FAIL(); }();
-    }
-    return mTestLayers.at(layer).getZOrder();
-}
-
-/* Sets the visible regions for a display. Returns false if the layers do not
- * cover the entire display or if a layer is not visible */
-bool Hwc2TestLayers::setVisibleRegions()
-{
-    /* The region of the display that is covered by layers above the current
-     * layer */
-    android::Region aboveOpaqueLayers;
-
-    bool optimized = true;
-
-    /* Iterate over test layers from max z order to min z order. */
-    for (auto& testLayer : mTestLayers) {
-        android::Region visibleRegion;
-
-        /* Set the visible region of this layer */
-        const hwc_rect_t displayFrame = testLayer.second.getDisplayFrame();
-
-        visibleRegion.set(android::Rect(displayFrame.left, displayFrame.top,
-                displayFrame.right, displayFrame.bottom));
-
-        /* Remove the area covered by opaque layers above this layer
-         * from this layer's visible region */
-        visibleRegion.subtractSelf(aboveOpaqueLayers);
-
-        testLayer.second.setVisibleRegion(visibleRegion);
-
-        /* If a layer is not visible, return false */
-        if (visibleRegion.isEmpty())
-            optimized = false;
-
-        /* If this layer is opaque, store the region it covers */
-        if (testLayer.second.getPlaneAlpha() == 1.0f)
-            aboveOpaqueLayers.orSelf(visibleRegion);
-    }
-
-    /* If the opaque region does not cover the entire display return false */
-    if (!aboveOpaqueLayers.isRect())
-        return false;
-
-    const auto rect = aboveOpaqueLayers.begin();
-    if (rect->left != 0 || rect->top != 0 || rect->right != mDisplayArea.width
-            || rect->bottom != mDisplayArea.height)
-        return false;
-
-    return optimized;
-}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.h b/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.h
deleted file mode 100644
index 909dd48..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2016 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 _HWC2_TEST_LAYERS_H
-#define _HWC2_TEST_LAYERS_H
-
-#include <map>
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-#include "Hwc2TestProperties.h"
-#include "Hwc2TestLayer.h"
-
-class Hwc2TestLayers {
-public:
-    Hwc2TestLayers(const std::vector<hwc2_layer_t>& layers,
-            Hwc2TestCoverage coverage, const Area& displayArea);
-
-    Hwc2TestLayers(const std::vector<hwc2_layer_t>& layers,
-            Hwc2TestCoverage coverage, const Area& displayArea,
-            const std::unordered_map<Hwc2TestPropertyName,
-            Hwc2TestCoverage>& coverageExceptions);
-
-    std::string dump() const;
-
-    void reset();
-
-    bool advance();
-    bool advanceVisibleRegions();
-
-    /* Test cases with multiple layers and property values can take quite some
-     * time to run. A significant amount of time can be spent on test cases
-     * where one layer is changing property values but is not visible. To
-     * decrease runtime, this function can be called. Removes layouts where a
-     * layer is completely blocked from view. It also removes layouts that do
-     * not cover the entire display.*/
-    bool optimizeLayouts();
-
-    bool contains(hwc2_layer_t layer) const;
-
-    int  getBuffer(hwc2_layer_t layer, buffer_handle_t* outHandle,
-            int32_t* outAcquireFence);
-
-    hwc2_blend_mode_t      getBlendMode(hwc2_layer_t layer) const;
-    Area                   getBufferArea(hwc2_layer_t layer) const;
-    hwc_color_t            getColor(hwc2_layer_t layer) const;
-    hwc2_composition_t     getComposition(hwc2_layer_t layer) const;
-    hwc_rect_t             getCursorPosition(hwc2_layer_t layer) const;
-    android::ui::Dataspace     getDataspace(hwc2_layer_t layer) const;
-    hwc_rect_t             getDisplayFrame(hwc2_layer_t layer) const;
-    android_pixel_format_t getFormat(hwc2_layer_t layer) const;
-    float                  getPlaneAlpha(hwc2_layer_t layer) const;
-    hwc_frect_t            getSourceCrop(hwc2_layer_t layer) const;
-    hwc_region_t           getSurfaceDamage(hwc2_layer_t layer) const;
-    hwc_transform_t        getTransform(hwc2_layer_t layer) const;
-    hwc_region_t           getVisibleRegion(hwc2_layer_t layer) const;
-    uint32_t               getZOrder(hwc2_layer_t layer) const;
-
-private:
-    bool setVisibleRegions();
-
-    std::map<hwc2_layer_t, Hwc2TestLayer> mTestLayers;
-
-    Area mDisplayArea;
-
-    bool mOptimize = false;
-};
-
-#endif /* ifndef _HWC2_TEST_LAYERS_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.cpp
deleted file mode 100644
index 904b927..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2017 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 <sstream>
-#include <android/hardware/graphics/common/1.0/types.h>
-
-#include "Hwc2TestPixelComparator.h"
-
-using android::hardware::graphics::common::V1_0::BufferUsage;
-
-uint32_t ComparatorResult::getPixel(int32_t x, int32_t y, uint32_t stride,
-        uint8_t* img) const
-{
-    uint32_t r = img[(y * stride + x) * 4 + 0];
-    uint32_t g = img[(y * stride + x) * 4 + 1];
-    uint32_t b = img[(y * stride + x) * 4 + 2];
-    uint32_t a = img[(y * stride + x) * 4 + 3];
-
-    uint32_t pixel = 0;
-    pixel |= r;
-    pixel |= g << 8;
-    pixel |= b << 16;
-    pixel |= a << 24;
-    return pixel;
-}
-
-void ComparatorResult::CompareBuffers(
-        android::sp<android::GraphicBuffer>& resultBuffer,
-        android::sp<android::GraphicBuffer>& expectedBuffer)
-{
-    uint8_t* resultBufferImg;
-    uint8_t* expectedBufferImg;
-    resultBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_READ_OFTEN),
-            (void**)(&resultBufferImg));
-
-    expectedBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_READ_OFTEN),
-            (void**)(&expectedBufferImg));
-    mComparisons.clear();
-    int32_t mDifferentPixelCount = 0;
-    int32_t mBlankPixelCount = 0;
-
-    for (uint32_t y = 0; y < resultBuffer->getHeight(); y++) {
-        for (uint32_t x = 0; x < resultBuffer->getWidth(); x++) {
-            uint32_t result = getPixel(x, y, resultBuffer->getStride(),
-                    resultBufferImg);
-            uint32_t expected = getPixel(x, y, expectedBuffer->getStride(),
-                    expectedBufferImg);
-
-            if (result == 0)
-                mBlankPixelCount++;
-
-            if (result != expected)
-                mDifferentPixelCount++;
-
-            mComparisons.emplace_back(std::make_tuple(x, y, result, expected));
-        }
-    }
-    resultBuffer->unlock();
-    expectedBuffer->unlock();
-}
-
-std::string ComparatorResult::pixelDiff(uint32_t x, uint32_t y,
-        uint32_t resultPixel, uint32_t expectedPixel) const
-{
-    uint32_t resultAlpha = (resultPixel >> 24) & 0xFF;
-    uint32_t resultBlue = (resultPixel >> 16) & 0xFF;
-    uint32_t resultGreen = (resultPixel >> 8) & 0xFF;
-    uint32_t resultRed = resultPixel & 0xFF;
-
-    uint32_t expectedAlpha = (expectedPixel >> 24) & 0xFF;
-    uint32_t expectedBlue = (expectedPixel >> 16) & 0xFF;
-    uint32_t expectedGreen = (expectedPixel >> 8) & 0xFF;
-    uint32_t expectedRed = expectedPixel & 0xFF;
-
-    std::ostringstream stream;
-
-    stream << "x: " << x << " y: " << y << std::endl;
-    stream << std::hex;
-    stream << "Result pixel:   " << resultRed << "|" << resultGreen << "|"
-           << resultBlue << "|" << resultAlpha << std::endl;
-
-    stream << "Expected pixel: " << expectedRed << "|" << expectedGreen << "|"
-           << expectedBlue << "|" << expectedAlpha << std::endl;
-
-    return stream.str();
-}
-
-std::string ComparatorResult::dumpComparison() const
-{
-    std::ostringstream stream;
-    stream << "Number of different pixels: " << mDifferentPixelCount;
-
-    for (const auto& comparison : mComparisons) {
-        if (std::get<2>(comparison) != std::get<3>(comparison))
-            stream << pixelDiff(std::get<0>(comparison),
-                    std::get<1>(comparison), std::get<2>(comparison),
-                    std::get<3>(comparison));
-    }
-    return stream.str();
-}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.h b/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.h
deleted file mode 100644
index 55fa936..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2017 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 _HWC2_TEST_PIXEL_COMPARATOR_H
-#define _HWC2_TEST_PIXEL_COMPARATOR_H
-
-#include <ui/GraphicBuffer.h>
-#include <cstdint>
-#include <string>
-#include <utility>
-#include <vector>
-
-class ComparatorResult {
-public:
-    static ComparatorResult& get()
-    {
-        static ComparatorResult instance;
-        return instance;
-    }
-
-    void CompareBuffers(android::sp<android::GraphicBuffer>& resultBuffer,
-            android::sp<android::GraphicBuffer>& expectedBuffer);
-
-    std::string dumpComparison() const;
-
-    ComparatorResult(const ComparatorResult&) = delete;
-    ComparatorResult(ComparatorResult&&) = delete;
-    ComparatorResult& operator=(ComparatorResult const&) = delete;
-    ComparatorResult& operator=(ComparatorResult&&) = delete;
-
-    int32_t getDifferentPixelCount() const { return mDifferentPixelCount; }
-    int32_t getBlankPixelCount() const { return mBlankPixelCount; }
-
-private:
-    ComparatorResult() = default;
-    uint32_t getPixel(int32_t x, int32_t y, uint32_t stride, uint8_t* img) const;
-    std::string pixelDiff(uint32_t x, uint32_t y, uint32_t resultPixel,
-            uint32_t expectedPixel) const;
-
-    int32_t mDifferentPixelCount;
-    int32_t mBlankPixelCount;
-    /* std::tuple<X coordinate, Y coordinate, resultPixel, expectedPixel> */
-    std::vector<std::tuple<uint32_t, uint32_t, uint32_t, uint32_t>>
-            mComparisons;
-};
-
-#endif /* ifndef _HWC2_TEST_PIXEL_COMPARATOR_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp
deleted file mode 100644
index c5b92d0..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp
+++ /dev/null
@@ -1,782 +0,0 @@
-/*
- * Copyright (C) 2016 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 <sstream>
-#include <cutils/log.h>
-#include <ui/Rect.h>
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-#include "Hwc2TestBuffer.h"
-#include "Hwc2TestProperties.h"
-
-Hwc2TestBufferArea::Hwc2TestBufferArea(Hwc2TestCoverage coverage,
-        const Area& displayArea)
-    : Hwc2TestProperty(mBufferAreas, mCompositionSupport),
-      mScalars((coverage == Hwc2TestCoverage::Complete)? mCompleteScalars:
-            (coverage == Hwc2TestCoverage::Basic)? mBasicScalars:
-            mDefaultScalars),
-      mDisplayArea(displayArea)
-{
-    update();
-}
-
-std::string Hwc2TestBufferArea::dump() const
-{
-    std::stringstream dmp;
-    const Area& curr = get();
-    dmp << "\tbuffer area: width " << curr.width << ", height " << curr.height
-            << "\n";
-    return dmp.str();
-}
-
-void Hwc2TestBufferArea::setDependent(Hwc2TestBuffer* buffer)
-{
-    mBuffer = buffer;
-    if (buffer) {
-        buffer->updateBufferArea(get());
-    }
-}
-
-void Hwc2TestBufferArea::setDependent(Hwc2TestSourceCrop* sourceCrop)
-{
-    mSourceCrop = sourceCrop;
-    if (mSourceCrop) {
-        mSourceCrop->updateBufferArea(get());
-    }
-}
-
-void Hwc2TestBufferArea::setDependent(Hwc2TestSurfaceDamage* surfaceDamage)
-{
-    mSurfaceDamage = surfaceDamage;
-    if (mSurfaceDamage) {
-        mSurfaceDamage->updateBufferArea(get());
-    }
-}
-
-void Hwc2TestBufferArea::update()
-{
-    mBufferAreas.clear();
-
-    if (mDisplayArea.width == 0 && mDisplayArea.height == 0) {
-        mBufferAreas.push_back({0, 0});
-        return;
-    }
-
-    for (auto scalar : mScalars) {
-        mBufferAreas.push_back({static_cast<int32_t>(scalar * mDisplayArea.width),
-                static_cast<int32_t>(scalar * mDisplayArea.height)});
-    }
-
-    updateDependents();
-}
-
-void Hwc2TestBufferArea::updateDependents()
-{
-    const Area& curr = get();
-
-    if (mBuffer)
-        mBuffer->updateBufferArea(curr);
-    if (mSourceCrop)
-        mSourceCrop->updateBufferArea(curr);
-    if (mSurfaceDamage)
-        mSurfaceDamage->updateBufferArea(curr);
-}
-
-const std::vector<float> Hwc2TestBufferArea::mDefaultScalars = {
-    1.0f,
-};
-
-const std::vector<float> Hwc2TestBufferArea::mBasicScalars = {
-    1.0f, 0.5f,
-};
-
-const std::vector<float> Hwc2TestBufferArea::mCompleteScalars = {
-    1.0f, 0.75f, 0.5f
-};
-
-
-Hwc2TestBlendMode::Hwc2TestBlendMode(Hwc2TestCoverage coverage)
-    : Hwc2TestProperty(coverage, mCompleteBlendModes, mBasicBlendModes,
-            mDefaultBlendModes, mCompositionSupport) { }
-
-std::string Hwc2TestBlendMode::dump() const
-{
-    std::stringstream dmp;
-    dmp << "\tblend mode: " << getBlendModeName(get()) << "\n";
-    return dmp.str();
-}
-
-void Hwc2TestBlendMode::setDependent(Hwc2TestColor* color)
-{
-    mColor = color;
-    updateDependents();
-}
-
-void Hwc2TestBlendMode::updateDependents()
-{
-    if (mColor)
-        mColor->updateBlendMode(get());
-}
-
-const std::vector<hwc2_blend_mode_t> Hwc2TestBlendMode::mDefaultBlendModes = {
-    HWC2_BLEND_MODE_NONE,
-};
-
-const std::vector<hwc2_blend_mode_t> Hwc2TestBlendMode::mBasicBlendModes = {
-    HWC2_BLEND_MODE_NONE,
-    HWC2_BLEND_MODE_PREMULTIPLIED,
-};
-
-const std::vector<hwc2_blend_mode_t> Hwc2TestBlendMode::mCompleteBlendModes = {
-    HWC2_BLEND_MODE_NONE,
-    HWC2_BLEND_MODE_PREMULTIPLIED,
-    HWC2_BLEND_MODE_COVERAGE,
-};
-
-
-Hwc2TestColor::Hwc2TestColor(Hwc2TestCoverage coverage,
-        hwc2_blend_mode_t blendMode)
-    : Hwc2TestProperty(mColors, mCompositionSupport),
-      mBaseColors((coverage == Hwc2TestCoverage::Complete)? mCompleteBaseColors:
-            (coverage == Hwc2TestCoverage::Basic)? mBasicBaseColors:
-            mDefaultBaseColors),
-      mBlendMode(blendMode)
-{
-    update();
-}
-
-std::string Hwc2TestColor::dump() const
-{
-    std::stringstream dmp;
-    const hwc_color_t& color = get();
-    dmp << "\tcolor: r " << std::to_string(color.r) << ", g "
-            << std::to_string(color.g) << ", b " << std::to_string(color.b)
-            << ", a " << std::to_string(color.a) << "\n";
-    return dmp.str();
-}
-
-void Hwc2TestColor::updateBlendMode(hwc2_blend_mode_t blendMode)
-{
-    mBlendMode = blendMode;
-    update();
-}
-
-void Hwc2TestColor::update()
-{
-    if (mBlendMode != HWC2_BLEND_MODE_PREMULTIPLIED) {
-        mColors = mBaseColors;
-        return;
-    }
-
-    mColors.clear();
-
-    for (const hwc_color_t& baseColor : mBaseColors) {
-        if (baseColor.a >= baseColor.r && baseColor.a >= baseColor.g
-                && baseColor.a >= baseColor.b) {
-            mColors.push_back(baseColor);
-        }
-    }
-
-}
-
-const std::vector<hwc_color_t> Hwc2TestColor::mDefaultBaseColors = {
-    {UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX},
-};
-
-const std::vector<hwc_color_t> Hwc2TestColor::mBasicBaseColors = {
-    {UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX},
-    {        0,         0,         0,         0},
-};
-
-const std::vector<hwc_color_t> Hwc2TestColor::mCompleteBaseColors = {
-    {UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX},
-    {UINT8_MAX, UINT8_MAX, UINT8_MAX,         0},
-    {UINT8_MAX, UINT8_MAX,         0, UINT8_MAX},
-    {UINT8_MAX, UINT8_MAX,         0,         0},
-    {UINT8_MAX,         0, UINT8_MAX, UINT8_MAX},
-    {UINT8_MAX,         0, UINT8_MAX,         0},
-    {UINT8_MAX,         0,         0, UINT8_MAX},
-    {UINT8_MAX,         0,         0,         0},
-    {        0, UINT8_MAX, UINT8_MAX, UINT8_MAX},
-    {        0, UINT8_MAX, UINT8_MAX,         0},
-    {        0, UINT8_MAX,         0, UINT8_MAX},
-    {        0, UINT8_MAX,         0,         0},
-    {        0,         0, UINT8_MAX, UINT8_MAX},
-    {        0,         0, UINT8_MAX,         0},
-    {        0,         0,         0, UINT8_MAX},
-    {        0,         0,         0,         0},
-};
-
-
-Hwc2TestComposition::Hwc2TestComposition(Hwc2TestCoverage coverage)
-    : Hwc2TestProperty(coverage, mCompleteCompositions, mBasicCompositions,
-            mDefaultCompositions, mCompositionSupport) { }
-
-std::string Hwc2TestComposition::dump() const
-{
-    std::stringstream dmp;
-    dmp << "\tcomposition: " << getCompositionName(get()) << "\n";
-    return dmp.str();
-}
-
-const std::vector<hwc2_composition_t> Hwc2TestComposition::mDefaultCompositions = {
-    HWC2_COMPOSITION_DEVICE,
-};
-
-const std::vector<hwc2_composition_t> Hwc2TestComposition::mBasicCompositions = {
-    HWC2_COMPOSITION_CLIENT,
-    HWC2_COMPOSITION_DEVICE,
-};
-
-const std::vector<hwc2_composition_t> Hwc2TestComposition::mCompleteCompositions = {
-    HWC2_COMPOSITION_CLIENT,
-    HWC2_COMPOSITION_DEVICE,
-    HWC2_COMPOSITION_SOLID_COLOR,
-    HWC2_COMPOSITION_CURSOR,
-    HWC2_COMPOSITION_SIDEBAND,
-};
-
-
-Hwc2TestDataspace::Hwc2TestDataspace(Hwc2TestCoverage coverage)
-    : Hwc2TestProperty(coverage, completeDataspaces, basicDataspaces,
-            defaultDataspaces, mCompositionSupport) { }
-
-std::string Hwc2TestDataspace::dump() const
-{
-    std::stringstream dmp;
-    dmp << "\tdataspace: " << static_cast<int32_t>(get()) << "\n";
-    return dmp.str();
-}
-
-const std::vector<android::ui::Dataspace> Hwc2TestDataspace::defaultDataspaces = {
-    android::ui::Dataspace::UNKNOWN,
-};
-
-const std::vector<android::ui::Dataspace> Hwc2TestDataspace::basicDataspaces = {
-    android::ui::Dataspace::UNKNOWN,
-    android::ui::Dataspace::V0_SRGB,
-};
-
-const std::vector<android::ui::Dataspace> Hwc2TestDataspace::completeDataspaces = {
-    android::ui::Dataspace::UNKNOWN,
-    android::ui::Dataspace::ARBITRARY,
-    android::ui::Dataspace::STANDARD_SHIFT,
-    android::ui::Dataspace::STANDARD_MASK,
-    android::ui::Dataspace::STANDARD_UNSPECIFIED,
-    android::ui::Dataspace::STANDARD_BT709,
-    android::ui::Dataspace::STANDARD_BT601_625,
-    android::ui::Dataspace::STANDARD_BT601_625_UNADJUSTED,
-    android::ui::Dataspace::STANDARD_BT601_525,
-    android::ui::Dataspace::STANDARD_BT601_525_UNADJUSTED,
-    android::ui::Dataspace::STANDARD_BT2020,
-    android::ui::Dataspace::STANDARD_BT2020_CONSTANT_LUMINANCE,
-    android::ui::Dataspace::STANDARD_BT470M,
-    android::ui::Dataspace::STANDARD_FILM,
-    android::ui::Dataspace::TRANSFER_SHIFT,
-    android::ui::Dataspace::TRANSFER_MASK,
-    android::ui::Dataspace::TRANSFER_UNSPECIFIED,
-    android::ui::Dataspace::TRANSFER_LINEAR,
-    android::ui::Dataspace::TRANSFER_SRGB,
-    android::ui::Dataspace::TRANSFER_SMPTE_170M,
-    android::ui::Dataspace::TRANSFER_GAMMA2_2,
-    android::ui::Dataspace::TRANSFER_GAMMA2_8,
-    android::ui::Dataspace::TRANSFER_ST2084,
-    android::ui::Dataspace::TRANSFER_HLG,
-    android::ui::Dataspace::RANGE_SHIFT,
-    android::ui::Dataspace::RANGE_MASK,
-    android::ui::Dataspace::RANGE_UNSPECIFIED,
-    android::ui::Dataspace::RANGE_FULL,
-    android::ui::Dataspace::RANGE_LIMITED,
-    android::ui::Dataspace::SRGB_LINEAR,
-    android::ui::Dataspace::V0_SRGB_LINEAR,
-    android::ui::Dataspace::SRGB,
-    android::ui::Dataspace::V0_SRGB,
-    android::ui::Dataspace::JFIF,
-    android::ui::Dataspace::V0_JFIF,
-    android::ui::Dataspace::BT601_625,
-    android::ui::Dataspace::V0_BT601_625,
-    android::ui::Dataspace::BT601_525,
-    android::ui::Dataspace::V0_BT601_525,
-    android::ui::Dataspace::BT709,
-    android::ui::Dataspace::V0_BT709,
-    android::ui::Dataspace::DEPTH,
-};
-
-
-Hwc2TestDisplayDimension::Hwc2TestDisplayDimension(Hwc2TestCoverage coverage)
-    : Hwc2TestProperty(
-            (coverage == Hwc2TestCoverage::Complete)? mCompleteDisplayDimensions:
-            (coverage == Hwc2TestCoverage::Basic)? mBasicDisplayDimensions:
-            mDefaultDisplayDimensions, mCompositionSupport) { }
-
-std::string Hwc2TestDisplayDimension::dump() const
-{
-    std::stringstream dmp;
-    const UnsignedArea& curr = get();
-    dmp << "\tdisplay dimension: " << curr.width<< " x " << curr.height<< "\n";
-    return dmp.str();
-}
-
-void Hwc2TestDisplayDimension::setDependent(Hwc2TestVirtualBuffer* buffer)
-{
-    mBuffers.insert(buffer);
-    updateDependents();
-}
-
-void Hwc2TestDisplayDimension::updateDependents()
-{
-    const UnsignedArea& curr = get();
-
-    for (Hwc2TestVirtualBuffer* buffer : mBuffers)
-        buffer->updateBufferArea({static_cast<int32_t>(curr.width),
-                static_cast<int32_t>(curr.height)});
-}
-
-const std::vector<UnsignedArea>
-        Hwc2TestDisplayDimension::mDefaultDisplayDimensions = {
-    {1920, 1080},
-};
-
-const std::vector<UnsignedArea>
-        Hwc2TestDisplayDimension::mBasicDisplayDimensions = {
-    {640, 480},
-    {1280, 720},
-    {1920, 1080},
-    {1920, 1200},
-};
-
-const std::vector<UnsignedArea>
-        Hwc2TestDisplayDimension::mCompleteDisplayDimensions = {
-    {320, 240},
-    {480, 320},
-    {640, 480},
-    {1280, 720},
-    {1920, 1080},
-    {1920, 1200},
-    {2560, 1440},
-    {2560, 1600},
-    {3840, 2160},
-    {4096, 2160},
-};
-
-
-Hwc2TestDisplayFrame::Hwc2TestDisplayFrame(Hwc2TestCoverage coverage,
-        const Area& displayArea)
-    : Hwc2TestProperty(mDisplayFrames, mCompositionSupport),
-      mFrectScalars((coverage == Hwc2TestCoverage::Complete)? mCompleteFrectScalars:
-            (coverage == Hwc2TestCoverage::Basic)? mBasicFrectScalars:
-            mDefaultFrectScalars),
-      mDisplayArea(displayArea)
-{
-    update();
-}
-
-std::string Hwc2TestDisplayFrame::dump() const
-{
-    std::stringstream dmp;
-    const hwc_rect_t& displayFrame = get();
-    dmp << "\tdisplay frame: left " << displayFrame.left << ", top "
-            << displayFrame.top << ", right " << displayFrame.right
-            << ", bottom " << displayFrame.bottom << "\n";
-    return dmp.str();
-}
-
-void Hwc2TestDisplayFrame::update()
-{
-    mDisplayFrames.clear();
-
-    if (mDisplayArea.width == 0 && mDisplayArea.height == 0) {
-        mDisplayFrames.push_back({0, 0, 0, 0});
-        return;
-    }
-
-    for (const auto& frectScalar : mFrectScalars) {
-        mDisplayFrames.push_back({
-                static_cast<int>(frectScalar.left * mDisplayArea.width),
-                static_cast<int>(frectScalar.top * mDisplayArea.height),
-                static_cast<int>(frectScalar.right * mDisplayArea.width),
-                static_cast<int>(frectScalar.bottom * mDisplayArea.height)});
-    }
-}
-
-const std::vector<hwc_frect_t> Hwc2TestDisplayFrame::mDefaultFrectScalars = {
-    {0.0, 0.0, 1.0, 1.0},
-};
-
-const std::vector<hwc_frect_t> Hwc2TestDisplayFrame::mBasicFrectScalars = {
-    {0.0, 0.0, 1.0, 1.0},
-    {0.0, 0.0, 1.0, 0.05},
-    {0.0, 0.95, 1.0, 1.0},
-};
-
-const std::vector<hwc_frect_t> Hwc2TestDisplayFrame::mCompleteFrectScalars = {
-    {0.0, 0.0, 1.0, 1.0},
-    {0.0, 0.05, 1.0, 0.95},
-    {0.0, 0.05, 1.0, 1.0},
-    {0.0, 0.0, 1.0, 0.05},
-    {0.0, 0.95, 1.0, 1.0},
-    {0.25, 0.0, 0.75, 0.35},
-    {0.25, 0.25, 0.75, 0.75},
-};
-
-
-Hwc2TestPlaneAlpha::Hwc2TestPlaneAlpha(Hwc2TestCoverage coverage)
-    : Hwc2TestProperty(coverage, mCompletePlaneAlphas, mBasicPlaneAlphas,
-            mDefaultPlaneAlphas, mCompositionSupport) { }
-
-std::string Hwc2TestPlaneAlpha::dump() const
-{
-    std::stringstream dmp;
-    dmp << "\tplane alpha: " << get() << "\n";
-    return dmp.str();
-}
-
-const std::vector<float> Hwc2TestPlaneAlpha::mDefaultPlaneAlphas = {
-    1.0f,
-};
-
-const std::vector<float> Hwc2TestPlaneAlpha::mBasicPlaneAlphas = {
-    1.0f, 0.0f,
-};
-
-const std::vector<float> Hwc2TestPlaneAlpha::mCompletePlaneAlphas = {
-    1.0f, 0.75f, 0.5f, 0.25f, 0.0f,
-};
-
-
-Hwc2TestSourceCrop::Hwc2TestSourceCrop(Hwc2TestCoverage coverage,
-        const Area& bufferArea)
-    : Hwc2TestProperty(mSourceCrops, mCompositionSupport),
-      mFrectScalars((coverage == Hwc2TestCoverage::Complete)? mCompleteFrectScalars:
-            (coverage == Hwc2TestCoverage::Basic)? mBasicFrectScalars:
-            mDefaultFrectScalars),
-      mBufferArea(bufferArea)
-{
-    update();
-}
-
-std::string Hwc2TestSourceCrop::dump() const
-{
-    std::stringstream dmp;
-    const hwc_frect_t& sourceCrop = get();
-    dmp << "\tsource crop: left " << sourceCrop.left << ", top "
-            << sourceCrop.top << ", right " << sourceCrop.right << ", bottom "
-            << sourceCrop.bottom << "\n";
-    return dmp.str();
-}
-
-void Hwc2TestSourceCrop::updateBufferArea(const Area& bufferArea)
-{
-    mBufferArea = bufferArea;
-    update();
-}
-
-void Hwc2TestSourceCrop::update()
-{
-    mSourceCrops.clear();
-
-    if (mBufferArea.width == 0 && mBufferArea.height == 0) {
-        mSourceCrops.push_back({0, 0, 0, 0});
-        return;
-    }
-
-    for (const auto& frectScalar : mFrectScalars) {
-        mSourceCrops.push_back({
-                frectScalar.left * mBufferArea.width,
-                frectScalar.top * mBufferArea.height,
-                frectScalar.right * mBufferArea.width,
-                frectScalar.bottom * mBufferArea.height});
-    }
-}
-
-const std::vector<hwc_frect_t> Hwc2TestSourceCrop::mDefaultFrectScalars = {
-    {0.0, 0.0, 1.0, 1.0},
-};
-
-const std::vector<hwc_frect_t> Hwc2TestSourceCrop::mBasicFrectScalars = {
-    {0.0, 0.0, 1.0, 1.0},
-    {0.0, 0.0, 0.5, 0.5},
-    {0.5, 0.5, 1.0, 1.0},
-};
-
-const std::vector<hwc_frect_t> Hwc2TestSourceCrop::mCompleteFrectScalars = {
-    {0.0, 0.0, 1.0, 1.0},
-    {0.0, 0.0, 0.5, 0.5},
-    {0.5, 0.5, 1.0, 1.0},
-    {0.0, 0.0, 0.25, 0.25},
-    {0.25, 0.25, 0.75, 0.75},
-};
-
-
-Hwc2TestSurfaceDamage::Hwc2TestSurfaceDamage(Hwc2TestCoverage coverage)
-    : Hwc2TestProperty(mSurfaceDamages, mCompositionSupport),
-      mRegionScalars((coverage == Hwc2TestCoverage::Complete)? mCompleteRegionScalars:
-            (coverage == Hwc2TestCoverage::Basic)? mBasicRegionScalars:
-            mDefaultRegionScalars)
-{
-    update();
-}
-
-Hwc2TestSurfaceDamage::~Hwc2TestSurfaceDamage()
-{
-    freeSurfaceDamages();
-}
-
-std::string Hwc2TestSurfaceDamage::dump() const
-{
-    std::stringstream dmp;
-
-    const hwc_region_t& curr = get();
-    dmp << "\tsurface damage: region count " << curr.numRects << "\n";
-    for (size_t i = 0; i < curr.numRects; i++) {
-        const hwc_rect_t& rect = curr.rects[i];
-        dmp << "\t\trect: left " << rect.left << ", top " << rect.top
-                << ", right " << rect.right << ", bottom " << rect.bottom << "\n";
-    }
-
-    return dmp.str();
-}
-
-void Hwc2TestSurfaceDamage::updateBufferArea(const Area& bufferArea)
-{
-    mBufferArea = bufferArea;
-    update();
-}
-
-void Hwc2TestSurfaceDamage::update()
-{
-    freeSurfaceDamages();
-
-    if (mBufferArea.width == 0 && mBufferArea.height == 0) {
-        mSurfaceDamages.push_back({0, nullptr});
-        return;
-    }
-
-    hwc_region_t damage;
-
-    for (const auto& regionScalar : mRegionScalars) {
-        damage.numRects = regionScalar.size();
-
-        if (damage.numRects > 0) {
-            hwc_rect_t* rects = new hwc_rect_t[damage.numRects];
-            if (!rects) {
-                ALOGW("failed to allocate new hwc_rect_t array");
-                continue;
-            }
-
-            for (size_t i = 0; i < damage.numRects; i++) {
-                rects[i].left = regionScalar[i].left * mBufferArea.width;
-                rects[i].top = regionScalar[i].top * mBufferArea.height;
-                rects[i].right = regionScalar[i].right * mBufferArea.width;
-                rects[i].bottom = regionScalar[i].bottom * mBufferArea.height;
-            }
-
-            damage.rects = static_cast<hwc_rect_t const*>(rects);
-        } else {
-            damage.rects = nullptr;
-        }
-
-        mSurfaceDamages.push_back(damage);
-    }
-}
-
-void Hwc2TestSurfaceDamage::freeSurfaceDamages()
-{
-    for (const auto& surfaceDamage : mSurfaceDamages) {
-        if (surfaceDamage.numRects > 0 && surfaceDamage.rects)
-            delete[] surfaceDamage.rects;
-    }
-    mSurfaceDamages.clear();
-}
-
-const std::vector<std::vector<hwc_frect_t>> Hwc2TestSurfaceDamage::mDefaultRegionScalars = {
-    {{}},
-};
-
-const std::vector<std::vector<hwc_frect_t>> Hwc2TestSurfaceDamage::mBasicRegionScalars = {
-    {{}},
-    {{0.0, 0.0, 1.0, 1.0}},
-};
-
-const std::vector<std::vector<hwc_frect_t>> Hwc2TestSurfaceDamage::mCompleteRegionScalars = {
-    {{}},
-    {{0.0, 0.0, 1.0, 1.0}},
-    {{0.0, 0.0, 0.5, 0.5}, {0.5, 0.5, 1.0, 1.0}},
-};
-
-
-Hwc2TestTransform::Hwc2TestTransform(Hwc2TestCoverage coverage)
-    : Hwc2TestProperty(coverage, mCompleteTransforms, mBasicTransforms,
-            mDefaultTransforms, mCompositionSupport) { }
-
-std::string Hwc2TestTransform::dump() const
-{
-    std::stringstream dmp;
-    dmp << "\ttransform: " << getTransformName(get()) << "\n";
-    return dmp.str();
-}
-
-const std::vector<hwc_transform_t> Hwc2TestTransform::mDefaultTransforms = {
-    static_cast<hwc_transform_t>(0),
-};
-
-const std::vector<hwc_transform_t> Hwc2TestTransform::mBasicTransforms = {
-    static_cast<hwc_transform_t>(0),
-    HWC_TRANSFORM_FLIP_H,
-    HWC_TRANSFORM_FLIP_V,
-    HWC_TRANSFORM_ROT_90,
-};
-
-const std::vector<hwc_transform_t> Hwc2TestTransform::mCompleteTransforms = {
-    static_cast<hwc_transform_t>(0),
-    HWC_TRANSFORM_FLIP_H,
-    HWC_TRANSFORM_FLIP_V,
-    HWC_TRANSFORM_ROT_90,
-    HWC_TRANSFORM_ROT_180,
-    HWC_TRANSFORM_ROT_270,
-    HWC_TRANSFORM_FLIP_H_ROT_90,
-    HWC_TRANSFORM_FLIP_V_ROT_90,
-};
-
-
-Hwc2TestVisibleRegion::~Hwc2TestVisibleRegion()
-{
-    release();
-}
-
-std::string Hwc2TestVisibleRegion::dump() const
-{
-    std::stringstream dmp;
-
-    const hwc_region_t& curr = get();
-    dmp << "\tvisible region: region count " << curr.numRects << "\n";
-    for (size_t i = 0; i < curr.numRects; i++) {
-        const hwc_rect_t& rect = curr.rects[i];
-        dmp << "\t\trect: left " << rect.left << ", top " << rect.top
-                << ", right " << rect.right << ", bottom " << rect.bottom << "\n";
-    }
-
-    return dmp.str();
-}
-
-void Hwc2TestVisibleRegion::set(const android::Region& visibleRegion)
-{
-    release();
-
-    size_t size = 0;
-    const android::Rect* rects = visibleRegion.getArray(&size);
-
-    mVisibleRegion.numRects = size;
-    mVisibleRegion.rects = nullptr;
-
-    if (size > 0) {
-        hwc_rect_t* hwcRects = new hwc_rect_t[size];
-        for (size_t i = 0; i < size; i++) {
-            hwcRects[i].left = rects[i].left;
-            hwcRects[i].top = rects[i].top;
-            hwcRects[i].right = rects[i].right;
-            hwcRects[i].bottom = rects[i].bottom;
-        }
-        mVisibleRegion.rects = hwcRects;
-    }
-}
-
-hwc_region_t Hwc2TestVisibleRegion::get() const
-{
-    return mVisibleRegion;
-}
-
-void Hwc2TestVisibleRegion::release()
-{
-    if (mVisibleRegion.numRects > 0 && mVisibleRegion.rects)
-        delete[] mVisibleRegion.rects;
-    mVisibleRegion.rects = nullptr;
-    mVisibleRegion.numRects = 0;
-}
-
-/* Identifies which layer properties are supported by each composition type.
- * hwc2_composition_t values range from:
- *  HWC2_COMPOSITION_INVALID = 0,
- *  HWC2_COMPOSITION_CLIENT = 1,
- *  HWC2_COMPOSITION_DEVICE = 2,
- *  HWC2_COMPOSITION_SOLID_COLOR = 3,
- *  HWC2_COMPOSITION_CURSOR = 4,
- *  HWC2_COMPOSITION_SIDEBAND = 5,
- *
- * Each property array can be indexed by a hwc2_composition_t value.
- * By using an array instead of a more complex data structure, runtimes for
- * some test cases showed a noticeable improvement.
- */
-
-/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
-const std::array<bool, 6> Hwc2TestBufferArea::mCompositionSupport = {{
-    false,   true,    true,    false,   true,    true,
-}};
-
-/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
-const std::array<bool, 6> Hwc2TestBlendMode::mCompositionSupport = {{
-    false,   true,    true,    false,   true,    true,
-}};
-
-/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
-const std::array<bool, 6> Hwc2TestColor::mCompositionSupport = {{
-    false,   false,   false,   true,    false,   false,
-}};
-
-/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
-const std::array<bool, 6> Hwc2TestComposition::mCompositionSupport = {{
-    false,   true,    true,    true,    true,    true,
-}};
-
-/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
-const std::array<bool, 6> Hwc2TestDataspace::mCompositionSupport = {{
-    false,   true,    true,    true,    true,    false,
-}};
-
-/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
-const std::array<bool, 6> Hwc2TestDisplayDimension::mCompositionSupport = {{
-    false,   true,    true,    true,    true,    true,
-}};
-
-/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
-const std::array<bool, 6> Hwc2TestDisplayFrame::mCompositionSupport = {{
-    false,   true,    true,    true,    false,   true,
-}};
-
-/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
-const std::array<bool, 6> Hwc2TestPlaneAlpha::mCompositionSupport = {{
-    false,   true,    true,    true,    true,    true,
-}};
-
-/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
-const std::array<bool, 6> Hwc2TestSourceCrop::mCompositionSupport = {{
-    false,   true,    true,    false,   true,    false,
-}};
-
-/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
-const std::array<bool, 6> Hwc2TestSurfaceDamage::mCompositionSupport = {{
-    false,   false,   true,    false,   true,    false,
-}};
-
-/*  INVALID  CLIENT   DEVICE   COLOR    CURSOR   SIDEBAND */
-const std::array<bool, 6> Hwc2TestTransform::mCompositionSupport = {{
-    false,   true,    true,    false,   true,    true,
-}};
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h
deleted file mode 100644
index 06ae314..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * Copyright (C) 2016 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 _HWC2_TEST_PROPERTIES_H
-#define _HWC2_TEST_PROPERTIES_H
-
-#include <array>
-#include <vector>
-
-#include <ui/GraphicTypes.h>
-#include <ui/Region.h>
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-enum class Hwc2TestCoverage {
-    Default = 0,
-    Basic,
-    Complete,
-};
-
-enum class Hwc2TestPropertyName {
-    BlendMode = 1,
-    BufferArea,
-    Color,
-    Composition,
-    CursorPosition,
-    Dataspace,
-    DisplayFrame,
-    PlaneAlpha,
-    SourceCrop,
-    SurfaceDamage,
-    Transform,
-};
-
-typedef struct {
-    int32_t width;
-    int32_t height;
-} Area;
-
-
-typedef struct {
-    uint32_t width;
-    uint32_t height;
-} UnsignedArea;
-
-
-class Hwc2TestContainer {
-public:
-    virtual ~Hwc2TestContainer() = default;
-
-    /* Resets the container */
-    virtual void reset() = 0;
-
-    /* Attempts to advance to the next valid value. Returns true if one can be
-     * found */
-    virtual bool advance() = 0;
-
-    virtual std::string dump() const = 0;
-
-    /* Returns true if the container supports the given composition type */
-    virtual bool isSupported(hwc2_composition_t composition) = 0;
-};
-
-
-template <class T>
-class Hwc2TestProperty : public Hwc2TestContainer {
-public:
-    Hwc2TestProperty(Hwc2TestCoverage coverage,
-            const std::vector<T>& completeList, const std::vector<T>& basicList,
-            const std::vector<T>& defaultList,
-            const std::array<bool, 6>& compositionSupport)
-        : Hwc2TestProperty((coverage == Hwc2TestCoverage::Complete)? completeList:
-                (coverage == Hwc2TestCoverage::Basic)? basicList : defaultList,
-                compositionSupport) { }
-
-    Hwc2TestProperty(const std::vector<T>& list,
-            const std::array<bool, 6>& compositionSupport)
-        : mList(list),
-          mCompositionSupport(compositionSupport) { }
-
-    void reset() override
-    {
-        mListIdx = 0;
-    }
-
-    bool advance() override
-    {
-        if (mListIdx + 1 < mList.size()) {
-            mListIdx++;
-            updateDependents();
-            return true;
-        }
-        reset();
-        updateDependents();
-        return false;
-    }
-
-    T get() const
-    {
-        return mList.at(mListIdx);
-    }
-
-    virtual bool isSupported(hwc2_composition_t composition)
-    {
-        return mCompositionSupport.at(composition);
-    }
-
-protected:
-    /* If a derived class has dependents, override this function */
-    virtual void updateDependents() { }
-
-    const std::vector<T>& mList;
-    size_t mListIdx = 0;
-
-    const std::array<bool, 6>& mCompositionSupport;
-};
-
-class Hwc2TestBuffer;
-class Hwc2TestSourceCrop;
-class Hwc2TestSurfaceDamage;
-
-class Hwc2TestBufferArea : public Hwc2TestProperty<Area> {
-public:
-    Hwc2TestBufferArea(Hwc2TestCoverage coverage, const Area& displayArea);
-
-    std::string dump() const override;
-
-    void setDependent(Hwc2TestBuffer* buffer);
-    void setDependent(Hwc2TestSourceCrop* sourceCrop);
-    void setDependent(Hwc2TestSurfaceDamage* surfaceDamage);
-
-protected:
-    void update();
-    void updateDependents() override;
-
-    const std::vector<float>& mScalars;
-    static const std::vector<float> mDefaultScalars;
-    static const std::vector<float> mBasicScalars;
-    static const std::vector<float> mCompleteScalars;
-
-    Area mDisplayArea;
-
-    Hwc2TestBuffer* mBuffer = nullptr;
-    Hwc2TestSourceCrop* mSourceCrop = nullptr;
-    Hwc2TestSurfaceDamage* mSurfaceDamage = nullptr;
-
-    std::vector<Area> mBufferAreas;
-
-    static const std::array<bool, 6> mCompositionSupport;
-};
-
-
-class Hwc2TestColor;
-
-class Hwc2TestBlendMode : public Hwc2TestProperty<hwc2_blend_mode_t> {
-public:
-    explicit Hwc2TestBlendMode(Hwc2TestCoverage coverage);
-
-    std::string dump() const override;
-
-    void setDependent(Hwc2TestColor* color);
-
-protected:
-    void updateDependents() override;
-
-    Hwc2TestColor* mColor = nullptr;
-
-    static const std::vector<hwc2_blend_mode_t> mDefaultBlendModes;
-    static const std::vector<hwc2_blend_mode_t> mBasicBlendModes;
-    static const std::vector<hwc2_blend_mode_t> mCompleteBlendModes;
-
-    static const std::array<bool, 6> mCompositionSupport;
-};
-
-
-class Hwc2TestColor : public Hwc2TestProperty<hwc_color_t> {
-public:
-    explicit Hwc2TestColor(Hwc2TestCoverage coverage,
-                           hwc2_blend_mode_t blendMode = HWC2_BLEND_MODE_NONE);
-
-    std::string dump() const override;
-
-    void updateBlendMode(hwc2_blend_mode_t blendMode);
-
-protected:
-    void update();
-
-    std::vector<hwc_color_t> mBaseColors;
-    static const std::vector<hwc_color_t> mDefaultBaseColors;
-    static const std::vector<hwc_color_t> mBasicBaseColors;
-    static const std::vector<hwc_color_t> mCompleteBaseColors;
-
-    hwc2_blend_mode_t mBlendMode;
-
-    std::vector<hwc_color_t> mColors;
-
-    static const std::array<bool, 6> mCompositionSupport;
-};
-
-
-class Hwc2TestComposition : public Hwc2TestProperty<hwc2_composition_t> {
-public:
-    explicit Hwc2TestComposition(Hwc2TestCoverage coverage);
-
-    std::string dump() const override;
-
-protected:
-    static const std::vector<hwc2_composition_t> mDefaultCompositions;
-    static const std::vector<hwc2_composition_t> mBasicCompositions;
-    static const std::vector<hwc2_composition_t> mCompleteCompositions;
-
-    static const std::array<bool, 6> mCompositionSupport;
-};
-
-
-class Hwc2TestDataspace : public Hwc2TestProperty<android::ui::Dataspace> {
-public:
-    explicit Hwc2TestDataspace(Hwc2TestCoverage coverage);
-
-    std::string dump() const override;
-
-protected:
-    static const std::vector<android::ui::Dataspace> defaultDataspaces;
-    static const std::vector<android::ui::Dataspace> basicDataspaces;
-    static const std::vector<android::ui::Dataspace> completeDataspaces;
-
-    static const std::array<bool, 6> mCompositionSupport;
-};
-
-class Hwc2TestVirtualBuffer;
-
-class Hwc2TestDisplayDimension : public Hwc2TestProperty<UnsignedArea> {
-public:
-    explicit Hwc2TestDisplayDimension(Hwc2TestCoverage coverage);
-
-    std::string dump() const;
-
-    void setDependent(Hwc2TestVirtualBuffer* buffer);
-
-private:
-    void updateDependents();
-
-    std::set<Hwc2TestVirtualBuffer*> mBuffers;
-
-    static const std::vector<UnsignedArea> mDefaultDisplayDimensions;
-    static const std::vector<UnsignedArea> mBasicDisplayDimensions;
-    static const std::vector<UnsignedArea> mCompleteDisplayDimensions;
-
-    static const std::array<bool, 6> mCompositionSupport;
-};
-
-
-class Hwc2TestDisplayFrame : public Hwc2TestProperty<hwc_rect_t> {
-public:
-    Hwc2TestDisplayFrame(Hwc2TestCoverage coverage, const Area& displayArea);
-
-    std::string dump() const override;
-
-protected:
-    void update();
-
-    const std::vector<hwc_frect_t>& mFrectScalars;
-    const static std::vector<hwc_frect_t> mDefaultFrectScalars;
-    const static std::vector<hwc_frect_t> mBasicFrectScalars;
-    const static std::vector<hwc_frect_t> mCompleteFrectScalars;
-
-    Area mDisplayArea;
-
-    std::vector<hwc_rect_t> mDisplayFrames;
-
-    static const std::array<bool, 6> mCompositionSupport;
-};
-
-
-class Hwc2TestPlaneAlpha : public Hwc2TestProperty<float> {
-public:
-    explicit Hwc2TestPlaneAlpha(Hwc2TestCoverage coverage);
-
-    std::string dump() const override;
-
-protected:
-    static const std::vector<float> mDefaultPlaneAlphas;
-    static const std::vector<float> mBasicPlaneAlphas;
-    static const std::vector<float> mCompletePlaneAlphas;
-
-    static const std::array<bool, 6> mCompositionSupport;
-};
-
-
-class Hwc2TestSourceCrop : public Hwc2TestProperty<hwc_frect_t> {
-public:
-    explicit Hwc2TestSourceCrop(Hwc2TestCoverage coverage, const Area& bufferArea = {0, 0});
-
-    std::string dump() const override;
-
-    void updateBufferArea(const Area& bufferArea);
-
-protected:
-    void update();
-
-    const std::vector<hwc_frect_t>& mFrectScalars;
-    const static std::vector<hwc_frect_t> mDefaultFrectScalars;
-    const static std::vector<hwc_frect_t> mBasicFrectScalars;
-    const static std::vector<hwc_frect_t> mCompleteFrectScalars;
-
-    Area mBufferArea;
-
-    std::vector<hwc_frect_t> mSourceCrops;
-
-    static const std::array<bool, 6> mCompositionSupport;
-};
-
-
-class Hwc2TestSurfaceDamage : public Hwc2TestProperty<hwc_region_t> {
-public:
-    explicit Hwc2TestSurfaceDamage(Hwc2TestCoverage coverage);
-    ~Hwc2TestSurfaceDamage();
-
-    std::string dump() const override;
-
-    void updateBufferArea(const Area& bufferArea);
-
-protected:
-    void update();
-    void freeSurfaceDamages();
-
-    const std::vector<std::vector<hwc_frect_t>> &mRegionScalars;
-    const static std::vector<std::vector<hwc_frect_t>> mDefaultRegionScalars;
-    const static std::vector<std::vector<hwc_frect_t>> mBasicRegionScalars;
-    const static std::vector<std::vector<hwc_frect_t>> mCompleteRegionScalars;
-
-    Area mBufferArea = {0, 0};
-
-    std::vector<hwc_region_t> mSurfaceDamages;
-
-    static const std::array<bool, 6> mCompositionSupport;
-};
-
-
-class Hwc2TestTransform : public Hwc2TestProperty<hwc_transform_t> {
-public:
-    explicit Hwc2TestTransform(Hwc2TestCoverage coverage);
-
-    std::string dump() const override;
-
-protected:
-    static const std::vector<hwc_transform_t> mDefaultTransforms;
-    static const std::vector<hwc_transform_t> mBasicTransforms;
-    static const std::vector<hwc_transform_t> mCompleteTransforms;
-
-    static const std::array<bool, 6> mCompositionSupport;
-};
-
-
-class Hwc2TestVisibleRegion {
-public:
-    ~Hwc2TestVisibleRegion();
-
-    std::string dump() const;
-
-    void set(const android::Region& visibleRegion);
-    hwc_region_t get() const;
-    void release();
-
-protected:
-    hwc_region_t mVisibleRegion = {0, nullptr};
-};
-
-#endif /* ifndef _HWC2_TEST_PROPERTIES_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp
deleted file mode 100644
index e6cceb8..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2016 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 <sstream>
-#include <sys/stat.h>
-
-#include "Hwc2TestVirtualDisplay.h"
-
-#define DIR_NAME "images"
-
-Hwc2TestVirtualDisplay::Hwc2TestVirtualDisplay(
-        Hwc2TestCoverage coverage)
-    : mDisplayDimension(coverage)
-{
-    mDisplayDimension.setDependent(&mOutputBuffer);
-    mDisplayDimension.setDependent(&mExpectedBuffer);
-}
-
-std::string Hwc2TestVirtualDisplay::dump() const
-{
-    std::stringstream dmp;
-
-    dmp << "virtual display: \n";
-
-    mDisplayDimension.dump();
-
-    return dmp.str();
-}
-
-int Hwc2TestVirtualDisplay::getOutputBuffer(buffer_handle_t* outHandle,
-        android::base::unique_fd* outAcquireFence)
-{
-    int32_t acquireFence;
-    int ret = mOutputBuffer.getOutputBuffer(outHandle, &acquireFence);
-    outAcquireFence->reset(acquireFence);
-    return ret;
-}
-
-void Hwc2TestVirtualDisplay::reset()
-{
-    return mDisplayDimension.reset();
-}
-
-bool Hwc2TestVirtualDisplay::advance()
-{
-    return mDisplayDimension.advance();
-}
-
-UnsignedArea Hwc2TestVirtualDisplay::getDisplayDimension() const
-{
-    return mDisplayDimension.get();
-}
-
-int Hwc2TestVirtualDisplay::verifyOutputBuffer(const Hwc2TestLayers* testLayers,
-        const std::vector<hwc2_layer_t>* allLayers,
-        const std::set<hwc2_layer_t>* clearLayers)
-{
-    int ret = mExpectedBuffer.generateExpectedBuffer(testLayers, allLayers,
-            clearLayers);
-    if (ret)
-        return ret;
-
-    ComparatorResult::get().CompareBuffers(mOutputBuffer.graphicBuffer(),
-        mExpectedBuffer.graphicBuffer());
-
-    return 0;
-}
-
-int Hwc2TestVirtualDisplay::writeBuffersToFile(std::string name)
-{
-    std::ostringstream expectedPath;
-    std::ostringstream resultPath;
-    int ret = mkdir(DIR_NAME, DEFFILEMODE);
-    if (ret && errno != EEXIST)
-        return ret;
-
-    expectedPath << DIR_NAME << "/expected-" << name << ".png";
-    resultPath << DIR_NAME << "/result-" << name << ".png";
-
-    if (!mExpectedBuffer.writeBufferToFile(expectedPath.str()) ||
-            !mOutputBuffer.writeBufferToFile(resultPath.str()))
-        return -1;
-
-    return 0;
-}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h
deleted file mode 100644
index 5a74a6c..0000000
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2016 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 _HWC2_TEST_VIRTUAL_DISPLAY_H
-#define _HWC2_TEST_VIRTUAL_DISPLAY_H
-
-#include "Hwc2TestBuffer.h"
-#include "Hwc2TestPixelComparator.h"
-#include "Hwc2TestProperties.h"
-
-#define HWC2_INCLUDE_STRINGIFICATION
-#define HWC2_USE_CPP11
-#include <hardware/hwcomposer2.h>
-#undef HWC2_INCLUDE_STRINGIFICATION
-#undef HWC2_USE_CPP11
-
-class Hwc2TestVirtualDisplay {
-public:
-    explicit Hwc2TestVirtualDisplay(Hwc2TestCoverage coverage);
-
-    std::string dump() const;
-
-    int getOutputBuffer(buffer_handle_t* outHandle,
-            android::base::unique_fd* outAcquireFence);
-
-    int verifyOutputBuffer(const Hwc2TestLayers* testLayers,
-            const std::vector<hwc2_layer_t>* allLayers,
-            const std::set<hwc2_layer_t>* clearLayers);
-
-    int writeBuffersToFile(std::string name);
-    void reset();
-    bool advance();
-
-    UnsignedArea getDisplayDimension() const;
-
-private:
-    Hwc2TestOutputBuffer mOutputBuffer;
-    Hwc2TestExpectedBuffer mExpectedBuffer;
-    Hwc2TestDisplayDimension mDisplayDimension;
-};
-
-#endif /* ifndef _HWC2_TEST_VIRTUAL_DISPLAY_H */
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f842d61..3c4a791 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -36,26 +36,38 @@
         ":libsurfaceflinger_sources",
         "libsurfaceflinger_unittest_main.cpp",
         "CachingTest.cpp",
-	"CompositionTest.cpp",
+        "CompositionTest.cpp",
         "DispSyncSourceTest.cpp",
         "DisplayIdentificationTest.cpp",
         "DisplayTransactionTest.cpp",
         "EventControlThreadTest.cpp",
         "EventThreadTest.cpp",
-        "IdleTimerTest.cpp",
+        "HWComposerTest.cpp",
+        "OneShotTimerTest.cpp",
         "LayerHistoryTest.cpp",
+        "LayerHistoryTestV2.cpp",
         "LayerMetadataTest.cpp",
+        "PhaseOffsetsTest.cpp",
+        "PromiseTest.cpp",
         "SchedulerTest.cpp",
         "SchedulerUtilsTest.cpp",
+        "SetFrameRateTest.cpp",
         "RefreshRateConfigsTest.cpp",
+        "RefreshRateSelectionTest.cpp",
         "RefreshRateStatsTest.cpp",
         "RegionSamplingTest.cpp",
         "TimeStatsTest.cpp",
+        "FrameTracerTest.cpp",
+        "TransactionApplicationTest.cpp",
+        "StrongTypingTest.cpp",
+        "VSyncDispatchTimerQueueTest.cpp",
+        "VSyncDispatchRealtimeTest.cpp",
+        "VSyncModulatorTest.cpp",
+        "VSyncPredictorTest.cpp",
+        "VSyncReactorTest.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockDisplay.cpp",
         "mock/DisplayHardware/MockPowerAdvisor.cpp",
-        "mock/gui/MockGraphicBufferConsumer.cpp",
-        "mock/gui/MockGraphicBufferProducer.cpp",
         "mock/MockDispSync.cpp",
         "mock/MockEventControlThread.cpp",
         "mock/MockEventThread.cpp",
@@ -63,13 +75,24 @@
         "mock/MockNativeWindowSurface.cpp",
         "mock/MockSurfaceInterceptor.cpp",
         "mock/MockTimeStats.cpp",
+        "mock/MockFrameTracer.cpp",
         "mock/system/window/MockNativeWindow.cpp",
     ],
     static_libs: [
         "libgmock",
         "libcompositionengine",
         "libcompositionengine_mocks",
+        "libgui_mocks",
+        "libperfetto_client_experimental",
         "librenderengine_mocks",
+        "perfetto_trace_protos",
+    ],
+    shared_libs: [
+        "libprotoutil",
+        "libstatssocket",
+        "libsurfaceflinger",
+        "libtimestats",
+        "libtimestats_proto",
     ],
     header_libs: [
         "libsurfaceflinger_headers",
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
index 74ce540..1b8c76d 100644
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "CachingTest"
 
@@ -91,3 +95,6 @@
     }
 }
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 349dd3f..32d722e 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "CompositionTest"
 
@@ -31,21 +35,29 @@
 #include <utils/String8.h>
 
 #include "BufferQueueLayer.h"
-#include "ColorLayer.h"
+#include "EffectLayer.h"
 #include "Layer.h"
-
-#include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockDispSync.h"
 #include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockMessageQueue.h"
+#include "mock/MockTimeStats.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
 namespace {
 
+namespace hal = android::hardware::graphics::composer::hal;
+
+using hal::Error;
+using hal::IComposer;
+using hal::IComposerClient;
+using hal::PowerMode;
+using hal::Transform;
+
 using testing::_;
 using testing::AtLeast;
 using testing::Between;
@@ -61,16 +73,11 @@
 using testing::ReturnRef;
 using testing::SetArgPointee;
 
-using android::Hwc2::Error;
-using android::Hwc2::IComposer;
-using android::Hwc2::IComposerClient;
-using android::Hwc2::Transform;
-
 using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
 using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
 
-constexpr hwc2_display_t HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID;
-constexpr hwc2_layer_t HWC_LAYER = 5000;
+constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID;
+constexpr hal::HWLayerId HWC_LAYER = 5000;
 constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0);
 
 constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
@@ -95,16 +102,13 @@
         mFlinger.mutableEventQueue().reset(mMessageQueue);
         setupScheduler();
 
-        EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
-        EXPECT_CALL(*mPrimaryDispSync, getPeriod())
-                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
-        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
         EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
                 .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
         EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
                 .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
 
         mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+        mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
         setupComposer(0);
     }
 
@@ -116,8 +120,6 @@
 
     void setupComposer(int virtualDisplayCount) {
         mComposer = new Hwc2::mock::Composer();
-        EXPECT_CALL(*mComposer, getCapabilities())
-                .WillOnce(Return(std::vector<IComposer::Capability>()));
         EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
         mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
 
@@ -125,25 +127,31 @@
     }
 
     void setupScheduler() {
-        std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
-        mFlinger.mutableRefreshRateConfigs() =
-                std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false,
-                                                                configs,
-                                                                /*currentConfig=*/0);
-        mFlinger.mutableRefreshRateStats() =
-                std::make_unique<scheduler::RefreshRateStats>(*mFlinger.mutableRefreshRateConfigs(),
-                                                              *mFlinger.mutableTimeStats(),
-                                                              /*currentConfig=*/0,
-                                                              /*powerMode=*/HWC_POWER_MODE_OFF);
-        mScheduler = new TestableScheduler(*mFlinger.mutableRefreshRateConfigs());
-        mScheduler->mutableEventControlThread().reset(mEventControlThread);
-        mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
-        EXPECT_CALL(*mEventThread.get(), registerDisplayEventConnection(_));
-        sp<Scheduler::ConnectionHandle> connectionHandle =
-                mScheduler->addConnection(std::move(mEventThread));
-        mFlinger.mutableSfConnectionHandle() = std::move(connectionHandle);
+        auto eventThread = std::make_unique<mock::EventThread>();
+        auto sfEventThread = std::make_unique<mock::EventThread>();
 
-        mFlinger.mutableScheduler().reset(mScheduler);
+        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*eventThread, createEventConnection(_, _))
+                .WillOnce(Return(
+                        new EventThreadConnection(eventThread.get(), ResyncCallback(),
+                                                  ISurfaceComposer::eConfigChangedSuppress)));
+
+        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+                .WillOnce(Return(
+                        new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+                                                  ISurfaceComposer::eConfigChangedSuppress)));
+
+        auto primaryDispSync = std::make_unique<mock::DispSync>();
+
+        EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*primaryDispSync, getPeriod())
+                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+        EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
+
+        mFlinger.setupScheduler(std::move(primaryDispSync),
+                                std::make_unique<mock::EventControlThread>(),
+                                std::move(eventThread), std::move(sfEventThread));
     }
 
     void setupForceGeometryDirty() {
@@ -165,9 +173,9 @@
     template <typename Case>
     void captureScreenComposition();
 
-    std::unordered_set<HWC2::Capability> mDefaultCapabilities = {HWC2::Capability::SidebandStream};
+    std::unordered_set<hal::Capability> mDefaultCapabilities = {hal::Capability::SIDEBAND_STREAM};
 
-    TestableScheduler* mScheduler;
+    bool mDisplayOff = false;
     TestableSurfaceFlinger mFlinger;
     sp<DisplayDevice> mDisplay;
     sp<DisplayDevice> mExternalDisplay;
@@ -178,13 +186,11 @@
     sp<GraphicBuffer> mBuffer = new GraphicBuffer();
     ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer();
 
-    std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
-    mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
-
     Hwc2::mock::Composer* mComposer = nullptr;
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+    mock::TimeStats* mTimeStats = new mock::TimeStats();
     mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
-    mock::DispSync* mPrimaryDispSync = new mock::DispSync();
+    Hwc2::mock::PowerAdvisor mPowerAdvisor;
 
     sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
 
@@ -225,6 +231,7 @@
     const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
     constexpr bool useIdentityTransform = true;
     constexpr bool forSystem = true;
+    constexpr bool regionSampling = false;
 
     DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH,
                                  DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB,
@@ -243,7 +250,7 @@
     int fd = -1;
     status_t result =
             mFlinger.captureScreenImplLocked(renderArea, traverseLayers, mCaptureScreenBuffer.get(),
-                                             useIdentityTransform, forSystem, &fd);
+                                             useIdentityTransform, forSystem, &fd, regionSampling);
     if (fd >= 0) {
         close(fd);
     }
@@ -260,17 +267,13 @@
 template <typename Derived>
 struct BaseDisplayVariant {
     static constexpr bool IS_SECURE = true;
-    static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_NORMAL;
+    static constexpr hal::PowerMode INIT_POWER_MODE = hal::PowerMode::ON;
 
     static void setupPreconditions(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setPowerMode(HWC_DISPLAY,
-                                 static_cast<Hwc2::IComposerClient::PowerMode>(
-                                         Derived::INIT_POWER_MODE)))
+        EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY, Derived::INIT_POWER_MODE))
                 .WillOnce(Return(Error::NONE));
 
-        FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, HWC2::DisplayType::Physical,
-                               true /* isPrimary */)
+        FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, true /* isPrimary */)
                 .setCapabilities(&test->mDefaultCapabilities)
                 .setPowerMode(Derived::INIT_POWER_MODE)
                 .inject(&test->mFlinger, test->mComposer);
@@ -283,8 +286,28 @@
         EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
         EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
         EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
-        test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, DEFAULT_DISPLAY_ID,
-                                                   false /* isVirtual */, true /* isPrimary */)
+
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+
+        auto ceDisplayArgs =
+                compositionengine::DisplayCreationArgsBuilder()
+                        .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+                        .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+                        .setIsSecure(Derived::IS_SECURE)
+                        .setLayerStackId(DEFAULT_LAYER_STACK)
+                        .setPowerAdvisor(&test->mPowerAdvisor)
+                        .setName(std::string("Injected display for ") +
+                                 test_info->test_case_name() + "." + test_info->name())
+                        .build();
+
+        auto compositionDisplay =
+                compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                       ceDisplayArgs);
+
+        test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
+                                                   DisplayConnectionType::Internal, HWC_DISPLAY,
+                                                   true /* isPrimary */)
                                  .setDisplaySurface(test->mDisplaySurface)
                                  .setNativeWindow(test->mNativeWindow)
                                  .setSecure(Derived::IS_SECURE)
@@ -306,18 +329,12 @@
         EXPECT_CALL(*test->mComposer,
                     setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY))
                 .Times(1);
-        EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
         EXPECT_CALL(*test->mComposer, getDisplayRequests(HWC_DISPLAY, _, _, _)).Times(1);
         EXPECT_CALL(*test->mComposer, acceptDisplayChanges(HWC_DISPLAY)).Times(1);
         EXPECT_CALL(*test->mComposer, presentDisplay(HWC_DISPLAY, _)).Times(1);
         EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1);
 
         EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
-        // TODO: remove once we verify that we can just grab the fence from the
-        // FramebufferSurface.
-        EXPECT_CALL(*test->mRenderEngine, flush()).WillRepeatedly(Invoke([]() {
-            return base::unique_fd();
-        }));
 
         EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1);
         EXPECT_CALL(*test->mDisplaySurface, advanceFrame()).Times(1);
@@ -329,17 +346,17 @@
     template <typename Case>
     static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillRepeatedly(
-                        [](const renderengine::DisplaySettings& displaySettings,
-                           const std::vector<renderengine::LayerSettings>&, ANativeWindowBuffer*,
-                           const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
-                            EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
-                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                      displaySettings.physicalDisplay);
-                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                      displaySettings.clip);
-                            return NO_ERROR;
-                        });
+                .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
+                                   const std::vector<const renderengine::LayerSettings*>&,
+                                   ANativeWindowBuffer*, const bool, base::unique_fd&&,
+                                   base::unique_fd*) -> status_t {
+                    EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.physicalDisplay);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.clip);
+                    return NO_ERROR;
+                });
     }
 
     static void setupNonEmptyFrameCompositionCallExpectations(CompositionTest* test) {
@@ -351,14 +368,24 @@
     }
 
     static void setupHwcCompositionCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
+
         EXPECT_CALL(*test->mDisplaySurface,
                     prepareFrame(compositionengine::DisplaySurface::COMPOSITION_HWC))
                 .Times(1);
     }
 
+    static void setupHwcClientCompositionCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
+    }
+
+    static void setupHwcForcedClientCompositionCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer, validateDisplay(HWC_DISPLAY, _, _)).Times(1);
+    }
+
     static void setupRECompositionCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mDisplaySurface,
-                    prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GLES))
+                    prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GPU))
                 .Times(1);
         EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
                 .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
@@ -368,18 +395,18 @@
                 .WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1),
                                 Return(0)));
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
-                .WillRepeatedly(
-                        [](const renderengine::DisplaySettings& displaySettings,
-                           const std::vector<renderengine::LayerSettings>&, ANativeWindowBuffer*,
-                           const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
-                            EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
-                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                      displaySettings.physicalDisplay);
-                            EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
-                                      displaySettings.clip);
-                            EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
-                            return NO_ERROR;
-                        });
+                .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
+                                   const std::vector<const renderengine::LayerSettings*>&,
+                                   ANativeWindowBuffer*, const bool, base::unique_fd&&,
+                                   base::unique_fd*) -> status_t {
+                    EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.physicalDisplay);
+                    EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                              displaySettings.clip);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
+                    return NO_ERROR;
+                });
     }
 
     template <typename Case>
@@ -410,7 +437,7 @@
 };
 
 struct PoweredOffDisplaySetupVariant : public BaseDisplayVariant<PoweredOffDisplaySetupVariant> {
-    static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_OFF;
+    static constexpr hal::PowerMode INIT_POWER_MODE = hal::PowerMode::OFF;
 
     template <typename Case>
     static void setupPreconditionCallExpectations(CompositionTest*) {}
@@ -429,6 +456,8 @@
     }
 
     static void setupHwcCompositionCallExpectations(CompositionTest*) {}
+    static void setupHwcClientCompositionCallExpectations(CompositionTest*) {}
+    static void setupHwcForcedClientCompositionCallExpectations(CompositionTest*) {}
 
     static void setupRECompositionCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
@@ -513,11 +542,11 @@
 
         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;
-        layer->latchBuffer(ignoredRecomputeVisibleRegions, 0);
+        layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0);
         Mock::VerifyAndClear(test->mRenderEngine);
     }
 
@@ -532,69 +561,85 @@
     }
 
     static void setupHwcSetGeometryCallExpectations(CompositionTest* test) {
-        // TODO: Coverage of other values
-        EXPECT_CALL(*test->mComposer,
-                    setLayerBlendMode(HWC_DISPLAY, HWC_LAYER, LayerProperties::BLENDMODE))
-                .Times(1);
-        // TODO: Coverage of other values for origin
-        EXPECT_CALL(*test->mComposer,
-                    setLayerDisplayFrame(HWC_DISPLAY, HWC_LAYER,
-                                         IComposerClient::Rect({0, 0, LayerProperties::WIDTH,
-                                                                LayerProperties::HEIGHT})))
-                .Times(1);
-        EXPECT_CALL(*test->mComposer,
-                    setLayerPlaneAlpha(HWC_DISPLAY, HWC_LAYER, LayerProperties::COLOR[3]))
-                .Times(1);
-        // TODO: Coverage of other values
-        EXPECT_CALL(*test->mComposer, setLayerZOrder(HWC_DISPLAY, HWC_LAYER, 0u)).Times(1);
-        // TODO: Coverage of other values
-        EXPECT_CALL(*test->mComposer, setLayerInfo(HWC_DISPLAY, HWC_LAYER, 0u, 0u)).Times(1);
+        if (!test->mDisplayOff) {
+            // TODO: Coverage of other values
+            EXPECT_CALL(*test->mComposer,
+                        setLayerBlendMode(HWC_DISPLAY, HWC_LAYER, LayerProperties::BLENDMODE))
+                    .Times(1);
+            // TODO: Coverage of other values for origin
+            EXPECT_CALL(*test->mComposer,
+                        setLayerDisplayFrame(HWC_DISPLAY, HWC_LAYER,
+                                             IComposerClient::Rect({0, 0, LayerProperties::WIDTH,
+                                                                    LayerProperties::HEIGHT})))
+                    .Times(1);
+            EXPECT_CALL(*test->mComposer,
+                        setLayerPlaneAlpha(HWC_DISPLAY, HWC_LAYER, LayerProperties::COLOR[3]))
+                    .Times(1);
+            // TODO: Coverage of other values
+            EXPECT_CALL(*test->mComposer, setLayerZOrder(HWC_DISPLAY, HWC_LAYER, 0u)).Times(1);
+            // TODO: Coverage of other values
+            EXPECT_CALL(*test->mComposer, setLayerInfo(HWC_DISPLAY, HWC_LAYER, 0u, 0u)).Times(1);
 
-        // These expectations retire on saturation as the code path these
-        // expectations are for appears to make an extra call to them.
-        // TODO: Investigate this extra call
-        EXPECT_CALL(*test->mComposer, setLayerTransform(HWC_DISPLAY, HWC_LAYER, DEFAULT_TRANSFORM))
-                .Times(AtLeast(1))
-                .RetiresOnSaturation();
+            // These expectations retire on saturation as the code path these
+            // expectations are for appears to make an extra call to them.
+            // TODO: Investigate this extra call
+            EXPECT_CALL(*test->mComposer,
+                        setLayerTransform(HWC_DISPLAY, HWC_LAYER, DEFAULT_TRANSFORM))
+                    .Times(AtLeast(1))
+                    .RetiresOnSaturation();
+        }
     }
 
     static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
-                                       IComposerClient::FRect({0.f, 0.f, LayerProperties::WIDTH,
-                                                               LayerProperties::HEIGHT})))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
+                                           IComposerClient::FRect({0.f, 0.f, LayerProperties::WIDTH,
+                                                                   LayerProperties::HEIGHT})))
+                    .Times(1);
+        }
     }
 
     static void setupHwcSetSourceCropColorCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
-                                       IComposerClient::FRect({0.f, 0.f, 0.f, 0.f})))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
+                                           IComposerClient::FRect({0.f, 0.f, 0.f, 0.f})))
+                    .Times(1);
+        }
     }
 
     static void setupHwcSetPerFrameCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerVisibleRegion(HWC_DISPLAY, HWC_LAYER,
-                                          std::vector<IComposerClient::Rect>({IComposerClient::Rect(
-                                                  {0, 0, LayerProperties::WIDTH,
-                                                   LayerProperties::HEIGHT})})))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerVisibleRegion(HWC_DISPLAY, HWC_LAYER,
+                                              std::vector<IComposerClient::Rect>(
+                                                      {IComposerClient::Rect(
+                                                              {0, 0, LayerProperties::WIDTH,
+                                                               LayerProperties::HEIGHT})})))
+                    .Times(1);
+        }
     }
 
     static void setupHwcSetPerFrameColorCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _))
+                    .Times(1);
 
-        // TODO: use COLOR
-        EXPECT_CALL(*test->mComposer,
-                    setLayerColor(HWC_DISPLAY, HWC_LAYER,
-                                  IComposerClient::Color({0xff, 0xff, 0xff, 0xff})))
-                .Times(1);
+            // TODO: use COLOR
+            EXPECT_CALL(*test->mComposer,
+                        setLayerColor(HWC_DISPLAY, HWC_LAYER,
+                                      IComposerClient::Color({0xff, 0xff, 0xff, 0xff})))
+                    .Times(1);
+        }
     }
 
     static void setupHwcSetPerFrameBufferCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
-        EXPECT_CALL(*test->mComposer, setLayerBuffer(HWC_DISPLAY, HWC_LAYER, _, _, _)).Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _))
+                    .Times(1);
+            EXPECT_CALL(*test->mComposer, setLayerBuffer(HWC_DISPLAY, HWC_LAYER, _, _, _)).Times(1);
+        }
 
         setupBufferLayerPostFrameCallExpectations(test);
     }
@@ -602,7 +647,7 @@
     static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<renderengine::LayerSettings>& layerSettings,
+                             const std::vector<const renderengine::LayerSettings*>& layerSettings,
                              ANativeWindowBuffer*, const bool, base::unique_fd&&,
                              base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
@@ -612,16 +657,22 @@
                               displaySettings.clip);
                     // screen capture adds an additional color layer as an alpha
                     // prefill, so gtet the back layer.
-                    renderengine::LayerSettings layer = layerSettings.back();
-                    EXPECT_THAT(layer.source.buffer.buffer, Not(IsNull()));
-                    EXPECT_THAT(layer.source.buffer.fence, Not(IsNull()));
-                    EXPECT_EQ(DEFAULT_TEXTURE_ID, layer.source.buffer.textureName);
-                    EXPECT_EQ(false, layer.source.buffer.isY410BT2020);
-                    EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha);
-                    EXPECT_EQ(false, layer.source.buffer.isOpaque);
-                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
-                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
-                    EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+                    if (layerSettings.empty()) {
+                        ADD_FAILURE() << "layerSettings was not expected to be empty in "
+                                         "setupREBufferCompositionCommonCallExpectations "
+                                         "verification lambda";
+                        return NO_ERROR;
+                    }
+                    const renderengine::LayerSettings* layer = layerSettings.back();
+                    EXPECT_THAT(layer->source.buffer.buffer, Not(IsNull()));
+                    EXPECT_THAT(layer->source.buffer.fence, Not(IsNull()));
+                    EXPECT_EQ(DEFAULT_TEXTURE_ID, layer->source.buffer.textureName);
+                    EXPECT_EQ(false, layer->source.buffer.isY410BT2020);
+                    EXPECT_EQ(true, layer->source.buffer.usePremultipliedAlpha);
+                    EXPECT_EQ(false, layer->source.buffer.isOpaque);
+                    EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace);
+                    EXPECT_EQ(LayerProperties::COLOR[3], layer->alpha);
                     return NO_ERROR;
                 });
     }
@@ -645,7 +696,7 @@
     static void setupREColorCompositionCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<renderengine::LayerSettings>& layerSettings,
+                             const std::vector<const renderengine::LayerSettings*>& layerSettings,
                              ANativeWindowBuffer*, const bool, base::unique_fd&&,
                              base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
@@ -655,14 +706,20 @@
                               displaySettings.clip);
                     // screen capture adds an additional color layer as an alpha
                     // prefill, so get the back layer.
-                    renderengine::LayerSettings layer = layerSettings.back();
-                    EXPECT_THAT(layer.source.buffer.buffer, IsNull());
+                    if (layerSettings.empty()) {
+                        ADD_FAILURE()
+                                << "layerSettings was not expected to be empty in "
+                                   "setupREColorCompositionCallExpectations verification lambda";
+                        return NO_ERROR;
+                    }
+                    const renderengine::LayerSettings* layer = layerSettings.back();
+                    EXPECT_THAT(layer->source.buffer.buffer, IsNull());
                     EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
                                     LayerProperties::COLOR[2]),
-                              layer.source.solidColor);
-                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
-                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
-                    EXPECT_EQ(LayerProperties::COLOR[3], layer.alpha);
+                              layer->source.solidColor);
+                    EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace);
+                    EXPECT_EQ(LayerProperties::COLOR[3], layer->alpha);
                     return NO_ERROR;
                 });
     }
@@ -674,7 +731,9 @@
 
 struct DefaultLayerProperties : public BaseLayerProperties<DefaultLayerProperties> {};
 
-struct ColorLayerProperties : public BaseLayerProperties<ColorLayerProperties> {};
+struct EffectLayerProperties : public BaseLayerProperties<EffectLayerProperties> {
+    static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE;
+};
 
 struct SidebandLayerProperties : public BaseLayerProperties<SidebandLayerProperties> {
     using Base = BaseLayerProperties<SidebandLayerProperties>;
@@ -715,7 +774,7 @@
     static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<renderengine::LayerSettings>& layerSettings,
+                             const std::vector<const renderengine::LayerSettings*>& layerSettings,
                              ANativeWindowBuffer*, const bool, base::unique_fd&&,
                              base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
@@ -725,12 +784,18 @@
                               displaySettings.clip);
                     // screen capture adds an additional color layer as an alpha
                     // prefill, so get the back layer.
-                    renderengine::LayerSettings layer = layerSettings.back();
-                    EXPECT_THAT(layer.source.buffer.buffer, IsNull());
-                    EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor);
-                    EXPECT_EQ(0.0, layer.geometry.roundedCornersRadius);
-                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer.sourceDataspace);
-                    EXPECT_EQ(1.0f, layer.alpha);
+                    if (layerSettings.empty()) {
+                        ADD_FAILURE() << "layerSettings was not expected to be empty in "
+                                         "setupInsecureREBufferCompositionCommonCallExpectations "
+                                         "verification lambda";
+                        return NO_ERROR;
+                    }
+                    const renderengine::LayerSettings* layer = layerSettings.back();
+                    EXPECT_THAT(layer->source.buffer.buffer, IsNull());
+                    EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer->source.solidColor);
+                    EXPECT_EQ(0.0, layer->geometry.roundedCornersRadius);
+                    EXPECT_EQ(ui::Dataspace::UNKNOWN, layer->sourceDataspace);
+                    EXPECT_EQ(1.0f, layer->alpha);
                     return NO_ERROR;
                 });
     }
@@ -769,13 +834,16 @@
 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();
 
+        // Layer should be registered with scheduler.
+        EXPECT_EQ(1, test->mFlinger.scheduler()->layerHistorySize());
+
         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;
@@ -783,8 +851,7 @@
         layerDrawingState.active.h = 100;
         layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
                                         LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
-        layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform());
-        layer->setVisibleRegion(Region(Rect(0, 0, 100, 100)));
+        layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
 
         return layer;
     }
@@ -793,19 +860,13 @@
         EXPECT_CALL(*test->mComposer, createLayer(HWC_DISPLAY, _))
                 .WillOnce(DoAll(SetArgPointee<1>(HWC_LAYER), Return(Error::NONE)));
 
-        std::vector<std::unique_ptr<compositionengine::OutputLayer>> outputLayers;
-        outputLayers.emplace_back(test->mDisplay->getCompositionDisplay()
-                                          ->getOrCreateOutputLayer(DEFAULT_DISPLAY_ID,
-                                                                   layer->getCompositionLayer(),
-                                                                   layer));
-
-        test->mDisplay->getCompositionDisplay()->setOutputLayersOrderedByZ(std::move(outputLayers));
+        auto outputLayer = test->mDisplay->getCompositionDisplay()->injectOutputLayerForTest(
+                layer->getCompositionEngineLayerFE());
+        outputLayer->editState().visibleRegion = Region(Rect(0, 0, 100, 100));
+        outputLayer->editState().outputSpaceVisibleRegion = Region(Rect(0, 0, 100, 100));
 
         Mock::VerifyAndClear(test->mComposer);
 
-        Vector<sp<Layer>> layers;
-        layers.add(layer);
-        test->mDisplay->setVisibleLayersSortedByZ(layers);
         test->mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
     }
 
@@ -813,23 +874,26 @@
         EXPECT_CALL(*test->mComposer, destroyLayer(HWC_DISPLAY, HWC_LAYER))
                 .WillOnce(Return(Error::NONE));
 
-        test->mDisplay->getCompositionDisplay()->setOutputLayersOrderedByZ(
-                std::vector<std::unique_ptr<compositionengine::OutputLayer>>());
+        test->mDisplay->getCompositionDisplay()->clearOutputLayers();
         test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
+
+        // Layer should be unregistered with scheduler.
+        test->mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
+        EXPECT_EQ(0, test->mFlinger.scheduler()->layerHistorySize());
     }
 };
 
 template <typename LayerProperties>
-struct ColorLayerVariant : public BaseLayerVariant<LayerProperties> {
+struct EffectLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<ColorLayer>;
+    using FlingerLayerType = sp<EffectLayer>;
 
     static FlingerLayerType createLayer(CompositionTest* test) {
-        FlingerLayerType layer = Base::template createLayerWithFactory<ColorLayer>(test, [test]() {
-            return new ColorLayer(LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(),
-                                                    String8("test-layer"), LayerProperties::WIDTH,
-                                                    LayerProperties::HEIGHT,
-                                                    LayerProperties::LAYER_FLAGS, LayerMetadata()));
+        FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() {
+            return new EffectLayer(
+                    LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), "test-layer",
+                                      LayerProperties::WIDTH, LayerProperties::HEIGHT,
+                                      LayerProperties::LAYER_FLAGS, LayerMetadata()));
         });
 
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
@@ -866,11 +930,12 @@
 
         FlingerLayerType layer =
                 Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() {
-                    return new BufferQueueLayer(
-                            LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(),
-                                              String8("test-layer"), LayerProperties::WIDTH,
-                                              LayerProperties::HEIGHT,
-                                              LayerProperties::LAYER_FLAGS, LayerMetadata()));
+                    sp<Client> client;
+                    LayerCreationArgs args(test->mFlinger.mFlinger.get(), client, "test-layer",
+                                           LayerProperties::WIDTH, LayerProperties::HEIGHT,
+                                           LayerProperties::LAYER_FLAGS, LayerMetadata());
+                    args.textureName = test->mFlinger.mutableTexturePool().back();
+                    return new BufferQueueLayer(args);
                 });
 
         LayerProperties::setupLayerState(test, layer);
@@ -879,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);
     }
 
@@ -924,12 +989,14 @@
 
 template <IComposerClient::Composition CompositionType>
 struct KeepCompositionTypeVariant {
-    static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(CompositionType);
+    static constexpr hal::Composition TYPE = CompositionType;
 
     static void setupHwcSetCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, CompositionType))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, CompositionType))
+                    .Times(1);
+        }
     }
 
     static void setupHwcGetCallExpectations(CompositionTest* test) {
@@ -940,12 +1007,14 @@
 template <IComposerClient::Composition InitialCompositionType,
           IComposerClient::Composition FinalCompositionType>
 struct ChangeCompositionTypeVariant {
-    static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(FinalCompositionType);
+    static constexpr hal::Composition TYPE = FinalCompositionType;
 
     static void setupHwcSetCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, InitialCompositionType))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, InitialCompositionType))
+                    .Times(1);
+        }
     }
 
     static void setupHwcGetCallExpectations(CompositionTest* test) {
@@ -996,14 +1065,46 @@
     template <typename Case>
     static void setupCallExpectations(CompositionTest* test) {
         Case::Display::setupNonEmptyFrameCompositionCallExpectations(test);
+        Case::Display::setupHwcClientCompositionCallExpectations(test);
         Case::Display::setupRECompositionCallExpectations(test);
         Case::Display::template setupRELayerCompositionCallExpectations<Case>(test);
     }
 };
 
-struct ForcedClientCompositionResultVariant : public RECompositionResultVariant {
+struct ForcedClientCompositionResultVariant : public CompositionResultBaseVariant {
     static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
-        layer->forceClientComposition(test->mDisplay);
+        const auto outputLayer =
+                TestableSurfaceFlinger::findOutputLayerForDisplay(layer, test->mDisplay);
+        LOG_FATAL_IF(!outputLayer);
+        outputLayer->editState().forceClientComposition = true;
+    }
+
+    template <typename Case>
+    static void setupCallExpectations(CompositionTest* test) {
+        Case::Display::setupNonEmptyFrameCompositionCallExpectations(test);
+        Case::Display::setupHwcForcedClientCompositionCallExpectations(test);
+        Case::Display::setupRECompositionCallExpectations(test);
+        Case::Display::template setupRELayerCompositionCallExpectations<Case>(test);
+    }
+
+    template <typename Case>
+    static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {}
+
+    template <typename Case>
+    static void setupCallExpectationsForDirtyFrame(CompositionTest*) {}
+};
+
+struct ForcedClientCompositionViaDebugOptionResultVariant : public CompositionResultBaseVariant {
+    static void setupLayerState(CompositionTest* test, sp<Layer>) {
+        test->mFlinger.mutableDebugDisableHWC() = true;
+    }
+
+    template <typename Case>
+    static void setupCallExpectations(CompositionTest* test) {
+        Case::Display::setupNonEmptyFrameCompositionCallExpectations(test);
+        Case::Display::setupHwcForcedClientCompositionCallExpectations(test);
+        Case::Display::setupRECompositionCallExpectations(test);
+        Case::Display::template setupRELayerCompositionCallExpectations<Case>(test);
     }
 
     template <typename Case>
@@ -1080,11 +1181,11 @@
     static void cleanup(CompositionTest* test) {
         Layer::cleanupInjectedLayers(test);
 
-        for (auto& hwcDisplay : test->mFlinger.mFakeHwcDisplays) {
-            hwcDisplay->mutableLayers().clear();
+        for (auto& displayData : test->mFlinger.mutableHwcDisplayData()) {
+            static_cast<TestableSurfaceFlinger::HWC2Display*>(displayData.second.hwcDisplay.get())
+                    ->mutableLayers()
+                    .clear();
         }
-
-        test->mDisplay->setVisibleLayersSortedByZ(Vector<sp<android::Layer>>());
     }
 };
 
@@ -1146,31 +1247,31 @@
  *  Single-color layers
  */
 
-TEST_F(CompositionTest, HWCComposedColorLayerWithDirtyGeometry) {
+TEST_F(CompositionTest, HWCComposedEffectLayerWithDirtyGeometry) {
     displayRefreshCompositionDirtyGeometry<
-            CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+            CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
                             KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
                             HwcCompositionResultVariant>>();
 }
 
-TEST_F(CompositionTest, HWCComposedColorLayerWithDirtyFrame) {
+TEST_F(CompositionTest, HWCComposedEffectLayerWithDirtyFrame) {
     displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+            CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
                             KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
                             HwcCompositionResultVariant>>();
 }
 
-TEST_F(CompositionTest, REComposedColorLayer) {
+TEST_F(CompositionTest, REComposedEffectLayer) {
     displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+            CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
                             ChangeCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR,
                                                          IComposerClient::Composition::CLIENT>,
                             RECompositionResultVariant>>();
 }
 
-TEST_F(CompositionTest, captureScreenColorLayer) {
+TEST_F(CompositionTest, captureScreenEffectLayer) {
     captureScreenComposition<
-            CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+            CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
                             NoCompositionTypeVariant, REScreenshotResultVariant>>();
 }
 
@@ -1299,6 +1400,7 @@
  */
 
 TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyGeometry) {
+    mDisplayOff = true;
     displayRefreshCompositionDirtyGeometry<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
             KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
@@ -1306,6 +1408,7 @@
 }
 
 TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyFrame) {
+    mDisplayOff = true;
     displayRefreshCompositionDirtyFrame<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
             KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
@@ -1313,6 +1416,7 @@
 }
 
 TEST_F(CompositionTest, displayOffREComposedNormalBufferLayer) {
+    mDisplayOff = true;
     displayRefreshCompositionDirtyFrame<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
             ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
@@ -1326,5 +1430,26 @@
             NoCompositionTypeVariant, REScreenshotResultVariant>>();
 }
 
+/* ------------------------------------------------------------------------
+ *  Client composition forced through debug/developer settings
+ */
+
+TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyGeometry) {
+    displayRefreshCompositionDirtyGeometry<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+                            ForcedClientCompositionViaDebugOptionResultVariant>>();
+}
+
+TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyFrame) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+                            ForcedClientCompositionViaDebugOptionResultVariant>>();
+}
+
 } // namespace
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 0aa8cf5..afebc40 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -43,7 +43,7 @@
     void createDispSync();
     void createDispSyncSource();
 
-    void onVSyncEvent(nsecs_t when) override;
+    void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) override;
 
     std::unique_ptr<mock::DispSync> mDispSync;
     std::unique_ptr<DispSyncSource> mDispSyncSource;
@@ -51,7 +51,6 @@
     AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder;
 
     static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms;
-    static constexpr std::chrono::nanoseconds mOffsetThresholdForNextVsync = 16ms;
     static constexpr int mIterations = 100;
 };
 
@@ -67,7 +66,7 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-void DispSyncSourceTest::onVSyncEvent(nsecs_t when) {
+void DispSyncSourceTest::onVSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) {
     ALOGD("onVSyncEvent: %" PRId64, when);
 
     mVSyncEventCallRecorder.recordCall(when);
@@ -79,8 +78,7 @@
 
 void DispSyncSourceTest::createDispSyncSource() {
     createDispSync();
-    mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(),
-                                                       mOffsetThresholdForNextVsync.count(), true,
+    mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true,
                                                        "DispSyncSourceTest");
     mDispSyncSource->setCallback(this);
 }
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index 55995d0..2a0e913 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#include <functional>
+#include <string_view>
+
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
@@ -61,11 +64,73 @@
         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
         "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6";
 
+const unsigned char kPanasonicTvEdid[] =
+        "\x00\xff\xff\xff\xff\xff\xff\x00\x34\xa9\x96\xa2\x01\x01\x01"
+        "\x01\x00\x1d\x01\x03\x80\x80\x48\x78\x0a\xda\xff\xa3\x58\x4a"
+        "\xa2\x29\x17\x49\x4b\x20\x08\x00\x31\x40\x61\x40\x01\x01\x01"
+        "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x08\xe8\x00\x30\xf2\x70"
+        "\x5a\x80\xb0\x58\x8a\x00\xba\x88\x21\x00\x00\x1e\x02\x3a\x80"
+        "\x18\x71\x38\x2d\x40\x58\x2c\x45\x00\xba\x88\x21\x00\x00\x1e"
+        "\x00\x00\x00\xfc\x00\x50\x61\x6e\x61\x73\x6f\x6e\x69\x63\x2d"
+        "\x54\x56\x0a\x00\x00\x00\xfd\x00\x17\x3d\x0f\x88\x3c\x00\x0a"
+        "\x20\x20\x20\x20\x20\x20\x01\x1d\x02\x03\x6b\xf0\x57\x61\x60"
+        "\x10\x1f\x66\x65\x05\x14\x20\x21\x22\x04\x13\x03\x12\x07\x16"
+        "\x5d\x5e\x5f\x62\x63\x64\x2c\x0d\x07\x01\x15\x07\x50\x57\x07"
+        "\x01\x67\x04\x03\x83\x0f\x00\x00\x6e\x03\x0c\x00\x20\x00\x38"
+        "\x3c\x2f\x08\x80\x01\x02\x03\x04\x67\xd8\x5d\xc4\x01\x78\x80"
+        "\x03\xe2\x00\x4b\xe3\x05\xff\x01\xe2\x0f\x33\xe3\x06\x0f\x01"
+        "\xe5\x01\x8b\x84\x90\x01\xeb\x01\x46\xd0\x00\x44\x03\x70\x80"
+        "\x5e\x75\x94\xe6\x11\x46\xd0\x00\x70\x00\x66\x21\x56\xaa\x51"
+        "\x00\x1e\x30\x46\x8f\x33\x00\xba\x88\x21\x00\x00\x1e\x00\x00"
+        "\xc8";
+
+const unsigned char kHisenseTvEdid[] =
+        "\x00\xff\xff\xff\xff\xff\xff\x00\x20\xa3\x00\x00\x00\x00\x00"
+        "\x00\x12\x1d\x01\x03\x80\x00\x00\x78\x0a\xd7\xa5\xa2\x59\x4a"
+        "\x96\x24\x14\x50\x54\xa3\x08\x00\xd1\xc0\xb3\x00\x81\x00\x81"
+        "\x80\x81\x40\x81\xc0\x01\x01\x01\x01\x02\x3a\x80\x18\x71\x38"
+        "\x2d\x40\x58\x2c\x45\x00\x3f\x43\x21\x00\x00\x1a\x02\x3a\x80"
+        "\x18\x71\x38\x2d\x40\x58\x2c\x45\x00\x3f\x43\x21\x00\x00\x1a"
+        "\x00\x00\x00\xfd\x00\x1e\x4c\x1e\x5a\x1e\x00\x0a\x20\x20\x20"
+        "\x20\x20\x20\x00\x00\x00\xfc\x00\x48\x69\x73\x65\x6e\x73\x65"
+        "\x0a\x20\x20\x20\x20\x20\x01\x47\x02\x03\x2d\x71\x50\x90\x05"
+        "\x04\x03\x07\x02\x06\x01\x1f\x14\x13\x12\x16\x11\x15\x20\x2c"
+        "\x09\x07\x03\x15\x07\x50\x57\x07\x00\x39\x07\xbb\x66\x03\x0c"
+        "\x00\x12\x34\x00\x83\x01\x00\x00\x01\x1d\x00\x72\x51\xd0\x1e"
+        "\x20\x6e\x28\x55\x00\xc4\x8e\x21\x00\x00\x1e\x01\x1d\x80\x18"
+        "\x71\x1c\x16\x20\x58\x2c\x25\x00\xc4\x8e\x21\x00\x00\x9e\x8c"
+        "\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\x13\x8e\x21\x00"
+        "\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x07";
+
+const unsigned char kCtlDisplayEdid[] =
+        "\x00\xff\xff\xff\xff\xff\xff\x00\x0e\x8c\x9d\x24\x00\x00\x00\x00"
+        "\xff\x17\x01\x04\xa5\x34\x1d\x78\x3a\xa7\x25\xa4\x57\x51\xa0\x26"
+        "\x10\x50\x54\xbf\xef\x80\xb3\x00\xa9\x40\x95\x00\x81\x40\x81\x80"
+        "\x95\x0f\x71\x4f\x90\x40\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c"
+        "\x45\x00\x09\x25\x21\x00\x00\x1e\x66\x21\x50\xb0\x51\x00\x1b\x30"
+        "\x40\x70\x36\x00\x09\x25\x21\x00\x00\x1e\x00\x00\x00\xfd\x00\x31"
+        "\x4c\x1e\x52\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfc"
+        "\x00\x4c\x50\x32\x33\x36\x31\x0a\x20\x20\x20\x20\x20\x20\x01\x3e"
+        "\x02\x03\x22\xf2\x4f\x90\x9f\x05\x14\x04\x13\x03\x02\x12\x11\x07"
+        "\x06\x16\x15\x01\x23\x09\x07\x07\x83\x01\x00\x00\x65\xb9\x14\x00"
+        "\x04\x00\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c\x45\x00\x09\x25"
+        "\x21\x00\x00\x1e\x02\x3a\x80\xd0\x72\x38\x2d\x40\x10\x2c\x45\x80"
+        "\x09\x25\x21\x00\x00\x1e\x01\x1d\x00\x72\x51\xd0\x1e\x20\x6e\x28"
+        "\x55\x00\x09\x25\x21\x00\x00\x1e\x8c\x0a\xd0\x8a\x20\xe0\x2d\x10"
+        "\x10\x3e\x96\x00\x09\x25\x21\x00\x00\x18\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4";
+
 template <size_t N>
 DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&bytes)[N]) {
     return DisplayIdentificationData(bytes, bytes + N - 1);
 }
 
+uint32_t hash(const char* str) {
+    return static_cast<uint32_t>(std::hash<std::string_view>()(str));
+}
+
 } // namespace
 
 const DisplayIdentificationData& getInternalEdid() {
@@ -83,12 +148,30 @@
     return data;
 }
 
+const DisplayIdentificationData& getPanasonicTvEdid() {
+    static const DisplayIdentificationData data = asDisplayIdentificationData(kPanasonicTvEdid);
+    return data;
+}
+
+const DisplayIdentificationData& getHisenseTvEdid() {
+    static const DisplayIdentificationData data = asDisplayIdentificationData(kHisenseTvEdid);
+    return data;
+}
+
+const DisplayIdentificationData& getCtlDisplayEdid() {
+    static const DisplayIdentificationData data = asDisplayIdentificationData(kCtlDisplayEdid);
+    return data;
+}
+
 TEST(DisplayIdentificationTest, isEdid) {
     EXPECT_FALSE(isEdid({}));
 
     EXPECT_TRUE(isEdid(getInternalEdid()));
     EXPECT_TRUE(isEdid(getExternalEdid()));
     EXPECT_TRUE(isEdid(getExternalEedid()));
+    EXPECT_TRUE(isEdid(getPanasonicTvEdid()));
+    EXPECT_TRUE(isEdid(getHisenseTvEdid()));
+    EXPECT_TRUE(isEdid(getCtlDisplayEdid()));
 }
 
 TEST(DisplayIdentificationTest, parseEdid) {
@@ -97,19 +180,86 @@
     EXPECT_EQ(0x4ca3u, edid->manufacturerId);
     EXPECT_STREQ("SEC", edid->pnpId.data());
     // ASCII text should be used as fallback if display name and serial number are missing.
-    EXPECT_EQ("121AT11-801", edid->displayName);
+    EXPECT_EQ(hash("121AT11-801"), edid->modelHash);
+    EXPECT_TRUE(edid->displayName.empty());
+    EXPECT_EQ(12610, edid->productId);
+    EXPECT_EQ(21, edid->manufactureOrModelYear);
+    EXPECT_EQ(0, edid->manufactureWeek);
+    EXPECT_FALSE(edid->cea861Block);
 
     edid = parseEdid(getExternalEdid());
     ASSERT_TRUE(edid);
     EXPECT_EQ(0x22f0u, edid->manufacturerId);
     EXPECT_STREQ("HWP", edid->pnpId.data());
+    EXPECT_EQ(hash("HP ZR30w"), edid->modelHash);
     EXPECT_EQ("HP ZR30w", edid->displayName);
+    EXPECT_EQ(10348, edid->productId);
+    EXPECT_EQ(22, edid->manufactureOrModelYear);
+    EXPECT_EQ(2, edid->manufactureWeek);
+    EXPECT_FALSE(edid->cea861Block);
 
     edid = parseEdid(getExternalEedid());
     ASSERT_TRUE(edid);
     EXPECT_EQ(0x4c2du, edid->manufacturerId);
     EXPECT_STREQ("SAM", edid->pnpId.data());
+    EXPECT_EQ(hash("SAMSUNG"), edid->modelHash);
     EXPECT_EQ("SAMSUNG", edid->displayName);
+    EXPECT_EQ(2302, edid->productId);
+    EXPECT_EQ(21, edid->manufactureOrModelYear);
+    EXPECT_EQ(41, edid->manufactureWeek);
+    ASSERT_TRUE(edid->cea861Block);
+    ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock);
+    auto physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress;
+    EXPECT_EQ(2, physicalAddress.a);
+    EXPECT_EQ(0, physicalAddress.b);
+    EXPECT_EQ(0, physicalAddress.c);
+    EXPECT_EQ(0, physicalAddress.d);
+
+    edid = parseEdid(getPanasonicTvEdid());
+    ASSERT_TRUE(edid);
+    EXPECT_EQ(13481, edid->manufacturerId);
+    EXPECT_STREQ("MEI", edid->pnpId.data());
+    EXPECT_EQ(hash("Panasonic-TV"), edid->modelHash);
+    EXPECT_EQ("Panasonic-TV", edid->displayName);
+    EXPECT_EQ(41622, edid->productId);
+    EXPECT_EQ(29, edid->manufactureOrModelYear);
+    EXPECT_EQ(0, edid->manufactureWeek);
+    ASSERT_TRUE(edid->cea861Block);
+    ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock);
+    physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress;
+    EXPECT_EQ(2, physicalAddress.a);
+    EXPECT_EQ(0, physicalAddress.b);
+    EXPECT_EQ(0, physicalAddress.c);
+    EXPECT_EQ(0, physicalAddress.d);
+
+    edid = parseEdid(getHisenseTvEdid());
+    ASSERT_TRUE(edid);
+    EXPECT_EQ(8355, edid->manufacturerId);
+    EXPECT_STREQ("HEC", edid->pnpId.data());
+    EXPECT_EQ(hash("Hisense"), edid->modelHash);
+    EXPECT_EQ("Hisense", edid->displayName);
+    EXPECT_EQ(0, edid->productId);
+    EXPECT_EQ(29, edid->manufactureOrModelYear);
+    EXPECT_EQ(18, edid->manufactureWeek);
+    ASSERT_TRUE(edid->cea861Block);
+    ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock);
+    physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress;
+    EXPECT_EQ(1, physicalAddress.a);
+    EXPECT_EQ(2, physicalAddress.b);
+    EXPECT_EQ(3, physicalAddress.c);
+    EXPECT_EQ(4, physicalAddress.d);
+
+    edid = parseEdid(getCtlDisplayEdid());
+    ASSERT_TRUE(edid);
+    EXPECT_EQ(3724, edid->manufacturerId);
+    EXPECT_STREQ("CTL", edid->pnpId.data());
+    EXPECT_EQ(hash("LP2361"), edid->modelHash);
+    EXPECT_EQ("LP2361", edid->displayName);
+    EXPECT_EQ(9373, edid->productId);
+    EXPECT_EQ(23, edid->manufactureOrModelYear);
+    EXPECT_EQ(0xff, edid->manufactureWeek);
+    ASSERT_TRUE(edid->cea861Block);
+    EXPECT_FALSE(edid->cea861Block->hdmiVendorDataBlock);
 }
 
 TEST(DisplayIdentificationTest, parseInvalidEdid) {
@@ -122,13 +272,15 @@
     auto edid = parseEdid(data);
     ASSERT_TRUE(edid);
     // Serial number should be used as fallback if display name is invalid.
-    EXPECT_EQ("CN4202137Q", edid->displayName);
+    const auto modelHash = hash("CN4202137Q");
+    EXPECT_EQ(modelHash, edid->modelHash);
+    EXPECT_TRUE(edid->displayName.empty());
 
     // Parsing should succeed even if EDID is truncated.
     data.pop_back();
     edid = parseEdid(data);
     ASSERT_TRUE(edid);
-    EXPECT_EQ("CN4202137Q", edid->displayName);
+    EXPECT_EQ(modelHash, edid->modelHash);
 }
 
 TEST(DisplayIdentificationTest, getPnpId) {
@@ -156,6 +308,93 @@
     EXPECT_NE(secondaryInfo->id, tertiaryInfo->id);
 }
 
+TEST(DisplayIdentificationTest, deviceProductInfo) {
+    using ManufactureYear = DeviceProductInfo::ManufactureYear;
+    using ManufactureWeekAndYear = DeviceProductInfo::ManufactureWeekAndYear;
+    using ModelYear = DeviceProductInfo::ModelYear;
+    using RelativeAddress = DeviceProductInfo::RelativeAddress;
+
+    {
+        const auto displayIdInfo = parseDisplayIdentificationData(0, getInternalEdid());
+        ASSERT_TRUE(displayIdInfo);
+        ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+        const auto& info = *displayIdInfo->deviceProductInfo;
+        EXPECT_STREQ("", info.name.data());
+        EXPECT_STREQ("SEC", info.manufacturerPnpId.data());
+        EXPECT_STREQ("12610", info.productId.data());
+        ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
+        EXPECT_EQ(2011, std::get<ManufactureYear>(info.manufactureOrModelDate).year);
+        EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+    }
+    {
+        const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEdid());
+        ASSERT_TRUE(displayIdInfo);
+        ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+        const auto& info = *displayIdInfo->deviceProductInfo;
+        EXPECT_STREQ("HP ZR30w", info.name.data());
+        EXPECT_STREQ("HWP", info.manufacturerPnpId.data());
+        EXPECT_STREQ("10348", info.productId.data());
+        ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
+        const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
+        EXPECT_EQ(2012, date.year);
+        EXPECT_EQ(2, date.week);
+        EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+    }
+    {
+        const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEedid());
+        ASSERT_TRUE(displayIdInfo);
+        ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+        const auto& info = *displayIdInfo->deviceProductInfo;
+        EXPECT_STREQ("SAMSUNG", info.name.data());
+        EXPECT_STREQ("SAM", info.manufacturerPnpId.data());
+        EXPECT_STREQ("2302", info.productId.data());
+        ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
+        const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
+        EXPECT_EQ(2011, date.year);
+        EXPECT_EQ(41, date.week);
+        EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress);
+    }
+    {
+        const auto displayIdInfo = parseDisplayIdentificationData(0, getPanasonicTvEdid());
+        ASSERT_TRUE(displayIdInfo);
+        ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+        const auto& info = *displayIdInfo->deviceProductInfo;
+        EXPECT_STREQ("Panasonic-TV", info.name.data());
+        EXPECT_STREQ("MEI", info.manufacturerPnpId.data());
+        EXPECT_STREQ("41622", info.productId.data());
+        ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
+        const auto& date = std::get<ManufactureYear>(info.manufactureOrModelDate);
+        EXPECT_EQ(2019, date.year);
+        EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress);
+    }
+    {
+        const auto displayIdInfo = parseDisplayIdentificationData(0, getHisenseTvEdid());
+        ASSERT_TRUE(displayIdInfo);
+        ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+        const auto& info = *displayIdInfo->deviceProductInfo;
+        EXPECT_STREQ("Hisense", info.name.data());
+        EXPECT_STREQ("HEC", info.manufacturerPnpId.data());
+        EXPECT_STREQ("0", info.productId.data());
+        ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate));
+        const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate);
+        EXPECT_EQ(2019, date.year);
+        EXPECT_EQ(18, date.week);
+        EXPECT_EQ((RelativeAddress{{1, 2, 3, 4}}), info.relativeAddress);
+    }
+    {
+        const auto displayIdInfo = parseDisplayIdentificationData(0, getCtlDisplayEdid());
+        ASSERT_TRUE(displayIdInfo);
+        ASSERT_TRUE(displayIdInfo->deviceProductInfo);
+        const auto& info = *displayIdInfo->deviceProductInfo;
+        EXPECT_STREQ("LP2361", info.name.data());
+        EXPECT_STREQ("CTL", info.manufacturerPnpId.data());
+        EXPECT_STREQ("9373", info.productId.data());
+        ASSERT_TRUE(std::holds_alternative<ModelYear>(info.manufactureOrModelDate));
+        EXPECT_EQ(2013, std::get<ModelYear>(info.manufactureOrModelDate).year);
+        EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress);
+    }
+}
+
 TEST(DisplayIdentificationTest, getFallbackDisplayId) {
     // Manufacturer ID should be invalid.
     ASSERT_FALSE(getPnpId(getFallbackDisplayId(0)));
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index f40996e..ce5f35c 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
@@ -21,45 +25,60 @@
 
 #include <compositionengine/Display.h>
 #include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/mock/Display.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
 #include <compositionengine/mock/DisplaySurface.h>
+#include <compositionengine/mock/RenderSurface.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <gui/mock/GraphicBufferConsumer.h>
+#include <gui/mock/GraphicBufferProducer.h>
 #include <log/log.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <ui/DebugUtils.h>
 
 #include "DisplayIdentificationTest.h"
-#include "Scheduler/RefreshRateConfigs.h"
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
 #include "mock/MockDispSync.h"
 #include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockMessageQueue.h"
 #include "mock/MockNativeWindowSurface.h"
 #include "mock/MockSurfaceInterceptor.h"
-#include "mock/gui/MockGraphicBufferConsumer.h"
-#include "mock/gui/MockGraphicBufferProducer.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
 namespace {
 
+namespace hal = android::hardware::graphics::composer::hal;
+
 using testing::_;
+using testing::AnyNumber;
 using testing::DoAll;
 using testing::Mock;
 using testing::ResultOf;
 using testing::Return;
+using testing::ReturnRefOfCopy;
 using testing::SetArgPointee;
+using testing::StrictMock;
 
-using android::Hwc2::ColorMode;
-using android::Hwc2::Error;
-using android::Hwc2::Hdr;
-using android::Hwc2::IComposer;
-using android::Hwc2::IComposerClient;
-using android::Hwc2::PerFrameMetadataKey;
-using android::Hwc2::RenderIntent;
+using hal::ColorMode;
+using hal::Connection;
+using hal::DisplayCapability;
+using hal::DisplayType;
+using hal::Error;
+using hal::Hdr;
+using hal::HWDisplayId;
+using hal::IComposer;
+using hal::IComposerClient;
+using hal::PerFrameMetadataKey;
+using hal::PowerMode;
+using hal::RenderIntent;
 
 using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
 using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
@@ -70,7 +89,7 @@
 constexpr int32_t DEFAULT_DPI = 320;
 constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565;
 
-constexpr int HWC_POWER_MODE_LEET = 1337; // An out of range power mode value
+constexpr int POWER_MODE_LEET = 1337; // An out of range power mode value
 
 /* ------------------------------------------------------------------------
  * Boolean avoidance
@@ -96,19 +115,19 @@
     DisplayTransactionTest();
     ~DisplayTransactionTest() override;
 
-    void setupScheduler();
-
     // --------------------------------------------------------------------
     // Mock/Fake injection
 
+    void injectMockScheduler();
     void injectMockComposer(int virtualDisplayCount);
     void injectFakeBufferQueueFactory();
     void injectFakeNativeWindowSurfaceFactory();
+    sp<DisplayDevice> injectDefaultInternalDisplay(std::function<void(FakeDisplayDeviceInjector&)>);
 
     // --------------------------------------------------------------------
     // Postcondition helpers
 
-    bool hasPhysicalHwcDisplay(hwc2_display_t hwcDisplayId);
+    bool hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId);
     bool hasTransactionFlagSet(int flag);
     bool hasDisplayDevice(sp<IBinder> displayToken);
     sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken);
@@ -120,13 +139,10 @@
     // --------------------------------------------------------------------
     // Test instances
 
-    TestableScheduler* mScheduler;
     TestableSurfaceFlinger mFlinger;
-    mock::EventThread* mEventThread = new mock::EventThread();
-    mock::EventThread* mSFEventThread = new mock::EventThread();
-    mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
     sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
     sp<GraphicBuffer> mBuffer = new GraphicBuffer();
+    Hwc2::mock::PowerAdvisor mPowerAdvisor;
 
     // These mocks are created by the test, but are destroyed by SurfaceFlinger
     // by virtue of being stored into a std::unique_ptr. However we still need
@@ -135,7 +151,11 @@
     Hwc2::mock::Composer* mComposer = nullptr;
     mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
     mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor();
-    mock::DispSync* mPrimaryDispSync = new mock::DispSync();
+
+    mock::DispSync* mPrimaryDispSync = new mock::DispSync;
+    mock::EventControlThread* mEventControlThread = new mock::EventControlThread;
+    mock::EventThread* mEventThread = new mock::EventThread;
+    mock::EventThread* mSFEventThread = new mock::EventThread;
 
     // These mocks are created only when expected to be created via a factory.
     sp<mock::GraphicBufferConsumer> mConsumer;
@@ -151,7 +171,7 @@
     // Default to no wide color display support configured
     mFlinger.mutableHasWideColorDisplay() = false;
     mFlinger.mutableUseColorManagement() = false;
-    mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED;
+    mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
 
     // Default to using HWC virtual displays
     mFlinger.mutableUseHwcVirtualDisplays() = true;
@@ -165,7 +185,7 @@
         return nullptr;
     });
 
-    setupScheduler();
+    injectMockScheduler();
     mFlinger.mutableEventQueue().reset(mMessageQueue);
     mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
     mFlinger.mutableInterceptor().reset(mSurfaceInterceptor);
@@ -179,35 +199,25 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-void DisplayTransactionTest::setupScheduler() {
-    std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
-    mFlinger.mutableRefreshRateConfigs() =
-            std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
-                                                            /*currentConfig=*/0);
-    mFlinger.mutableRefreshRateStats() =
-            std::make_unique<scheduler::RefreshRateStats>(*mFlinger.mutableRefreshRateConfigs(),
-                                                          *mFlinger.mutableTimeStats(),
-                                                          /*currentConfig=*/0,
-                                                          /*powerMode=*/HWC_POWER_MODE_OFF);
-    mScheduler = new TestableScheduler(*mFlinger.mutableRefreshRateConfigs());
-    mScheduler->mutableEventControlThread().reset(mEventControlThread);
-    mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
+void DisplayTransactionTest::injectMockScheduler() {
     EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*mEventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(mEventThread, ResyncCallback(),
+                                                       ISurfaceComposer::eConfigChangedSuppress)));
 
-    sp<Scheduler::ConnectionHandle> sfConnectionHandle =
-            mScheduler->addConnection(std::unique_ptr<EventThread>(mSFEventThread));
-    mFlinger.mutableSfConnectionHandle() = std::move(sfConnectionHandle);
-    sp<Scheduler::ConnectionHandle> appConnectionHandle =
-            mScheduler->addConnection(std::unique_ptr<EventThread>(mEventThread));
-    mFlinger.mutableAppConnectionHandle() = std::move(appConnectionHandle);
-    mFlinger.mutableScheduler().reset(mScheduler);
+    EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(mSFEventThread, ResyncCallback(),
+                                                       ISurfaceComposer::eConfigChangedSuppress)));
+
+    mFlinger.setupScheduler(std::unique_ptr<DispSync>(mPrimaryDispSync),
+                            std::unique_ptr<EventControlThread>(mEventControlThread),
+                            std::unique_ptr<EventThread>(mEventThread),
+                            std::unique_ptr<EventThread>(mSFEventThread));
 }
 
 void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) {
     mComposer = new Hwc2::mock::Composer();
-    EXPECT_CALL(*mComposer, getCapabilities())
-            .WillOnce(Return(std::vector<IComposer::Capability>()));
     EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
     mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
 
@@ -238,7 +248,51 @@
     });
 }
 
-bool DisplayTransactionTest::hasPhysicalHwcDisplay(hwc2_display_t hwcDisplayId) {
+sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
+        std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
+    constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
+    constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
+    constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
+    constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0;
+
+    // The DisplayDevice is required to have a framebuffer (behind the
+    // ANativeWindow interface) which uses the actual hardware display
+    // size.
+    EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+    EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+            .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+    EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
+    EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
+    EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
+    EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
+
+    auto compositionDisplay = compositionengine::impl::
+            createDisplay(mFlinger.getCompositionEngine(),
+                          compositionengine::DisplayCreationArgsBuilder()
+                                  .setPhysical(
+                                          {DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+                                  .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+                                  .setPowerAdvisor(&mPowerAdvisor)
+                                  .build());
+
+    auto injector =
+            FakeDisplayDeviceInjector(mFlinger, compositionDisplay, DisplayConnectionType::Internal,
+                                      DEFAULT_DISPLAY_HWC_DISPLAY_ID, true /* isPrimary */);
+
+    injector.setNativeWindow(mNativeWindow);
+    if (injectExtra) {
+        injectExtra(injector);
+    }
+
+    auto displayDevice = injector.inject();
+
+    Mock::VerifyAndClear(mNativeWindow.get());
+
+    return displayDevice;
+}
+
+bool DisplayTransactionTest::hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId) {
     return mFlinger.mutableHwcPhysicalDisplayIdMap().count(hwcDisplayId) == 1;
 }
 
@@ -296,8 +350,8 @@
     static std::optional<DisplayId> get() {
         if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
             return getFallbackDisplayId(static_cast<bool>(PhysicalDisplay::PRIMARY)
-                                                ? HWC_DISPLAY_PRIMARY
-                                                : HWC_DISPLAY_EXTERNAL);
+                                                ? LEGACY_DISPLAY_TYPE_PRIMARY
+                                                : LEGACY_DISPLAY_TYPE_EXTERNAL);
         }
 
         const auto info =
@@ -317,6 +371,33 @@
     static std::optional<DisplayId> get() { return {}; }
 };
 
+template <typename>
+struct DisplayConnectionTypeGetter {
+    static constexpr std::optional<DisplayConnectionType> value;
+};
+
+template <typename PhysicalDisplay>
+struct DisplayConnectionTypeGetter<PhysicalDisplayId<PhysicalDisplay>> {
+    static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE;
+};
+
+template <typename>
+struct HwcDisplayIdGetter {
+    static constexpr std::optional<HWDisplayId> value;
+};
+
+constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010;
+
+template <DisplayId::Type displayId>
+struct HwcDisplayIdGetter<VirtualDisplayId<displayId>> {
+    static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID;
+};
+
+template <typename PhysicalDisplay>
+struct HwcDisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> {
+    static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID;
+};
+
 // DisplayIdType can be:
 //     1) PhysicalDisplayId<...> for generated ID of physical display backed by HWC.
 //     2) VirtualDisplayId<...> for hard-coded ID of virtual display backed by HWC.
@@ -325,6 +406,8 @@
           Secure secure, Primary primary, int grallocUsage>
 struct DisplayVariant {
     using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
+    using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
+    using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>;
 
     // The display width and height
     static constexpr int WIDTH = width;
@@ -349,9 +432,21 @@
     static constexpr Primary PRIMARY = primary;
 
     static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
-        auto injector =
-                FakeDisplayDeviceInjector(test->mFlinger, DISPLAY_ID::get(),
-                                          static_cast<bool>(VIRTUAL), static_cast<bool>(PRIMARY));
+        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
+        if (auto displayId = DISPLAY_ID::get()) {
+            ceDisplayArgs.setPhysical({*displayId, DisplayConnectionType::Internal});
+        } else {
+            ceDisplayArgs.setUseHwcVirtualDisplays(false);
+        }
+        ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor).build();
+
+        auto compositionDisplay =
+                compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                       ceDisplayArgs.build());
+
+        auto injector = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
+                                                  CONNECTION_TYPE::value, HWC_DISPLAY_ID_OPT::value,
+                                                  static_cast<bool>(PRIMARY));
 
         injector.setSecure(static_cast<bool>(SECURE));
         injector.setNativeWindow(test->mNativeWindow);
@@ -409,21 +504,20 @@
     }
 };
 
-template <hwc2_display_t hwcDisplayId, HWC2::DisplayType hwcDisplayType, typename DisplayVariant,
+template <HWDisplayId hwcDisplayId, DisplayType hwcDisplayType, typename DisplayVariant,
           typename PhysicalDisplay = void>
 struct HwcDisplayVariant {
     // The display id supplied by the HWC
-    static constexpr hwc2_display_t HWC_DISPLAY_ID = hwcDisplayId;
+    static constexpr HWDisplayId HWC_DISPLAY_ID = hwcDisplayId;
 
     // The HWC display type
-    static constexpr HWC2::DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
+    static constexpr DisplayType HWC_DISPLAY_TYPE = hwcDisplayType;
 
     // The HWC active configuration id
     static constexpr int HWC_ACTIVE_CONFIG_ID = 2001;
-    static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_NORMAL;
+    static constexpr PowerMode INIT_POWER_MODE = PowerMode::ON;
 
-    static void injectPendingHotplugEvent(DisplayTransactionTest* test,
-                                          HWC2::Connection connection) {
+    static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) {
         test->mFlinger.mutablePendingHotplugEvents().emplace_back(
                 HotplugEvent{HWC_DISPLAY_ID, connection});
     }
@@ -445,21 +539,43 @@
     // Called by tests to inject a HWC display setup
     static void injectHwcDisplay(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
                                 Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer,
-                    setPowerMode(HWC_DISPLAY_ID,
-                                 static_cast<Hwc2::IComposerClient::PowerMode>(INIT_POWER_MODE)))
+        EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE))
                 .WillOnce(Return(Error::NONE));
         injectHwcDisplayWithNoDefaultCapabilities(test);
     }
 
+    static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+            DisplayTransactionTest* test) {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+
+        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+                                     .setPhysical({*DisplayVariant::DISPLAY_ID::get(),
+                                                   PhysicalDisplay::CONNECTION_TYPE})
+                                     .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
+                                     .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
+                                     .setPowerAdvisor(&test->mPowerAdvisor)
+                                     .setName(std::string("Injected display for ") +
+                                              test_info->test_case_name() + "." + test_info->name())
+                                     .build();
+
+        return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                      ceDisplayArgs);
+    }
+
     static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getDisplayType(HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(static_cast<IComposerClient::DisplayType>(
-                                        HWC_DISPLAY_TYPE)),
-                                Return(Error::NONE)));
-        EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
+        constexpr auto CONNECTION_TYPE =
+                PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal
+                ? IComposerClient::DisplayConnectionType::INTERNAL
+                : IComposerClient::DisplayConnectionType::EXTERNAL;
+
+        EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
+                .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE)));
+
+        EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_))
+                .WillOnce(Return(hal::Error::NONE));
         EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
                 .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}),
                                 Return(Error::NONE)));
@@ -483,6 +599,10 @@
                     getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
                                         IComposerClient::Attribute::DPI_Y, _))
                 .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer,
+                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                        IComposerClient::Attribute::CONFIG_GROUP, _))
+                .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
 
         if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
             EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
@@ -506,12 +626,11 @@
 constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY =
         GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB;
 
-template <hwc2_display_t hwcDisplayId, typename PhysicalDisplay, int width, int height,
-          Critical critical>
+template <typename PhysicalDisplay, int width, int height, Critical critical>
 struct PhysicalDisplayVariant
       : DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height, critical, Async::FALSE,
                        Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
-        HwcDisplayVariant<hwcDisplayId, HWC2::DisplayType::Physical,
+        HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL,
                           DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height,
                                          critical, Async::FALSE, Secure::TRUE,
                                          PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>,
@@ -519,16 +638,20 @@
 
 template <bool hasIdentificationData>
 struct PrimaryDisplay {
+    static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal;
     static constexpr Primary PRIMARY = Primary::TRUE;
     static constexpr uint8_t PORT = 255;
+    static constexpr HWDisplayId HWC_DISPLAY_ID = 1001;
     static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
     static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid;
 };
 
 template <bool hasIdentificationData>
 struct ExternalDisplay {
+    static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External;
     static constexpr Primary PRIMARY = Primary::FALSE;
     static constexpr uint8_t PORT = 254;
+    static constexpr HWDisplayId HWC_DISPLAY_ID = 1002;
     static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
     static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
 };
@@ -536,19 +659,19 @@
 struct TertiaryDisplay {
     static constexpr Primary PRIMARY = Primary::FALSE;
     static constexpr uint8_t PORT = 253;
+    static constexpr HWDisplayId HWC_DISPLAY_ID = 1003;
     static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid;
 };
 
 // A primary display is a physical display that is critical
 using PrimaryDisplayVariant =
-        PhysicalDisplayVariant<1001, PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
+        PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>;
 
 // An external display is physical display that is not critical.
 using ExternalDisplayVariant =
-        PhysicalDisplayVariant<1002, ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
+        PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>;
 
-using TertiaryDisplayVariant =
-        PhysicalDisplayVariant<1003, TertiaryDisplay, 1600, 1200, Critical::FALSE>;
+using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>;
 
 // A virtual display not supported by the HWC.
 constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0;
@@ -562,6 +685,23 @@
 
     static void injectHwcDisplay(DisplayTransactionTest*) {}
 
+    static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+            DisplayTransactionTest* test) {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+
+        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+                                     .setPixels({Base::WIDTH, Base::HEIGHT})
+                                     .setIsSecure(static_cast<bool>(Base::SECURE))
+                                     .setPowerAdvisor(&test->mPowerAdvisor)
+                                     .setName(std::string("Injected display for ") +
+                                              test_info->test_case_name() + "." + test_info->name())
+                                     .build();
+
+        return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                      ceDisplayArgs);
+    }
+
     static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
     }
@@ -580,13 +720,40 @@
       : DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE, secure,
                        Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>,
         HwcDisplayVariant<
-                1010, HWC2::DisplayType::Virtual,
+                HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL,
                 DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
                                secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> {
     using Base = DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE,
                                 secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
     using Self = HwcVirtualDisplayVariant<width, height, secure>;
 
+    static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+            DisplayTransactionTest* test) {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+
+        auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+                                     .setUseHwcVirtualDisplays(false)
+                                     .setPixels({Base::WIDTH, Base::HEIGHT})
+                                     .setIsSecure(static_cast<bool>(Base::SECURE))
+                                     .setPowerAdvisor(&test->mPowerAdvisor)
+                                     .setName(std::string("Injected display for ") +
+                                              test_info->test_case_name() + "." + test_info->name())
+                                     .build();
+
+        auto compositionDisplay =
+                compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+                                                       ceDisplayArgs);
+        compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get());
+
+        // Insert display data so that the HWC thinks it created the virtual display.
+        if (const auto displayId = Base::DISPLAY_ID::get()) {
+            test->mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
+        }
+
+        return compositionDisplay;
+    }
+
     static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
         Base::setupNativeWindowSurfaceCreationCallExpectations(test);
         EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
@@ -608,7 +775,7 @@
     static void injectConfigChange(DisplayTransactionTest* test) {
         test->mFlinger.mutableHasWideColorDisplay() = false;
         test->mFlinger.mutableUseColorManagement() = false;
-        test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED;
+        test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
     }
 
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
@@ -628,7 +795,7 @@
     static void injectConfigChange(DisplayTransactionTest* test) {
         test->mFlinger.mutableUseColorManagement() = true;
         test->mFlinger.mutableHasWideColorDisplay() = true;
-        test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED;
+        test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
     }
 
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
@@ -883,8 +1050,8 @@
 
 TEST_F(DisplayTransactionTest, hotplugEnqueuesEventsForDisplayTransaction) {
     constexpr int currentSequenceId = 123;
-    constexpr hwc2_display_t hwcDisplayId1 = 456;
-    constexpr hwc2_display_t hwcDisplayId2 = 654;
+    constexpr HWDisplayId hwcDisplayId1 = 456;
+    constexpr HWDisplayId hwcDisplayId2 = 654;
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -907,8 +1074,8 @@
     // Invocation
 
     // Simulate two hotplug events (a connect and a disconnect)
-    mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, HWC2::Connection::Connected);
-    mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, HWC2::Connection::Disconnected);
+    mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, Connection::CONNECTED);
+    mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, Connection::DISCONNECTED);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -920,15 +1087,15 @@
     const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
     ASSERT_EQ(2u, pendingEvents.size());
     EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
-    EXPECT_EQ(HWC2::Connection::Connected, pendingEvents[0].connection);
+    EXPECT_EQ(Connection::CONNECTED, pendingEvents[0].connection);
     EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
-    EXPECT_EQ(HWC2::Connection::Disconnected, pendingEvents[1].connection);
+    EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
 }
 
 TEST_F(DisplayTransactionTest, hotplugDiscardsUnexpectedEvents) {
     constexpr int currentSequenceId = 123;
     constexpr int otherSequenceId = 321;
-    constexpr hwc2_display_t displayId = 456;
+    constexpr HWDisplayId displayId = 456;
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -950,7 +1117,7 @@
     // Invocation
 
     // Call with an unexpected sequence id
-    mFlinger.onHotplugReceived(otherSequenceId, displayId, HWC2::Connection::Invalid);
+    mFlinger.onHotplugReceived(otherSequenceId, displayId, Connection::INVALID);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -964,7 +1131,7 @@
 
 TEST_F(DisplayTransactionTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread) {
     constexpr int currentSequenceId = 123;
-    constexpr hwc2_display_t displayId1 = 456;
+    constexpr HWDisplayId displayId1 = 456;
 
     // --------------------------------------------------------------------
     // Note:
@@ -998,7 +1165,7 @@
     // Simulate a disconnect on a display id that is not connected. This should
     // be enqueued by onHotplugReceived(), and dequeued by
     // processDisplayHotplugEventsLocked(), but then ignored as invalid.
-    mFlinger.onHotplugReceived(currentSequenceId, displayId1, HWC2::Connection::Disconnected);
+    mFlinger.onHotplugReceived(currentSequenceId, displayId1, Connection::DISCONNECTED);
 
     // --------------------------------------------------------------------
     // Postconditions
@@ -1141,8 +1308,8 @@
     // Preconditions
 
     // vsync is enabled and available
-    mScheduler->mutablePrimaryHWVsyncEnabled() = true;
-    mScheduler->mutableHWVsyncAvailable() = true;
+    mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = true;
+    mFlinger.scheduler()->mutableHWVsyncAvailable() = true;
 
     // A display exists
     auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
@@ -1166,8 +1333,8 @@
     // Postconditions
 
     // vsyncs should be off and not available.
-    EXPECT_FALSE(mScheduler->mutablePrimaryHWVsyncEnabled());
-    EXPECT_FALSE(mScheduler->mutableHWVsyncAvailable());
+    EXPECT_FALSE(mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled());
+    EXPECT_FALSE(mFlinger.scheduler()->mutableHWVsyncAvailable());
 
     // The display should have been removed from the display map.
     EXPECT_FALSE(hasDisplayDevice(existing.token()));
@@ -1184,13 +1351,6 @@
  */
 class GetBestColorModeTest : public DisplayTransactionTest {
 public:
-    static constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
-
-    GetBestColorModeTest()
-          : DisplayTransactionTest(),
-            mInjector(FakeDisplayDeviceInjector(mFlinger, DEFAULT_DISPLAY_ID, false /* isVirtual */,
-                                                true /* isPrimary */)) {}
-
     void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
 
     void addHwcColorModesMapping(ui::ColorMode colorMode,
@@ -1203,21 +1363,12 @@
     void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
 
     void getBestColorMode() {
-        mInjector.setHwcColorModes(mHwcColorModes);
-        mInjector.setHasWideColorGamut(mHasWideColorGamut);
-        mInjector.setNativeWindow(mNativeWindow);
-
-        // Creating a DisplayDevice requires getting default dimensions from the
-        // native window.
-        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(1080 /* arbitrary */), Return(0)));
-        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
-                .WillRepeatedly(DoAll(SetArgPointee<1>(1920 /* arbitrary */), Return(0)));
-        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
-        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
-        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
-        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
-        auto displayDevice = mInjector.inject();
+        auto displayDevice =
+                injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+                    injector.setHwcColorModes(mHwcColorModes);
+                    injector.setHasWideColorGamut(mHasWideColorGamut);
+                    injector.setNativeWindow(mNativeWindow);
+                });
 
         displayDevice->getCompositionDisplay()
                 ->getDisplayColorProfile()
@@ -1234,7 +1385,6 @@
     ui::RenderIntent mInputRenderIntent;
     bool mHasWideColorGamut = false;
     std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
-    FakeDisplayDeviceInjector mInjector;
 };
 
 TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
@@ -1284,6 +1434,230 @@
 }
 
 /* ------------------------------------------------------------------------
+ * DisplayDevice::setProjection
+ */
+
+class DisplayDeviceSetProjectionTest : public DisplayTransactionTest {
+public:
+    static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080;  // arbitrary
+    static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary
+
+    static constexpr int32_t TRANSFORM_FLAGS_ROT_0 = 0;
+    static constexpr int32_t TRANSFORM_FLAGS_ROT_90 = HAL_TRANSFORM_ROT_90;
+    static constexpr int32_t TRANSFORM_FLAGS_ROT_180 = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V;
+    static constexpr int32_t TRANSFORM_FLAGS_ROT_270 =
+            HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
+
+    DisplayDeviceSetProjectionTest(ui::Size flingerDisplaySize, ui::Size hardwareDisplaySize,
+                                   ui::Rotation physicalOrientation)
+          : mFlingerDisplaySize(flingerDisplaySize),
+            mHardwareDisplaySize(hardwareDisplaySize),
+            mPhysicalOrientation(physicalOrientation),
+            mDisplayDevice(createDisplayDevice()) {}
+
+    sp<DisplayDevice> createDisplayDevice() {
+        return injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+            injector.setPhysicalOrientation(mPhysicalOrientation);
+        });
+    }
+
+    ui::Size SwapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
+
+    void setProjectionForRotation0() {
+        // A logical rotation of 0 uses the SurfaceFlinger display size
+        mDisplayDevice->setProjection(ui::ROTATION_0, Rect(mFlingerDisplaySize),
+                                      Rect(mFlingerDisplaySize));
+    }
+
+    void setProjectionForRotation90() {
+        // A logical rotation of 90 uses the SurfaceFlinger display size with
+        // the width/height swapped.
+        mDisplayDevice->setProjection(ui::ROTATION_90, Rect(SwapWH(mFlingerDisplaySize)),
+                                      Rect(SwapWH(mFlingerDisplaySize)));
+    }
+
+    void setProjectionForRotation180() {
+        // A logical rotation of 180 uses the SurfaceFlinger display size
+        mDisplayDevice->setProjection(ui::ROTATION_180, Rect(mFlingerDisplaySize),
+                                      Rect(mFlingerDisplaySize));
+    }
+
+    void setProjectionForRotation270() {
+        // A logical rotation of 270 uses the SurfaceFlinger display size with
+        // the width/height swapped.
+        mDisplayDevice->setProjection(ui::ROTATION_270, Rect(SwapWH(mFlingerDisplaySize)),
+                                      Rect(SwapWH(mFlingerDisplaySize)));
+    }
+
+    void expectStateForHardwareTransform0() {
+        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
+                                mHardwareDisplaySize.height),
+                  compositionState.transform);
+        EXPECT_EQ(TRANSFORM_FLAGS_ROT_0, compositionState.orientation);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
+        EXPECT_EQ(false, compositionState.needsFiltering);
+    }
+
+    void expectStateForHardwareTransform90() {
+        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
+                                mHardwareDisplaySize.height),
+                  compositionState.transform);
+        EXPECT_EQ(TRANSFORM_FLAGS_ROT_90, compositionState.orientation);
+        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.sourceClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
+        // For 90, the frame and viewport have the hardware display size width and height swapped
+        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
+        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
+        EXPECT_EQ(false, compositionState.needsFiltering);
+    }
+
+    void expectStateForHardwareTransform180() {
+        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
+                                mHardwareDisplaySize.height),
+                  compositionState.transform);
+        EXPECT_EQ(TRANSFORM_FLAGS_ROT_180, compositionState.orientation);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
+        EXPECT_EQ(false, compositionState.needsFiltering);
+    }
+
+    void expectStateForHardwareTransform270() {
+        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
+                                mHardwareDisplaySize.height),
+                  compositionState.transform);
+        EXPECT_EQ(TRANSFORM_FLAGS_ROT_270, compositionState.orientation);
+        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.sourceClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
+        // For 270, the frame and viewport have the hardware display size width and height swapped
+        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
+        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
+        EXPECT_EQ(false, compositionState.needsFiltering);
+    }
+
+    const ui::Size mFlingerDisplaySize;
+    const ui::Size mHardwareDisplaySize;
+    const ui::Rotation mPhysicalOrientation;
+    const sp<DisplayDevice> mDisplayDevice;
+};
+
+struct DisplayDeviceSetProjectionTest_Installed0 : public DisplayDeviceSetProjectionTest {
+    DisplayDeviceSetProjectionTest_Installed0()
+          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::ROTATION_0) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith0OutputRotation) {
+    setProjectionForRotation0();
+    expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith90OutputRotation) {
+    setProjectionForRotation90();
+    expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith180OutputRotation) {
+    setProjectionForRotation180();
+    expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith270OutputRotation) {
+    setProjectionForRotation270();
+    expectStateForHardwareTransform270();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed90 : public DisplayDeviceSetProjectionTest {
+    DisplayDeviceSetProjectionTest_Installed90()
+          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::ROTATION_90) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith0OutputRotation) {
+    setProjectionForRotation0();
+    expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith90OutputRotation) {
+    setProjectionForRotation90();
+    expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith180OutputRotation) {
+    setProjectionForRotation180();
+    expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith270OutputRotation) {
+    setProjectionForRotation270();
+    expectStateForHardwareTransform0();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed180 : public DisplayDeviceSetProjectionTest {
+    DisplayDeviceSetProjectionTest_Installed180()
+          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::ROTATION_180) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith0OutputRotation) {
+    setProjectionForRotation0();
+    expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith90OutputRotation) {
+    setProjectionForRotation90();
+    expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith180OutputRotation) {
+    setProjectionForRotation180();
+    expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith270OutputRotation) {
+    setProjectionForRotation270();
+    expectStateForHardwareTransform90();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed270 : public DisplayDeviceSetProjectionTest {
+    DisplayDeviceSetProjectionTest_Installed270()
+          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::ROTATION_270) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith0OutputRotation) {
+    setProjectionForRotation0();
+    expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith90OutputRotation) {
+    setProjectionForRotation90();
+    expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith180OutputRotation) {
+    setProjectionForRotation180();
+    expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith270OutputRotation) {
+    setProjectionForRotation270();
+    expectStateForHardwareTransform180();
+}
+
+/* ------------------------------------------------------------------------
  * SurfaceFlinger::getDisplayNativePrimaries
  */
 
@@ -1361,7 +1735,8 @@
 
     ui::DisplayPrimaries primaries;
     populateDummyDisplayNativePrimaries(primaries);
-    EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
+    EXPECT_EQ(NAME_NOT_FOUND,
+              mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries));
 
     // Check primaries argument wasn't modified in case of failure
     checkDummyDisplayNativePrimaries(primaries);
@@ -1397,6 +1772,9 @@
     // surfaces.
     injectFakeNativeWindowSurfaceFactory();
 
+    // A compositionengine::Display has already been created
+    auto compositionDisplay = Case::Display::injectCompositionDisplay(this);
+
     // --------------------------------------------------------------------
     // Call Expectations
 
@@ -1411,19 +1789,25 @@
     // Invocation
 
     DisplayDeviceState state;
-    state.displayId = static_cast<bool>(Case::Display::VIRTUAL) ? std::nullopt
-                                                                : Case::Display::DISPLAY_ID::get();
+    if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+        const auto displayId = Case::Display::DISPLAY_ID::get();
+        ASSERT_TRUE(displayId);
+        const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+        ASSERT_TRUE(hwcDisplayId);
+        state.physical = {*displayId, *connectionType, *hwcDisplayId};
+    }
+
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
 
-    auto device =
-            mFlinger.setupNewDisplayDeviceInternal(displayToken, Case::Display::DISPLAY_ID::get(),
-                                                   state, displaySurface, producer);
+    auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+                                                         displaySurface, producer);
 
     // --------------------------------------------------------------------
     // Postconditions
 
     ASSERT_TRUE(device != nullptr);
     EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
+    EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType());
     EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual());
     EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
     EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
@@ -1437,7 +1821,7 @@
     // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
     // remapped, and the test only ever sets up one config. If there were an error
     // looking up the remapped index, device->getActiveConfig() would be -1 instead.
-    EXPECT_EQ(0, device->getActiveConfig());
+    EXPECT_EQ(0, device->getActiveConfig().value());
     EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
               device->getSupportedPerFrameMetadata());
 }
@@ -1455,14 +1839,7 @@
 }
 
 TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) {
-    using Case = HwcVirtualDisplayCase;
-
-    // Insert display data so that the HWC thinks it created the virtual display.
-    const auto displayId = Case::Display::DISPLAY_ID::get();
-    ASSERT_TRUE(displayId);
-    mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
-
-    setupNewDisplayDeviceInternalTest<Case>();
+    setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>();
 }
 
 TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) {
@@ -1587,21 +1964,26 @@
     EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
     EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
 
+    std::optional<DisplayDeviceState::Physical> expectedPhysical;
+    if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+        const auto displayId = Case::Display::DISPLAY_ID::get();
+        ASSERT_TRUE(displayId);
+        const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
+        ASSERT_TRUE(hwcDisplayId);
+        expectedPhysical = {*displayId, *connectionType, *hwcDisplayId};
+    }
+
     // The display should have been set up in the current display state
     ASSERT_TRUE(hasCurrentDisplayState(displayToken));
     const auto& current = getCurrentDisplayState(displayToken);
     EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual());
-    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL) ? std::nullopt
-                                                        : Case::Display::DISPLAY_ID::get(),
-              current.displayId);
+    EXPECT_EQ(expectedPhysical, current.physical);
 
     // The display should have been set up in the drawing display state
     ASSERT_TRUE(hasDrawingDisplayState(displayToken));
     const auto& draw = getDrawingDisplayState(displayToken);
     EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual());
-    EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL) ? std::nullopt
-                                                        : Case::Display::DISPLAY_ID::get(),
-              draw.displayId);
+    EXPECT_EQ(expectedPhysical, draw.physical);
 }
 
 template <typename Case>
@@ -1632,7 +2014,7 @@
     setupCommonPreconditions<Case>();
 
     // A hotplug connect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, HWC2::Connection::Connected);
+    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
 
     // --------------------------------------------------------------------
     // Call Expectations
@@ -1668,7 +2050,7 @@
     setupCommonPreconditions<Case>();
 
     // A hotplug connect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, HWC2::Connection::Connected);
+    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
 
     // --------------------------------------------------------------------
     // Invocation
@@ -1690,7 +2072,7 @@
     setupCommonPreconditions<Case>();
 
     // A hotplug disconnect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, HWC2::Connection::Disconnected);
+    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
 
     // The display is already completely set up.
     Case::Display::injectHwcDisplay(this);
@@ -1771,11 +2153,11 @@
     ignoresHotplugConnectCommon<SimpleExternalDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest, processHotplugDisconnectPrimaryDisplay) {
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) {
     processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>();
 }
 
-TEST_F(HandleTransactionLockedTest, processHotplugDisconnectExternalDisplay) {
+TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) {
     processesHotplugDisconnectCommon<SimpleExternalDisplayCase>();
 }
 
@@ -1788,9 +2170,9 @@
     setupCommonPreconditions<Case>();
 
     // A hotplug connect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, HWC2::Connection::Connected);
+    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
     // A hotplug disconnect event is also enqueued for the same display
-    Case::Display::injectPendingHotplugEvent(this, HWC2::Connection::Disconnected);
+    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
 
     // --------------------------------------------------------------------
     // Call Expectations
@@ -1836,9 +2218,9 @@
     existing.inject();
 
     // A hotplug disconnect event is enqueued for a display
-    Case::Display::injectPendingHotplugEvent(this, HWC2::Connection::Disconnected);
+    Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
     // A hotplug connect event is also enqueued for the same display
-    Case::Display::injectPendingHotplugEvent(this, HWC2::Connection::Connected);
+    Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED);
 
     // --------------------------------------------------------------------
     // Call Expectations
@@ -2050,8 +2432,8 @@
 TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
-    constexpr int oldTransform = 0;
-    constexpr int newTransform = 2;
+    constexpr ui::Rotation oldTransform = ui::ROTATION_0;
+    constexpr ui::Rotation newTransform = ui::ROTATION_180;
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -2424,7 +2806,7 @@
 
 TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) {
     using Case = SimplePrimaryDisplayCase;
-    constexpr int initialOrientation = 180;
+    constexpr ui::Rotation initialOrientation = ui::ROTATION_180;
     const Rect initialFrame = {1, 2, 3, 4};
     const Rect initialViewport = {5, 6, 7, 8};
 
@@ -2468,8 +2850,8 @@
 
 TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) {
     using Case = SimplePrimaryDisplayCase;
-    constexpr int initialOrientation = 90;
-    constexpr int desiredOrientation = 180;
+    constexpr ui::Rotation initialOrientation = ui::ROTATION_90;
+    constexpr ui::Rotation desiredOrientation = ui::ROTATION_180;
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -2715,7 +3097,7 @@
     // processing.
     EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
 
-    EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
+    EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
 
     // --------------------------------------------------------------------
     // Invocation
@@ -2731,7 +3113,7 @@
     // The layer stack state should be set to zero
     EXPECT_EQ(0u, primaryDisplayState.layerStack);
     // The orientation state should be set to zero
-    EXPECT_EQ(0, primaryDisplayState.orientation);
+    EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
 
     // The frame state should be set to INVALID
     EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.frame);
@@ -2743,10 +3125,10 @@
     EXPECT_EQ(0u, primaryDisplayState.width);
     EXPECT_EQ(0u, primaryDisplayState.height);
 
-    // The display should be set to HWC_POWER_MODE_NORMAL
+    // The display should be set to PowerMode::ON
     ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token()));
     auto displayDevice = primaryDisplay.mutableDisplayDevice();
-    EXPECT_EQ(HWC_POWER_MODE_NORMAL, displayDevice->getPowerMode());
+    EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode());
 
     // The display refresh period should be set in the frame tracker.
     FrameStats stats;
@@ -2778,8 +3160,8 @@
 
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>(
-                                        {Hwc2::DisplayCapability::DOZE})),
+                .WillOnce(DoAll(SetArgPointee<1>(
+                                        std::vector<DisplayCapability>({DisplayCapability::DOZE})),
                                 Return(Error::NONE)));
     }
 };
@@ -2795,7 +3177,7 @@
 
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::DisplayCapability>({})),
+                .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})),
                                 Return(Error::NONE)));
     }
 };
@@ -2869,7 +3251,7 @@
 // selected subset which provides complete test coverage of the implementation.
 // --------------------------------------------------------------------
 
-template <int initialPowerMode, int targetPowerMode>
+template <PowerMode initialPowerMode, PowerMode targetPowerMode>
 struct TransitionVariantCommon {
     static constexpr auto INITIAL_POWER_MODE = initialPowerMode;
     static constexpr auto TARGET_POWER_MODE = targetPowerMode;
@@ -2877,8 +3259,7 @@
     static void verifyPostconditions(DisplayTransactionTest*) {}
 };
 
-struct TransitionOffToOnVariant
-      : public TransitionVariantCommon<HWC_POWER_MODE_OFF, HWC_POWER_MODE_NORMAL> {
+struct TransitionOffToOnVariant : public TransitionVariantCommon<PowerMode::OFF, PowerMode::ON> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
@@ -2894,7 +3275,7 @@
 };
 
 struct TransitionOffToDozeSuspendVariant
-      : public TransitionVariantCommon<HWC_POWER_MODE_OFF, HWC_POWER_MODE_DOZE_SUSPEND> {
+      : public TransitionVariantCommon<PowerMode::OFF, PowerMode::DOZE_SUSPEND> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND);
@@ -2908,8 +3289,7 @@
     }
 };
 
-struct TransitionOnToOffVariant
-      : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_OFF> {
+struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
@@ -2923,7 +3303,7 @@
 };
 
 struct TransitionDozeSuspendToOffVariant
-      : public TransitionVariantCommon<HWC_POWER_MODE_DOZE_SUSPEND, HWC_POWER_MODE_OFF> {
+      : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
@@ -2935,8 +3315,7 @@
     }
 };
 
-struct TransitionOnToDozeVariant
-      : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_DOZE> {
+struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
@@ -2945,7 +3324,7 @@
 };
 
 struct TransitionDozeSuspendToDozeVariant
-      : public TransitionVariantCommon<HWC_POWER_MODE_DOZE_SUSPEND, HWC_POWER_MODE_DOZE> {
+      : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
@@ -2954,8 +3333,7 @@
     }
 };
 
-struct TransitionDozeToOnVariant
-      : public TransitionVariantCommon<HWC_POWER_MODE_DOZE, HWC_POWER_MODE_NORMAL> {
+struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
@@ -2964,7 +3342,7 @@
 };
 
 struct TransitionDozeSuspendToOnVariant
-      : public TransitionVariantCommon<HWC_POWER_MODE_DOZE_SUSPEND, HWC_POWER_MODE_NORMAL> {
+      : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
@@ -2974,7 +3352,7 @@
 };
 
 struct TransitionOnToDozeSuspendVariant
-      : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_DOZE_SUSPEND> {
+      : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test);
@@ -2984,7 +3362,7 @@
 };
 
 struct TransitionOnToUnknownVariant
-      : public TransitionVariantCommon<HWC_POWER_MODE_NORMAL, HWC_POWER_MODE_LEET> {
+      : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> {
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test);
@@ -3009,7 +3387,7 @@
     using DispSync = DispSyncVariant;
     using Transition = TransitionVariant;
 
-    static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, int mode) {
+    static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) {
         Display::injectHwcDisplayWithNoDefaultCapabilities(test);
         auto display = Display::makeFakeExistingDisplayInjector(test);
         display.inject();
@@ -3018,20 +3396,21 @@
     }
 
     static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
-        test->mScheduler->mutablePrimaryHWVsyncEnabled() = enabled;
+        test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled;
     }
 
     static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
         EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1);
     }
 
-    static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test, int mode) {
+    static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
+                                                        PowerMode mode) {
         EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true));
-        EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, mode)).Times(1);
+        EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode)))
+                .Times(1);
     }
 
-    static void setupComposerCallExpectations(DisplayTransactionTest* test,
-                                              IComposerClient::PowerMode mode) {
+    static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) {
         // Any calls to get the active config will return a default value.
         EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _))
                 .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID),
@@ -3073,14 +3452,14 @@
     void transitionDisplayCommon();
 };
 
-template <int PowerMode>
+template <PowerMode PowerMode>
 struct PowerModeInitialVSyncEnabled : public std::false_type {};
 
 template <>
-struct PowerModeInitialVSyncEnabled<HWC_POWER_MODE_NORMAL> : public std::true_type {};
+struct PowerModeInitialVSyncEnabled<PowerMode::ON> : public std::true_type {};
 
 template <>
-struct PowerModeInitialVSyncEnabled<HWC_POWER_MODE_DOZE> : public std::true_type {};
+struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {};
 
 template <typename Case>
 void SetPowerModeInternalTest::transitionDisplayCommon() {
@@ -3123,18 +3502,18 @@
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.inject();
 
-    // The display is already set to HWC_POWER_MODE_NORMAL
-    display.mutableDisplayDevice()->setPowerMode(HWC_POWER_MODE_NORMAL);
+    // The display is already set to PowerMode::ON
+    display.mutableDisplayDevice()->setPowerMode(PowerMode::ON);
 
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_NORMAL);
+    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON);
 
     // --------------------------------------------------------------------
     // Postconditions
 
-    EXPECT_EQ(HWC_POWER_MODE_NORMAL, display.mutableDisplayDevice()->getPowerMode());
+    EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
 }
 
 TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) {
@@ -3153,18 +3532,18 @@
     auto display = Case::Display::makeFakeExistingDisplayInjector(this);
     display.inject();
 
-    // The display is set to HWC_POWER_MODE_NORMAL
-    getDisplayDevice(display.token())->setPowerMode(HWC_POWER_MODE_NORMAL);
+    // The display is set to PowerMode::ON
+    getDisplayDevice(display.token())->setPowerMode(PowerMode::ON);
 
     // --------------------------------------------------------------------
     // Invocation
 
-    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), HWC_POWER_MODE_OFF);
+    mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF);
 
     // --------------------------------------------------------------------
     // Postconditions
 
-    EXPECT_EQ(HWC_POWER_MODE_NORMAL, display.mutableDisplayDevice()->getPowerMode());
+    EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode());
 }
 
 TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) {
@@ -3249,3 +3628,6 @@
 
 } // namespace
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index dbd9b84..b90b566 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -19,13 +19,12 @@
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
 #include <log/log.h>
-
 #include <utils/Errors.h>
 
 #include "AsyncCallRecorder.h"
 #include "Scheduler/EventThread.h"
+#include "Scheduler/HwcStrongTypes.h"
 
 using namespace std::chrono_literals;
 using namespace std::placeholders;
@@ -34,6 +33,7 @@
 using testing::Invoke;
 
 namespace android {
+
 namespace {
 
 constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = 111;
@@ -42,10 +42,13 @@
 
 class MockVSyncSource : public VSyncSource {
 public:
+    const char* getName() const override { return "test"; }
+
     MOCK_METHOD1(setVSyncEnabled, void(bool));
     MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
     MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
     MOCK_METHOD1(pauseVsyncCallback, void(bool));
+    MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
 } // namespace
@@ -54,8 +57,7 @@
 protected:
     class MockEventThreadConnection : public EventThreadConnection {
     public:
-        MockEventThreadConnection(android::impl::EventThread* eventThread,
-                                  ResyncCallback&& resyncCallback,
+        MockEventThreadConnection(impl::EventThread* eventThread, ResyncCallback&& resyncCallback,
                                   ISurfaceComposer::ConfigChanged configChanged)
               : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {}
         MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
@@ -67,7 +69,7 @@
     EventThreadTest();
     ~EventThreadTest() override;
 
-    void createThread();
+    void createThread(std::unique_ptr<VSyncSource>);
     sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
                                                    ISurfaceComposer::ConfigChanged configChanged);
 
@@ -82,7 +84,8 @@
     void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                 bool expectedConnected);
     void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
-                                                      int32_t expectedConfigId);
+                                                      int32_t expectedConfigId,
+                                                      nsecs_t expectedVsyncPeriod);
 
     AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
     AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
@@ -91,9 +94,9 @@
     AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
     ConnectionEventRecorder mConnectionEventCallRecorder{0};
 
-    MockVSyncSource mVSyncSource;
+    MockVSyncSource* mVSyncSource;
     VSyncSource::Callback* mCallback = nullptr;
-    std::unique_ptr<android::impl::EventThread> mThread;
+    std::unique_ptr<impl::EventThread> mThread;
     sp<MockEventThreadConnection> mConnection;
 };
 
@@ -102,16 +105,19 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    EXPECT_CALL(mVSyncSource, setVSyncEnabled(_))
+    auto vsyncSource = std::make_unique<MockVSyncSource>();
+    mVSyncSource = vsyncSource.get();
+
+    EXPECT_CALL(*mVSyncSource, setVSyncEnabled(_))
             .WillRepeatedly(Invoke(mVSyncSetEnabledCallRecorder.getInvocable()));
 
-    EXPECT_CALL(mVSyncSource, setCallback(_))
+    EXPECT_CALL(*mVSyncSource, setCallback(_))
             .WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable()));
 
-    EXPECT_CALL(mVSyncSource, setPhaseOffset(_))
+    EXPECT_CALL(*mVSyncSource, setPhaseOffset(_))
             .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable()));
 
-    createThread();
+    createThread(std::move(vsyncSource));
     mConnection = createConnection(mConnectionEventCallRecorder,
                                    ISurfaceComposer::eConfigChangedDispatch);
 
@@ -129,11 +135,9 @@
     EXPECT_TRUE(!mVSyncSetCallbackCallRecorder.waitForUnexpectedCall().has_value());
 }
 
-void EventThreadTest::createThread() {
-    mThread =
-            std::make_unique<android::impl::EventThread>(&mVSyncSource,
-                                                         mInterceptVSyncCallRecorder.getInvocable(),
-                                                         "unit-test-event-thread");
+void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) {
+    mThread = std::make_unique<impl::EventThread>(std::move(source),
+                                                  mInterceptVSyncCallRecorder.getInvocable());
 
     // EventThread should register itself as VSyncSource callback.
     mCallback = expectVSyncSetCallbackCallReceived();
@@ -205,13 +209,15 @@
 }
 
 void EventThreadTest::expectConfigChangedEventReceivedByConnection(
-        PhysicalDisplayId expectedDisplayId, int32_t expectedConfigId) {
+        PhysicalDisplayId expectedDisplayId, int32_t expectedConfigId,
+        nsecs_t expectedVsyncPeriod) {
     auto args = mConnectionEventCallRecorder.waitForCall();
     ASSERT_TRUE(args.has_value());
     const auto& event = std::get<0>(args.value());
     EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, event.header.type);
     EXPECT_EQ(expectedDisplayId, event.header.displayId);
     EXPECT_EQ(expectedConfigId, event.config.configId);
+    EXPECT_EQ(expectedVsyncPeriod, event.config.vsyncPeriod);
 }
 
 namespace {
@@ -252,14 +258,14 @@
 
     // Use the received callback to signal a first vsync event.
     // The interceptor should receive the event, as well as the connection.
-    mCallback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123, 456);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // Use the received callback to signal a second vsync event.
     // The interceptor should receive the event, but the the connection should
     // not as it was only interested in the first.
-    mCallback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456, 123);
     expectInterceptCallReceived(456);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
@@ -293,7 +299,7 @@
     // Send a vsync event. EventThread should then make a call to the
     // interceptor, and the second connection. The first connection should not
     // get the event.
-    mCallback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123, 456);
     expectInterceptCallReceived(123);
     EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value());
     expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123,
@@ -308,17 +314,17 @@
 
     // Send a vsync event. EventThread should then make a call to the
     // interceptor, and the connection.
-    mCallback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123, 456);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection(123, 1u);
 
     // A second event should go to the same places.
-    mCallback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456, 123);
     expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection(456, 2u);
 
     // A third event should go to the same places.
-    mCallback->onVSyncEvent(789);
+    mCallback->onVSyncEvent(789, 777);
     expectInterceptCallReceived(789);
     expectVsyncEventReceivedByConnection(789, 3u);
 }
@@ -330,22 +336,22 @@
     expectVSyncSetEnabledCallReceived(true);
 
     // The first event will be seen by the interceptor, and not the connection.
-    mCallback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123, 456);
     expectInterceptCallReceived(123);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
     // The second event will be seen by the interceptor and the connection.
-    mCallback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456, 123);
     expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection(456, 2u);
 
     // The third event will be seen by the interceptor, and not the connection.
-    mCallback->onVSyncEvent(789);
+    mCallback->onVSyncEvent(789, 777);
     expectInterceptCallReceived(789);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
     // The fourth event will be seen by the interceptor and the connection.
-    mCallback->onVSyncEvent(101112);
+    mCallback->onVSyncEvent(101112, 7847);
     expectInterceptCallReceived(101112);
     expectVsyncEventReceivedByConnection(101112, 4u);
 }
@@ -360,7 +366,7 @@
     mConnection = nullptr;
 
     // The first event will be seen by the interceptor, and not the connection.
-    mCallback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123, 456);
     expectInterceptCallReceived(123);
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value());
 
@@ -380,13 +386,13 @@
 
     // The first event will be seen by the interceptor, and by the connection,
     // which then returns an error.
-    mCallback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123, 456);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
     // A subsequent event will be seen by the interceptor and not by the
     // connection.
-    mCallback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456, 123);
     expectInterceptCallReceived(456);
     EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value());
 
@@ -394,6 +400,34 @@
     expectVSyncSetEnabledCallReceived(false);
 }
 
+TEST_F(EventThreadTest, tracksEventConnections) {
+    EXPECT_EQ(1, mThread->getEventThreadConnectionCount());
+    ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
+    sp<MockEventThreadConnection> errorConnection =
+            createConnection(errorConnectionEventRecorder,
+                             ISurfaceComposer::eConfigChangedSuppress);
+    mThread->setVsyncRate(1, errorConnection);
+    EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+    ConnectionEventRecorder secondConnectionEventRecorder{0};
+    sp<MockEventThreadConnection> secondConnection =
+            createConnection(secondConnectionEventRecorder,
+                             ISurfaceComposer::eConfigChangedSuppress);
+    mThread->setVsyncRate(1, secondConnection);
+    EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
+
+    // EventThread should enable vsync callbacks.
+    expectVSyncSetEnabledCallReceived(true);
+
+    // The first event will be seen by the interceptor, and by the connection,
+    // which then returns an error.
+    mCallback->onVSyncEvent(123, 456);
+    expectInterceptCallReceived(123);
+    expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
+    expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
+                                         1u);
+    EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+}
+
 TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
     ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
     sp<MockEventThreadConnection> errorConnection =
@@ -406,13 +440,13 @@
 
     // The first event will be seen by the interceptor, and by the connection,
     // which then returns an non-fatal error.
-    mCallback->onVSyncEvent(123);
+    mCallback->onVSyncEvent(123, 456);
     expectInterceptCallReceived(123);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
 
     // A subsequent event will be seen by the interceptor, and by the connection,
     // which still then returns an non-fatal error.
-    mCallback->onVSyncEvent(456);
+    mCallback->onVSyncEvent(456, 123);
     expectInterceptCallReceived(456);
     expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u);
 
@@ -446,18 +480,18 @@
 }
 
 TEST_F(EventThreadTest, postConfigChangedPrimary) {
-    mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 7);
-    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7);
+    mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(7), 16666666);
+    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666);
 }
 
 TEST_F(EventThreadTest, postConfigChangedExternal) {
-    mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, 5);
-    expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5);
+    mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, HwcConfigIndexType(5), 16666666);
+    expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666);
 }
 
 TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
-    mThread->onConfigChanged(DISPLAY_ID_64BIT, 7);
-    expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7);
+    mThread->onConfigChanged(DISPLAY_ID_64BIT, HwcConfigIndexType(7), 16666666);
+    expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666);
 }
 
 TEST_F(EventThreadTest, suppressConfigChanged) {
@@ -466,8 +500,8 @@
             createConnection(suppressConnectionEventRecorder,
                              ISurfaceComposer::eConfigChangedSuppress);
 
-    mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 9);
-    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9);
+    mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(9), 16666666);
+    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
 
     auto args = suppressConnectionEventRecorder.waitForCall();
     ASSERT_FALSE(args.has_value());
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
index 1d75011..b50ddf5 100644
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -20,42 +20,21 @@
 
 #include "Scheduler/PhaseOffsets.h"
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
 
-using RefreshRateType = RefreshRateConfigs::RefreshRateType;
+struct FakePhaseOffsets : PhaseConfiguration {
+    static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
 
-class FakePhaseOffsets : public android::scheduler::PhaseOffsets {
-    nsecs_t FAKE_PHASE_OFFSET_NS = 0;
+    Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
 
-public:
-    FakePhaseOffsets() = default;
-    ~FakePhaseOffsets() = default;
-
-    nsecs_t getCurrentAppOffset() override { return FAKE_PHASE_OFFSET_NS; }
-    nsecs_t getCurrentSfOffset() override { return FAKE_PHASE_OFFSET_NS; }
-
-    PhaseOffsets::Offsets getOffsetsForRefreshRate(
-            RefreshRateType /*refreshRateType*/) const override {
-        return getCurrentOffsets();
+    Offsets getCurrentOffsets() const override {
+        return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+                {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+                {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
     }
 
-    // Returns early, early GL, and late offsets for Apps and SF.
-    PhaseOffsets::Offsets getCurrentOffsets() const override {
-        return Offsets{{RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
-                       {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
-                       {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
-    }
-
-    // This function should be called when the device is switching between different
-    // refresh rates, to properly update the offsets.
-    void setRefreshRateType(RefreshRateType /*refreshRateType*/) override {}
-
-    nsecs_t getOffsetThresholdForNextVsync() const override { return FAKE_PHASE_OFFSET_NS; }
-
-    // Returns current offsets in human friendly format.
-    void dump(std::string& /*result*/) const override {}
+    void setRefreshRateFps(float) override {}
+    void dump(std::string&) const override {}
 };
 
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
new file mode 100644
index 0000000..a119e27
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
@@ -0,0 +1,384 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <FrameTracer/FrameTracer.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <perfetto/trace/trace.pb.h>
+
+#include "libsurfaceflinger_unittest_main.h"
+
+using namespace google::protobuf;
+
+namespace android {
+namespace {
+
+class FrameTracerTest : public testing::Test {
+public:
+    FrameTracerTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+        // Need to initialize tracing in process for testing, and only once per test suite.
+        static bool wasInitialized = false;
+        if (!wasInitialized) {
+            perfetto::TracingInitArgs args;
+            args.backends = perfetto::kInProcessBackend;
+            perfetto::Tracing::Initialize(args);
+            wasInitialized = true;
+        }
+    }
+
+    ~FrameTracerTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    void SetUp() override {
+        mFrameTracer = std::make_unique<FrameTracer>();
+        mFrameTracer->registerDataSource();
+    }
+
+    void TearDown() override { mFrameTracer.reset(); }
+
+    // Each tracing session can be used for a single block of Start -> Stop.
+    static std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest() {
+        perfetto::TraceConfig cfg;
+        cfg.set_duration_ms(500);
+        cfg.add_buffers()->set_size_kb(1024);
+        auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+        ds_cfg->set_name(FrameTracer::kFrameTracerDataSource);
+
+        auto tracingSession = perfetto::Tracing::NewTrace(perfetto::kInProcessBackend);
+        tracingSession->Setup(cfg);
+        return tracingSession;
+    }
+
+    std::vector<perfetto::protos::TracePacket> readGraphicsFramePacketsBlocking(
+            perfetto::TracingSession* tracingSession) {
+        std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+        perfetto::protos::Trace trace;
+        EXPECT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
+
+        std::vector<perfetto::protos::TracePacket> packets;
+        for (const auto& packet : trace.packet()) {
+            if (!packet.has_graphics_frame_event()) {
+                continue;
+            }
+            packets.emplace_back(packet);
+        }
+        return packets;
+    }
+
+    std::unique_ptr<FrameTracer> mFrameTracer;
+    FenceToFenceTimeMap fenceFactory;
+};
+
+TEST_F(FrameTracerTest, traceNewLayerStartsTrackingLayerWhenTracing) {
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
+
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 5;
+    mFrameTracer->traceNewLayer(layerId, layerName);
+
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
+
+    auto tracingSession = getTracingSessionForTest();
+    tracingSession->StartBlocking();
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
+    mFrameTracer->traceNewLayer(layerId, layerName);
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 1\n");
+    tracingSession->StopBlocking();
+}
+
+TEST_F(FrameTracerTest, onDestroyRemovesTheTrackedLayer) {
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
+
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 5;
+    const int32_t secondlayerId = 6;
+
+    auto tracingSession = getTracingSessionForTest();
+    tracingSession->StartBlocking();
+    mFrameTracer->traceNewLayer(layerId, layerName);
+    mFrameTracer->traceNewLayer(secondlayerId, layerName);
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 2\n");
+    tracingSession->StopBlocking();
+
+    mFrameTracer->onDestroy(layerId);
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 1\n");
+    mFrameTracer->onDestroy(layerId);
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 1\n");
+    mFrameTracer->onDestroy(secondlayerId);
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
+}
+
+TEST_F(FrameTracerTest, canTraceAfterAddingLayer) {
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 1;
+    const uint32_t bufferID = 2;
+    const uint64_t frameNumber = 3;
+    const nsecs_t timestamp = 4;
+    const nsecs_t duration = 5;
+    const auto type = FrameTracer::FrameEvent::POST;
+
+    {
+        auto tracingSession = getTracingSessionForTest();
+
+        tracingSession->StartBlocking();
+        mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration);
+        // Create second trace packet to finalize the previous one.
+        mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+        tracingSession->StopBlocking();
+
+        auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+        EXPECT_EQ(packets.size(), 0);
+    }
+
+    {
+        auto tracingSession = getTracingSessionForTest();
+
+        tracingSession->StartBlocking();
+        mFrameTracer->traceNewLayer(layerId, layerName);
+        mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration);
+        // Create second trace packet to finalize the previous one.
+        mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+        tracingSession->StopBlocking();
+
+        auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+        EXPECT_EQ(packets.size(), 1);
+
+        const auto& packet = packets[0];
+        ASSERT_TRUE(packet.has_timestamp());
+        EXPECT_EQ(packet.timestamp(), timestamp);
+        ASSERT_TRUE(packet.has_graphics_frame_event());
+        const auto& frame_event = packet.graphics_frame_event();
+        ASSERT_TRUE(frame_event.has_buffer_event());
+        const auto& buffer_event = frame_event.buffer_event();
+        ASSERT_TRUE(buffer_event.has_buffer_id());
+        EXPECT_EQ(buffer_event.buffer_id(), bufferID);
+        ASSERT_TRUE(buffer_event.has_frame_number());
+        EXPECT_EQ(buffer_event.frame_number(), frameNumber);
+        ASSERT_TRUE(buffer_event.has_type());
+        EXPECT_EQ(buffer_event.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type));
+        ASSERT_TRUE(buffer_event.has_duration_ns());
+        EXPECT_EQ(buffer_event.duration_ns(), duration);
+    }
+}
+
+TEST_F(FrameTracerTest, traceFenceTriggersOnNextTraceAfterFenceFired) {
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 5;
+    const uint32_t bufferID = 4;
+    const uint64_t frameNumber = 3;
+    const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
+
+    {
+        auto fenceTime = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+        fenceFactory.signalAllForTest(Fence::NO_FENCE, Fence::SIGNAL_TIME_PENDING);
+        auto tracingSession = getTracingSessionForTest();
+        tracingSession->StartBlocking();
+        // Trace.
+        mFrameTracer->traceNewLayer(layerId, layerName);
+        mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type);
+        // Create extra trace packet to (hopefully not) trigger and finalize the fence packet.
+        mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+        tracingSession->StopBlocking();
+
+        auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+        EXPECT_EQ(packets.size(), 0);
+    }
+
+    {
+        auto fenceTime = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+        auto tracingSession = getTracingSessionForTest();
+        tracingSession->StartBlocking();
+        mFrameTracer->traceNewLayer(layerId, layerName);
+        mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type);
+        const nsecs_t timestamp = systemTime();
+        fenceFactory.signalAllForTest(Fence::NO_FENCE, timestamp);
+        // Create extra trace packet to trigger and finalize fence trace packets.
+        mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+        tracingSession->StopBlocking();
+
+        auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+        EXPECT_EQ(packets.size(), 2); // Two packets because of the extra trace made above.
+
+        const auto& packet = packets[1];
+        ASSERT_TRUE(packet.has_timestamp());
+        EXPECT_EQ(packet.timestamp(), timestamp);
+        ASSERT_TRUE(packet.has_graphics_frame_event());
+        const auto& frame_event = packet.graphics_frame_event();
+        ASSERT_TRUE(frame_event.has_buffer_event());
+        const auto& buffer_event = frame_event.buffer_event();
+        ASSERT_TRUE(buffer_event.has_buffer_id());
+        EXPECT_EQ(buffer_event.buffer_id(), bufferID);
+        ASSERT_TRUE(buffer_event.has_frame_number());
+        EXPECT_EQ(buffer_event.frame_number(), frameNumber);
+        ASSERT_TRUE(buffer_event.has_type());
+        EXPECT_EQ(buffer_event.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type));
+        EXPECT_FALSE(buffer_event.has_duration_ns());
+    }
+}
+
+TEST_F(FrameTracerTest, traceFenceWithStartTimeAfterSignalTime_ShouldHaveNoDuration) {
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 5;
+    const uint32_t bufferID = 4;
+    const uint64_t frameNumber = 3;
+    const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
+
+    auto tracingSession = getTracingSessionForTest();
+
+    tracingSession->StartBlocking();
+    mFrameTracer->traceNewLayer(layerId, layerName);
+
+    // traceFence called after fence signalled.
+    const nsecs_t signalTime1 = systemTime();
+    const nsecs_t startTime1 = signalTime1 + 100000;
+    auto fence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime1);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence1, type, startTime1);
+
+    // traceFence called before fence signalled.
+    const nsecs_t signalTime2 = systemTime();
+    const nsecs_t startTime2 = signalTime2 + 100000;
+    auto fence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence2, type, startTime2);
+    fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime2);
+
+    // Create extra trace packet to trigger and finalize fence trace packets.
+    mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+    tracingSession->StopBlocking();
+
+    auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+    EXPECT_EQ(packets.size(), 2);
+
+    const auto& packet1 = packets[0];
+    ASSERT_TRUE(packet1.has_timestamp());
+    EXPECT_EQ(packet1.timestamp(), signalTime1);
+    ASSERT_TRUE(packet1.has_graphics_frame_event());
+    ASSERT_TRUE(packet1.graphics_frame_event().has_buffer_event());
+    ASSERT_FALSE(packet1.graphics_frame_event().buffer_event().has_duration_ns());
+
+    const auto& packet2 = packets[1];
+    ASSERT_TRUE(packet2.has_timestamp());
+    EXPECT_EQ(packet2.timestamp(), signalTime2);
+    ASSERT_TRUE(packet2.has_graphics_frame_event());
+    ASSERT_TRUE(packet2.graphics_frame_event().has_buffer_event());
+    ASSERT_FALSE(packet2.graphics_frame_event().buffer_event().has_duration_ns());
+}
+
+TEST_F(FrameTracerTest, traceFenceOlderThanDeadline_ShouldBeIgnored) {
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 5;
+    const uint32_t bufferID = 4;
+    const uint64_t frameNumber = 3;
+    const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
+    const nsecs_t signalTime = systemTime() - FrameTracer::kFenceSignallingDeadline;
+
+    auto tracingSession = getTracingSessionForTest();
+    auto fence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+    tracingSession->StartBlocking();
+    mFrameTracer->traceNewLayer(layerId, layerName);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence, type);
+    fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime);
+    // Create extra trace packet to trigger and finalize any previous fence packets.
+    mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+    tracingSession->StopBlocking();
+
+    auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+    EXPECT_EQ(packets.size(), 0);
+}
+
+TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) {
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 5;
+    const uint32_t bufferID = 4;
+    const uint64_t frameNumber = 3;
+    const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
+    const nsecs_t duration = 1234;
+
+    auto tracingSession = getTracingSessionForTest();
+
+    tracingSession->StartBlocking();
+    mFrameTracer->traceNewLayer(layerId, layerName);
+
+    // traceFence called after fence signalled.
+    const nsecs_t signalTime1 = systemTime();
+    const nsecs_t startTime1 = signalTime1 - duration;
+    auto fence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime1);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence1, type, startTime1);
+
+    // traceFence called before fence signalled.
+    const nsecs_t signalTime2 = systemTime();
+    const nsecs_t startTime2 = signalTime2 - duration;
+    auto fence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence2, type, startTime2);
+    fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime2);
+
+    // Create extra trace packet to trigger and finalize fence trace packets.
+    mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+    tracingSession->StopBlocking();
+
+    auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
+    EXPECT_EQ(packets.size(), 2);
+
+    const auto& packet1 = packets[0];
+    ASSERT_TRUE(packet1.has_timestamp());
+    EXPECT_EQ(packet1.timestamp(), startTime1);
+    ASSERT_TRUE(packet1.has_graphics_frame_event());
+    ASSERT_TRUE(packet1.graphics_frame_event().has_buffer_event());
+    ASSERT_TRUE(packet1.graphics_frame_event().buffer_event().has_duration_ns());
+    const auto& buffer_event1 = packet1.graphics_frame_event().buffer_event();
+    EXPECT_EQ(buffer_event1.duration_ns(), duration);
+
+    const auto& packet2 = packets[1];
+    ASSERT_TRUE(packet2.has_timestamp());
+    EXPECT_EQ(packet2.timestamp(), startTime2);
+    ASSERT_TRUE(packet2.has_graphics_frame_event());
+    ASSERT_TRUE(packet2.graphics_frame_event().has_buffer_event());
+    ASSERT_TRUE(packet2.graphics_frame_event().buffer_event().has_duration_ns());
+    const auto& buffer_event2 = packet2.graphics_frame_event().buffer_event();
+    EXPECT_EQ(buffer_event2.duration_ns(), duration);
+}
+
+} // namespace
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
new file mode 100644
index 0000000..91b304c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gui/LayerMetadata.h>
+#include <log/log.h>
+
+#include "DisplayHardware/HWComposer.h"
+#include "mock/DisplayHardware/MockComposer.h"
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
+namespace android {
+namespace {
+
+namespace hal = android::hardware::graphics::composer::hal;
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::ElementsAreArray;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrictMock;
+
+struct MockHWC2ComposerCallback : public HWC2::ComposerCallback {
+    ~MockHWC2ComposerCallback() = default;
+
+    MOCK_METHOD3(onHotplugReceived,
+                 void(int32_t sequenceId, hal::HWDisplayId display, hal::Connection connection));
+    MOCK_METHOD2(onRefreshReceived, void(int32_t sequenceId, hal::HWDisplayId display));
+    MOCK_METHOD4(onVsyncReceived,
+                 void(int32_t sequenceId, hal::HWDisplayId display, int64_t timestamp,
+                      std::optional<hal::VsyncPeriodNanos> vsyncPeriod));
+    MOCK_METHOD3(onVsyncPeriodTimingChangedReceived,
+                 void(int32_t sequenceId, hal::HWDisplayId display,
+                      const hal::VsyncPeriodChangeTimeline& updatedTimeline));
+    MOCK_METHOD2(onSeamlessPossible, void(int32_t sequenceId, hal::HWDisplayId display));
+};
+
+struct HWComposerTest : public testing::Test {
+    Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
+};
+
+struct HWComposerSetConfigurationTest : public HWComposerTest {
+    StrictMock<MockHWC2ComposerCallback> mCallback;
+};
+
+TEST_F(HWComposerSetConfigurationTest, loadsLayerMetadataSupport) {
+    const std::string kMetadata1Name = "com.example.metadata.1";
+    constexpr bool kMetadata1Mandatory = false;
+    const std::string kMetadata2Name = "com.example.metadata.2";
+    constexpr bool kMetadata2Mandatory = true;
+
+    EXPECT_CALL(*mHal, getMaxVirtualDisplayCount()).WillOnce(Return(0));
+    EXPECT_CALL(*mHal, getCapabilities()).WillOnce(Return(std::vector<hal::Capability>{}));
+    EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
+            .WillOnce(DoAll(SetArgPointee<0>(std::vector<hal::LayerGenericMetadataKey>{
+                                    {kMetadata1Name, kMetadata1Mandatory},
+                                    {kMetadata2Name, kMetadata2Mandatory},
+                            }),
+                            Return(hardware::graphics::composer::V2_4::Error::NONE)));
+    EXPECT_CALL(*mHal, registerCallback(_));
+    EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported()).WillOnce(Return(false));
+
+    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
+    hwc.setConfiguration(&mCallback, 123);
+
+    const auto& supported = hwc.getSupportedLayerGenericMetadata();
+    EXPECT_EQ(2u, supported.size());
+    EXPECT_EQ(1u, supported.count(kMetadata1Name));
+    EXPECT_EQ(kMetadata1Mandatory, supported.find(kMetadata1Name)->second);
+    EXPECT_EQ(1u, supported.count(kMetadata2Name));
+    EXPECT_EQ(kMetadata2Mandatory, supported.find(kMetadata2Name)->second);
+}
+
+TEST_F(HWComposerSetConfigurationTest, handlesUnsupportedCallToGetLayerGenericMetadataKeys) {
+    EXPECT_CALL(*mHal, getMaxVirtualDisplayCount()).WillOnce(Return(0));
+    EXPECT_CALL(*mHal, getCapabilities()).WillOnce(Return(std::vector<hal::Capability>{}));
+    EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
+            .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
+    EXPECT_CALL(*mHal, registerCallback(_));
+    EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported()).WillOnce(Return(false));
+
+    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
+    hwc.setConfiguration(&mCallback, 123);
+
+    const auto& supported = hwc.getSupportedLayerGenericMetadata();
+    EXPECT_EQ(0u, supported.size());
+}
+
+struct HWComposerLayerTest : public testing::Test {
+    static constexpr hal::HWDisplayId kDisplayId = static_cast<hal::HWDisplayId>(1001);
+    static constexpr hal::HWLayerId kLayerId = static_cast<hal::HWLayerId>(1002);
+
+    HWComposerLayerTest(const std::unordered_set<hal::Capability>& capabilities)
+          : mCapabilies(capabilities) {}
+
+    ~HWComposerLayerTest() override { EXPECT_CALL(*mHal, destroyLayer(kDisplayId, kLayerId)); }
+
+    std::unique_ptr<Hwc2::mock::Composer> mHal{new StrictMock<Hwc2::mock::Composer>()};
+    const std::unordered_set<hal::Capability> mCapabilies;
+    HWC2::impl::Layer mLayer{*mHal, mCapabilies, kDisplayId, kLayerId};
+};
+
+struct HWComposerLayerGenericMetadataTest : public HWComposerLayerTest {
+    static const std::string kLayerGenericMetadata1Name;
+    static constexpr bool kLayerGenericMetadata1Mandatory = false;
+    static const std::vector<uint8_t> kLayerGenericMetadata1Value;
+    static const std::string kLayerGenericMetadata2Name;
+    static constexpr bool kLayerGenericMetadata2Mandatory = true;
+    static const std::vector<uint8_t> kLayerGenericMetadata2Value;
+
+    HWComposerLayerGenericMetadataTest() : HWComposerLayerTest({}) {}
+};
+
+const std::string HWComposerLayerGenericMetadataTest::kLayerGenericMetadata1Name =
+        "com.example.metadata.1";
+
+const std::vector<uint8_t> HWComposerLayerGenericMetadataTest::kLayerGenericMetadata1Value = {1u,
+                                                                                              2u,
+                                                                                              3u};
+
+const std::string HWComposerLayerGenericMetadataTest::kLayerGenericMetadata2Name =
+        "com.example.metadata.2";
+
+const std::vector<uint8_t> HWComposerLayerGenericMetadataTest::kLayerGenericMetadata2Value = {45u,
+                                                                                              67u};
+
+TEST_F(HWComposerLayerGenericMetadataTest, forwardsSupportedMetadata) {
+    EXPECT_CALL(*mHal,
+                setLayerGenericMetadata(kDisplayId, kLayerId, kLayerGenericMetadata1Name,
+                                        kLayerGenericMetadata1Mandatory,
+                                        kLayerGenericMetadata1Value))
+            .WillOnce(Return(hardware::graphics::composer::V2_4::Error::NONE));
+    auto result = mLayer.setLayerGenericMetadata(kLayerGenericMetadata1Name,
+                                                 kLayerGenericMetadata1Mandatory,
+                                                 kLayerGenericMetadata1Value);
+    EXPECT_EQ(hal::Error::NONE, result);
+
+    EXPECT_CALL(*mHal,
+                setLayerGenericMetadata(kDisplayId, kLayerId, kLayerGenericMetadata2Name,
+                                        kLayerGenericMetadata2Mandatory,
+                                        kLayerGenericMetadata2Value))
+            .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
+    result = mLayer.setLayerGenericMetadata(kLayerGenericMetadata2Name,
+                                            kLayerGenericMetadata2Mandatory,
+                                            kLayerGenericMetadata2Value);
+    EXPECT_EQ(hal::Error::UNSUPPORTED, result);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 8e7440c..cae317b 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -1,140 +1,271 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
-#define LOG_TAG "LayerHistoryUnittests"
+#define LOG_TAG "LayerHistoryTest"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
 #include <log/log.h>
 
-#include <mutex>
-#include <thread>
-
 #include "Scheduler/LayerHistory.h"
+#include "Scheduler/LayerInfo.h"
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/MockLayer.h"
 
 using testing::_;
 using testing::Return;
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
 
 class LayerHistoryTest : public testing::Test {
-public:
-    LayerHistoryTest();
-    ~LayerHistoryTest() override;
-
 protected:
-    std::unique_ptr<LayerHistory> mLayerHistory;
+    static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::PresentTimeHistory::HISTORY_SIZE;
+    static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::MAX_FREQUENT_LAYER_PERIOD_NS;
 
-    static constexpr float MIN_REFRESH_RATE = 30.f;
-    static constexpr float MAX_REFRESH_RATE = 90.f;
-    static constexpr auto RELEVANT_FRAME_THRESHOLD = 90u;
-    static constexpr uint64_t THIRTY_FPS_INTERVAL = 33'333'333;
+    static constexpr float LO_FPS = 30.f;
+    static constexpr nsecs_t LO_FPS_PERIOD = 33'333'333;
 
-    void forceRelevancy(const std::unique_ptr<LayerHistory::LayerHandle>& testLayer) {
-        mLayerHistory->setVisibility(testLayer, true);
-        for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
-            mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
-        }
-    };
+    static constexpr float HI_FPS = 90.f;
+    static constexpr nsecs_t HI_FPS_PERIOD = 11'111'111;
+
+    LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
+
+    impl::LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
+    const impl::LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
+
+    size_t layerCount() const { return mScheduler->layerHistorySize(); }
+    size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
+
+    size_t frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+        const auto& infos = history().mLayerInfos;
+        return std::count_if(infos.begin(), infos.begin() + history().mActiveLayersEnd,
+                             [now](const auto& pair) { return pair.second->isFrequent(now); });
+    }
+
+    auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+
+    Hwc2::mock::Display mDisplay;
+    RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
+                                         .setVsyncPeriod(int32_t(LO_FPS_PERIOD))
+                                         .setConfigGroup(0)
+                                         .build(),
+                                 HWC2::Display::Config::Builder(mDisplay, 1)
+                                         .setVsyncPeriod(int32_t(HI_FPS_PERIOD))
+                                         .setConfigGroup(0)
+                                         .build()},
+                                HwcConfigIndexType(0)};
+    TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, false)};
+    TestableSurfaceFlinger mFlinger;
+
+    const nsecs_t mTime = systemTime();
 };
 
-LayerHistoryTest::LayerHistoryTest() {
-    mLayerHistory = std::make_unique<LayerHistory>();
-}
-LayerHistoryTest::~LayerHistoryTest() {}
-
 namespace {
+
 TEST_F(LayerHistoryTest, oneLayer) {
-    std::unique_ptr<LayerHistory::LayerHandle> testLayer =
-            mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
-    mLayerHistory->setVisibility(testLayer, true);
-    for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
-        EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-        mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    // no layers are returned if no layers are active.
+    ASSERT_TRUE(history().summarize(mTime).empty());
+    EXPECT_EQ(0, activeLayerCount());
+
+    // no layers are returned if active layers have insufficient history.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+        history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
+        ASSERT_TRUE(history().summarize(mTime).empty());
+        EXPECT_EQ(1, activeLayerCount());
     }
 
-    // Add a few more. This time we should get MAX refresh rate as the layer
-    // becomes relevant
-    static constexpr auto A_FEW = 10;
-    for (auto i = 0u; i < A_FEW; i++) {
-        EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-        mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+    // High FPS is returned once enough history has been recorded.
+    for (int i = 0; i < 10; i++) {
+        history().record(layer.get(), 0, mTime, LayerHistory::LayerUpdateType::Buffer);
+        ASSERT_EQ(1, history().summarize(mTime).size());
+        EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime)[0].desiredRefreshRate);
+        EXPECT_EQ(1, activeLayerCount());
     }
 }
 
-TEST_F(LayerHistoryTest, oneHDRLayer) {
-    std::unique_ptr<LayerHistory::LayerHandle> testLayer =
-            mLayerHistory->createLayer("TestHDRLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
-    mLayerHistory->setVisibility(testLayer, true);
-
-    mLayerHistory->insert(testLayer, 0, true /*isHDR*/);
-    EXPECT_FLOAT_EQ(0.0f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-    EXPECT_EQ(true, mLayerHistory->getDesiredRefreshRateAndHDR().second);
-
-    mLayerHistory->setVisibility(testLayer, false);
-    EXPECT_FLOAT_EQ(0.0f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-    EXPECT_EQ(false, mLayerHistory->getDesiredRefreshRateAndHDR().second);
-}
-
 TEST_F(LayerHistoryTest, explicitTimestamp) {
-    std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
-            mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
-    mLayerHistory->setVisibility(test30FpsLayer, true);
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
 
-    nsecs_t startTime = systemTime();
-    for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
-        mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
-                              false /*isHDR*/);
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = mTime;
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += LO_FPS_PERIOD;
     }
 
-    EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+    ASSERT_EQ(1, history().summarize(mTime).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(mTime)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
 }
 
 TEST_F(LayerHistoryTest, multipleLayers) {
-    std::unique_ptr<LayerHistory::LayerHandle> testLayer =
-            mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
-    mLayerHistory->setVisibility(testLayer, true);
-    std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
-            mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
-    mLayerHistory->setVisibility(test30FpsLayer, true);
-    std::unique_ptr<LayerHistory::LayerHandle> testLayer2 =
-            mLayerHistory->createLayer("TestLayer2", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
-    mLayerHistory->setVisibility(testLayer2, true);
+    auto layer1 = createLayer();
+    auto layer2 = createLayer();
+    auto layer3 = createLayer();
 
-    nsecs_t startTime = systemTime();
-    for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
-        mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+    EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer1, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer2, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer3, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+    nsecs_t time = mTime;
+
+    EXPECT_EQ(3, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // layer1 is active but infrequent.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
     }
-    EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
 
-    startTime = systemTime();
-    for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
-        mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
-                              false /*isHDR*/);
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // layer2 is frequent and has high refresh rate.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
     }
-    EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
 
-    for (int i = 10; i < RELEVANT_FRAME_THRESHOLD; i++) {
-        mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
-                              false /*isHDR*/);
+    // layer1 is still active but infrequent.
+    history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+
+    ASSERT_EQ(2, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer1 is no longer active.
+    // layer2 is frequent and has low refresh rate.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += LO_FPS_PERIOD;
     }
-    EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
 
-    // This frame is only around for 9 occurrences, so it doesn't throw
-    // anything off.
-    for (int i = 0; i < 9; i++) {
-        mLayerHistory->insert(testLayer2, 0, false /*isHDR*/);
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer2 still has low refresh rate.
+    // layer3 has high refresh rate but not enough history.
+    constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+        if (i % RATIO == 0) {
+            history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        }
+
+        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
     }
-    EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-    // After 1200 ms frames become obsolete.
-    std::this_thread::sleep_for(std::chrono::milliseconds(1500));
 
-    mLayerHistory->insert(test30FpsLayer,
-                          startTime + (RELEVANT_FRAME_THRESHOLD * THIRTY_FPS_INTERVAL),
-                          false /*isHDR*/);
-    EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer3 becomes recently active.
+    history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    ASSERT_EQ(2, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer1 expires.
+    layer1.clear();
+    ASSERT_EQ(2, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    EXPECT_EQ(2, layerCount());
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer2 still has low refresh rate.
+    // layer3 becomes inactive.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += LO_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer2 expires.
+    layer2.clear();
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // layer3 becomes active and has high refresh rate.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer3 expires.
+    layer3.clear();
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
 }
 
 } // namespace
-} // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
new file mode 100644
index 0000000..afd2b71
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -0,0 +1,748 @@
+/*
+ * 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 "LayerHistoryTestV2"
+
+#include <Layer.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include "Scheduler/LayerHistory.h"
+#include "Scheduler/LayerInfoV2.h"
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/MockLayer.h"
+
+using testing::_;
+using testing::Return;
+
+namespace android::scheduler {
+
+class LayerHistoryTestV2 : public testing::Test {
+protected:
+    static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
+    static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
+    static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE;
+    static constexpr auto PRESENT_TIME_HISTORY_DURATION = LayerInfoV2::HISTORY_DURATION;
+    static constexpr auto REFRESH_RATE_AVERAGE_HISTORY_DURATION =
+            LayerInfoV2::RefreshRateHistory::HISTORY_DURATION;
+
+    static constexpr float LO_FPS = 30.f;
+    static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
+
+    static constexpr float HI_FPS = 90.f;
+    static constexpr auto HI_FPS_PERIOD = static_cast<nsecs_t>(1e9f / HI_FPS);
+
+    LayerHistoryTestV2() { mFlinger.resetScheduler(mScheduler); }
+
+    impl::LayerHistoryV2& history() { return *mScheduler->mutableLayerHistoryV2(); }
+    const impl::LayerHistoryV2& history() const { return *mScheduler->mutableLayerHistoryV2(); }
+
+    size_t layerCount() const { return mScheduler->layerHistorySize(); }
+    size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
+
+    auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+        const auto& infos = history().mLayerInfos;
+        return std::count_if(infos.begin(),
+                             infos.begin() + static_cast<long>(history().mActiveLayersEnd),
+                             [now](const auto& pair) { return pair.second->isFrequent(now); });
+    }
+
+    auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+        const auto& infos = history().mLayerInfos;
+        return std::count_if(infos.begin(),
+                             infos.begin() + static_cast<long>(history().mActiveLayersEnd),
+                             [now](const auto& pair) { return pair.second->isAnimating(now); });
+    }
+
+    void setLayerInfoVote(Layer* layer,
+                          LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
+        for (auto& [weak, info] : history().mLayerInfos) {
+            if (auto strong = weak.promote(); strong && strong.get() == layer) {
+                info->setDefaultLayerVote(vote);
+                info->setLayerVote(vote, 0);
+                return;
+            }
+        }
+    }
+
+    auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+    auto createLayer(std::string name) {
+        return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
+    }
+
+    void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, float frameRate,
+                               float desiredRefreshRate, int numFrames) {
+        const nsecs_t framePeriod = static_cast<nsecs_t>(1e9f / frameRate);
+        impl::LayerHistoryV2::Summary summary;
+        for (int i = 0; i < numFrames; i++) {
+            history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+            time += framePeriod;
+
+            summary = history().summarize(time);
+        }
+
+        ASSERT_EQ(1, summary.size());
+        ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+        ASSERT_FLOAT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate)
+                << "Frame rate is " << frameRate;
+    }
+
+    Hwc2::mock::Display mDisplay;
+    RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
+                                         .setVsyncPeriod(int32_t(LO_FPS_PERIOD))
+                                         .setConfigGroup(0)
+                                         .build(),
+                                 HWC2::Display::Config::Builder(mDisplay, 1)
+                                         .setVsyncPeriod(int32_t(HI_FPS_PERIOD))
+                                         .setConfigGroup(0)
+                                         .build()},
+                                HwcConfigIndexType(0)};
+    TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, true)};
+    TestableSurfaceFlinger mFlinger;
+
+};
+
+namespace {
+
+TEST_F(LayerHistoryTestV2, oneLayer) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    const nsecs_t time = systemTime();
+
+    // No layers returned if no layers are active.
+    EXPECT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+
+    // Max returned if active layers have insufficient history.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+        EXPECT_EQ(1, activeLayerCount());
+    }
+
+    // Max is returned since we have enough history but there is no timestamp votes.
+    for (int i = 0; i < 10; i++) {
+        history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+        EXPECT_EQ(1, activeLayerCount());
+    }
+}
+
+TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+
+    history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer);
+    auto summary = history().summarize(time);
+    ASSERT_EQ(1, history().summarize(time).size());
+    // Layer is still considered inactive so we expect to get Min
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
+
+    summary = history().summarize(time);
+    EXPECT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+}
+
+TEST_F(LayerHistoryTestV2, explicitTimestamp) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += LO_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerNoVote) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerMinVote) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerMaxVote) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Max);
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += LO_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerExplicitVote) {
+    auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree())
+            .WillRepeatedly(
+                    Return(Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::Default)));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive, but the vote stays
+    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerExplicitExactVote) {
+    auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(
+                    Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive, but the vote stays
+    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, multipleLayers) {
+    auto layer1 = createLayer();
+    auto layer2 = createLayer();
+    auto layer3 = createLayer();
+
+    EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(3, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    impl::LayerHistoryV2::Summary summary;
+
+    // layer1 is active but infrequent.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+        summary = history().summarize(time);
+    }
+
+    ASSERT_EQ(1, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // layer2 is frequent and has high refresh rate.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+        summary = history().summarize(time);
+    }
+
+    // layer1 is still active but infrequent.
+    history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+
+    ASSERT_EQ(2, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote);
+    ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer1 is no longer active.
+    // layer2 is frequent and has low refresh rate.
+    for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += LO_FPS_PERIOD;
+        summary = history().summarize(time);
+    }
+
+    ASSERT_EQ(1, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer2 still has low refresh rate.
+    // layer3 has high refresh rate but not enough history.
+    constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+        if (i % RATIO == 0) {
+            history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        }
+
+        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+        summary = history().summarize(time);
+    }
+
+    ASSERT_EQ(2, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, summary[1].vote);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer3 becomes recently active.
+    history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    summary = history().summarize(time);
+    ASSERT_EQ(2, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer1 expires.
+    layer1.clear();
+    summary = history().summarize(time);
+    ASSERT_EQ(2, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[1].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, summary[1].desiredRefreshRate);
+    EXPECT_EQ(2, layerCount());
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer2 still has low refresh rate.
+    // layer3 becomes inactive.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += LO_FPS_PERIOD;
+        summary = history().summarize(time);
+    }
+
+    ASSERT_EQ(1, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer2 expires.
+    layer2.clear();
+    summary = history().summarize(time);
+    EXPECT_TRUE(summary.empty());
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // layer3 becomes active and has high refresh rate.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) {
+        history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+        summary = history().summarize(time);
+    }
+
+    ASSERT_EQ(1, summary.size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, summary[0].desiredRefreshRate);
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer3 expires.
+    layer3.clear();
+    summary = history().summarize(time);
+    EXPECT_TRUE(summary.empty());
+    EXPECT_EQ(0, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, inactiveLayers) {
+    auto layer = createLayer();
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    // the very first updates makes the layer frequent
+    for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+        EXPECT_EQ(1, layerCount());
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+        EXPECT_EQ(1, activeLayerCount());
+        EXPECT_EQ(1, frequentLayerCount(time));
+    }
+
+    // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+    EXPECT_EQ(1, layerCount());
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // advance the time for the previous frame to be inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+
+    // Now event if we post a quick few frame we should stay infrequent
+    for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += HI_FPS_PERIOD;
+
+        EXPECT_EQ(1, layerCount());
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+        EXPECT_EQ(1, activeLayerCount());
+        EXPECT_EQ(0, frequentLayerCount(time));
+    }
+
+    // More quick frames will get us to frequent again
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    time += HI_FPS_PERIOD;
+
+    EXPECT_EQ(1, layerCount());
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    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, LayerHistory::LayerUpdateType::Buffer);
+    history().record(explicitInvisiblelayer.get(), time, time,
+                     LayerHistory::LayerUpdateType::Buffer);
+
+    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));
+}
+
+TEST_F(LayerHistoryTestV2, infrequentAnimatingLayer) {
+    auto layer = createLayer();
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // layer is active but infrequent.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // another update with the same cadence keep in infrequent
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(0, animatingLayerCount(time));
+
+    // an update as animation will immediately vote for Max
+    history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX);
+    time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+    EXPECT_EQ(1, animatingLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, heuristicLayer60Hz) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+    for (float fps = 54.0f; fps < 65.0f; fps += 0.1f) {
+        recordFramesAndExpect(layer, time, fps, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+    }
+}
+
+TEST_F(LayerHistoryTestV2, heuristicLayer60_30Hz) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+    recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+
+    recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 30.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 30.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 60.0f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 60.0f, 60.0f, PRESENT_TIME_HISTORY_SIZE);
+}
+
+TEST_F(LayerHistoryTestV2, heuristicLayerNotOscillating) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.90f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.00f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 26.90f, 24.0f, PRESENT_TIME_HISTORY_SIZE);
+    recordFramesAndExpect(layer, time, 27.10f, 30.0f, PRESENT_TIME_HISTORY_SIZE);
+}
+
+class LayerHistoryTestV2Parameterized
+      : public LayerHistoryTestV2,
+        public testing::WithParamInterface<std::chrono::nanoseconds> {};
+
+TEST_P(LayerHistoryTestV2Parameterized, HeuristicLayerWithInfrequentLayer) {
+    std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
+    auto heuristicLayer = createLayer("HeuristicLayer");
+
+    EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(Layer::FrameRate()));
+
+    auto infrequentLayer = createLayer("InfrequentLayer");
+    EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
+            .WillRepeatedly(Return(Layer::FrameRate()));
+
+    const nsecs_t startTime = systemTime();
+
+    const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
+    history().record(heuristicLayer.get(), startTime, startTime,
+                     LayerHistory::LayerUpdateType::Buffer);
+    history().record(infrequentLayer.get(), startTime, startTime,
+                     LayerHistory::LayerUpdateType::Buffer);
+
+    nsecs_t time = startTime;
+    nsecs_t lastInfrequentUpdate = startTime;
+    const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
+    int infrequentLayerUpdates = 0;
+    while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
+        time += heuristicUpdateDelta.count();
+        history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+
+        if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
+            ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
+                  totalInfrequentLayerUpdates);
+            lastInfrequentUpdate = time;
+            history().record(infrequentLayer.get(), time, time,
+                             LayerHistory::LayerUpdateType::Buffer);
+            infrequentLayerUpdates++;
+        }
+
+        if (time - startTime > PRESENT_TIME_HISTORY_DURATION.count()) {
+            ASSERT_NE(0, history().summarize(time).size());
+            ASSERT_GE(2, history().summarize(time).size());
+
+            bool max = false;
+            bool min = false;
+            float heuristic = 0;
+            for (const auto& layer : history().summarize(time)) {
+                if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
+                    heuristic = layer.desiredRefreshRate;
+                } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
+                    max = true;
+                } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
+                    min = true;
+                }
+            }
+
+            if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
+                EXPECT_FLOAT_EQ(24.0f, heuristic);
+                EXPECT_FALSE(max);
+                if (history().summarize(time).size() == 2) {
+                    EXPECT_TRUE(min);
+                }
+            }
+        }
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestV2Parameterized,
+                        ::testing::Values(1s, 2s, 3s, 4s, 5s));
+
+} // namespace
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
similarity index 75%
rename from services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
rename to services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index eff22b6..0208728 100644
--- a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -21,17 +21,17 @@
 #include <utils/Log.h>
 
 #include "AsyncCallRecorder.h"
-#include "Scheduler/IdleTimer.h"
+#include "Scheduler/OneShotTimer.h"
 
 using namespace std::chrono_literals;
 
 namespace android {
 namespace scheduler {
 
-class IdleTimerTest : public testing::Test {
+class OneShotTimerTest : public testing::Test {
 protected:
-    IdleTimerTest() = default;
-    ~IdleTimerTest() override = default;
+    OneShotTimerTest() = default;
+    ~OneShotTimerTest() override = default;
 
     // This timeout should be used when a 3ms callback is expected.
     // While the tests typically request a callback after 3ms, the scheduler
@@ -46,7 +46,7 @@
     AsyncCallRecorder<void (*)()> mResetTimerCallback;
     AsyncCallRecorder<void (*)()> mExpiredTimerCallback;
 
-    std::unique_ptr<IdleTimer> mIdleTimer;
+    std::unique_ptr<OneShotTimer> mIdleTimer;
 
     void clearPendingCallbacks() {
         while (mExpiredTimerCallback.waitForCall(0us).has_value()) {
@@ -55,13 +55,14 @@
 };
 
 namespace {
-TEST_F(IdleTimerTest, createAndDestroyTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, [] {}, [] {});
+TEST_F(OneShotTimerTest, createAndDestroyTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(
+            3ms, [] {}, [] {});
 }
 
-TEST_F(IdleTimerTest, startStopTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, startStopTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(30ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     auto startTime = std::chrono::steady_clock::now();
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
@@ -70,7 +71,7 @@
     bool callbackCalled = mExpiredTimerCallback.waitForCall(25ms).has_value();
     // Under ideal conditions there should be no event. But occasionally
     // it is possible that the wait just prior takes more than 30ms, and
-    // a callback is observed. We check the elapsed time since before the IdleTimer
+    // a callback is observed. We check the elapsed time since before the OneShotTimer
     // thread was started as a sanity check to not have a flakey test.
     EXPECT_FALSE(callbackCalled && std::chrono::steady_clock::now() - startTime < 30ms);
 
@@ -79,9 +80,9 @@
     mIdleTimer->stop();
 }
 
-TEST_F(IdleTimerTest, resetTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, resetTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(20ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     // Observe any event that happens in about 25ms. We don't care if one was
@@ -104,9 +105,9 @@
     EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
 }
 
-TEST_F(IdleTimerTest, resetBackToBackTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, resetBackToBackTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(20ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
 
@@ -135,9 +136,9 @@
     EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
 }
 
-TEST_F(IdleTimerTest, startNotCalledTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, startNotCalledTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     // The start hasn't happened, so the callback does not happen.
     EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
     EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
@@ -147,9 +148,9 @@
     EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
 }
 
-TEST_F(IdleTimerTest, idleTimerIdlesTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, idleTimerIdlesTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
 
@@ -167,18 +168,18 @@
     EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
 }
 
-TEST_F(IdleTimerTest, timeoutCallbackExecutionTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
     mIdleTimer->stop();
 }
 
-TEST_F(IdleTimerTest, noCallbacksAfterStopAndResetTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
@@ -190,9 +191,9 @@
     EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
 }
 
-TEST_F(IdleTimerTest, noCallbacksAfterStopTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, noCallbacksAfterStopTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
 
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
new file mode 100644
index 0000000..0b74682
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/PhaseOffsets.h"
+
+using namespace testing;
+
+namespace android {
+namespace scheduler {
+
+class TestablePhaseOffsetsAsDurations : public impl::PhaseDurations {
+public:
+    TestablePhaseOffsetsAsDurations(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
+                                    nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+                                    nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
+          : impl::PhaseDurations({60.0f, 90.0f}, currentFps, sfDuration, appDuration,
+                                 sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
+                                 appEarlyGlDuration) {}
+};
+
+class PhaseDurationTest : public testing::Test {
+protected:
+    PhaseDurationTest()
+          : mPhaseDurations(60.0f, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000,
+                            21'000'000) {}
+
+    ~PhaseDurationTest() = default;
+
+    TestablePhaseOffsetsAsDurations mPhaseDurations;
+};
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_60Hz) {
+    mPhaseDurations.setRefreshRateFps(60.0f);
+    auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+    auto offsets = mPhaseDurations.getOffsetsForRefreshRate(60.0f);
+
+    EXPECT_EQ(currentOffsets, offsets);
+    EXPECT_EQ(offsets.late.sf, 6'166'667);
+
+    EXPECT_EQ(offsets.late.app, 2'333'334);
+
+    EXPECT_EQ(offsets.early.sf, 666'667);
+
+    EXPECT_EQ(offsets.early.app, 833'334);
+
+    EXPECT_EQ(offsets.earlyGl.sf, 3'166'667);
+
+    EXPECT_EQ(offsets.earlyGl.app, 15'500'001);
+}
+
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) {
+    mPhaseDurations.setRefreshRateFps(90.0f);
+    auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+    auto offsets = mPhaseDurations.getOffsetsForRefreshRate(90.0f);
+
+    EXPECT_EQ(currentOffsets, offsets);
+    EXPECT_EQ(offsets.late.sf, 611'111);
+
+    EXPECT_EQ(offsets.late.app, 2'333'333);
+
+    EXPECT_EQ(offsets.early.sf, -4'888'889);
+
+    EXPECT_EQ(offsets.early.app, 833'333);
+
+    EXPECT_EQ(offsets.earlyGl.sf, -2'388'889);
+
+    EXPECT_EQ(offsets.earlyGl.app, 9'944'444);
+}
+
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) {
+    TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
+
+    auto validateOffsets = [](auto& offsets) {
+        EXPECT_EQ(offsets.late.sf, 1'000'000);
+
+        EXPECT_EQ(offsets.late.app, 1'000'000);
+
+        EXPECT_EQ(offsets.early.sf, 1'000'000);
+
+        EXPECT_EQ(offsets.early.app, 1'000'000);
+
+        EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+
+        EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+    };
+
+    phaseOffsetsWithDefaultValues.setRefreshRateFps(90.0f);
+    auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
+    auto offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
+    EXPECT_EQ(currentOffsets, offsets);
+    validateOffsets(offsets);
+
+    phaseOffsetsWithDefaultValues.setRefreshRateFps(60.0f);
+    currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
+    offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
+    EXPECT_EQ(currentOffsets, offsets);
+    validateOffsets(offsets);
+}
+
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+    auto offsets = mPhaseDurations.getOffsetsForRefreshRate(14.7f);
+
+    EXPECT_EQ(offsets.late.sf, 57'527'208);
+
+    EXPECT_EQ(offsets.late.app, 37'027'208);
+
+    EXPECT_EQ(offsets.early.sf, 52'027'208);
+
+    EXPECT_EQ(offsets.early.app, 35'527'208);
+
+    EXPECT_EQ(offsets.earlyGl.sf, 54'527'208);
+
+    EXPECT_EQ(offsets.earlyGl.app, 33'527'208);
+}
+
+} // namespace
+
+class TestablePhaseOffsets : public impl::PhaseOffsets {
+public:
+    TestablePhaseOffsets()
+          : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 1'000'000, 1'000'000, {}, {}, {}, {},
+                               10'000'000) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+    PhaseOffsetsTest() = default;
+    ~PhaseOffsetsTest() = default;
+
+    TestablePhaseOffsets mPhaseOffsets;
+};
+
+namespace {
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+    auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(14.7f);
+
+    EXPECT_EQ(offsets.late.sf, 1'000'000);
+
+    EXPECT_EQ(offsets.late.app, 1'000'000);
+
+    EXPECT_EQ(offsets.early.sf, 1'000'000);
+
+    EXPECT_EQ(offsets.early.app, 1'000'000);
+
+    EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+
+    EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
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 f315a8a..1f6f166 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -21,8 +21,10 @@
 #include <log/log.h>
 #include <thread>
 
+#include "../../Scheduler/RefreshRateConfigs.h"
 #include "DisplayHardware/HWC2.h"
 #include "Scheduler/RefreshRateConfigs.h"
+#include "mock/DisplayHardware/MockDisplay.h"
 
 using namespace std::chrono_literals;
 using testing::_;
@@ -30,29 +32,111 @@
 namespace android {
 namespace scheduler {
 
-using RefreshRateType = RefreshRateConfigs::RefreshRateType;
+namespace hal = android::hardware::graphics::composer::hal;
+
 using RefreshRate = RefreshRateConfigs::RefreshRate;
+using LayerVoteType = RefreshRateConfigs::LayerVoteType;
+using LayerRequirement = RefreshRateConfigs::LayerRequirement;
 
 class RefreshRateConfigsTest : public testing::Test {
 protected:
-    static constexpr int CONFIG_ID_60 = 0;
-    static constexpr hwc2_config_t HWC2_CONFIG_ID_60 = 0;
-    static constexpr int CONFIG_ID_90 = 1;
-    static constexpr hwc2_config_t HWC2_CONFIG_ID_90 = 1;
-    static constexpr int64_t VSYNC_60 = 16666667;
-    static constexpr int64_t VSYNC_90 = 11111111;
-
     RefreshRateConfigsTest();
     ~RefreshRateConfigsTest();
 
-    void assertRatesEqual(const RefreshRate& left, const RefreshRate& right) {
-        ASSERT_EQ(left.configId, right.configId);
-        ASSERT_EQ(left.name, right.name);
-        ASSERT_EQ(left.fps, right.fps);
-        ASSERT_EQ(left.vsyncPeriod, right.vsyncPeriod);
+    float findClosestKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs, float frameRate) {
+        return refreshRateConfigs.findClosestKnownFrameRate(frameRate);
     }
+
+    std::vector<float> getKnownFrameRate(const RefreshRateConfigs& refreshRateConfigs) {
+        return refreshRateConfigs.mKnownFrameRates;
+    }
+
+    // Test config IDs
+    static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0);
+    static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1);
+    static inline const HwcConfigIndexType HWC_CONFIG_ID_72 = HwcConfigIndexType(2);
+    static inline const HwcConfigIndexType HWC_CONFIG_ID_120 = HwcConfigIndexType(3);
+    static inline const HwcConfigIndexType HWC_CONFIG_ID_30 = HwcConfigIndexType(4);
+
+    // Test configs
+    std::shared_ptr<const HWC2::Display::Config> mConfig60 =
+            createConfig(HWC_CONFIG_ID_60, 0, static_cast<int64_t>(1e9f / 60));
+    std::shared_ptr<const HWC2::Display::Config> mConfig90 =
+            createConfig(HWC_CONFIG_ID_90, 0, static_cast<int64_t>(1e9f / 90));
+    std::shared_ptr<const HWC2::Display::Config> mConfig90DifferentGroup =
+            createConfig(HWC_CONFIG_ID_90, 1, static_cast<int64_t>(1e9f / 90));
+    std::shared_ptr<const HWC2::Display::Config> mConfig90DifferentResolution =
+            createConfig(HWC_CONFIG_ID_90, 0, static_cast<int64_t>(1e9f / 90), 111, 222);
+    std::shared_ptr<const HWC2::Display::Config> mConfig72 =
+            createConfig(HWC_CONFIG_ID_72, 0, static_cast<int64_t>(1e9f / 72));
+    std::shared_ptr<const HWC2::Display::Config> mConfig72DifferentGroup =
+            createConfig(HWC_CONFIG_ID_72, 1, static_cast<int64_t>(1e9f / 72));
+    std::shared_ptr<const HWC2::Display::Config> mConfig120 =
+            createConfig(HWC_CONFIG_ID_120, 0, static_cast<int64_t>(1e9f / 120));
+    std::shared_ptr<const HWC2::Display::Config> mConfig120DifferentGroup =
+            createConfig(HWC_CONFIG_ID_120, 1, static_cast<int64_t>(1e9f / 120));
+    std::shared_ptr<const HWC2::Display::Config> mConfig30 =
+            createConfig(HWC_CONFIG_ID_30, 0, static_cast<int64_t>(1e9f / 30));
+
+    // Test device configurations
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> m60OnlyConfigDevice = {mConfig60};
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90Device = {mConfig60, mConfig90};
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90DeviceWithDifferentGroups =
+            {mConfig60, mConfig90DifferentGroup};
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90DeviceWithDifferentResolutions =
+            {mConfig60, mConfig90DifferentResolution};
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_72_90Device = {mConfig60,
+                                                                                 mConfig90,
+                                                                                 mConfig72};
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> m60_90_72_120Device = {mConfig60,
+                                                                                     mConfig90,
+                                                                                     mConfig72,
+                                                                                     mConfig120};
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60_72_90_120Device = {mConfig60,
+                                                                                        mConfig90,
+                                                                                        mConfig72,
+                                                                                        mConfig120,
+                                                                                        mConfig30};
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60Device =
+            {mConfig60, mConfig90DifferentGroup, mConfig72DifferentGroup, mConfig120DifferentGroup,
+             mConfig30};
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60_72_90Device =
+            {mConfig60, mConfig90, mConfig72, mConfig120DifferentGroup, mConfig30};
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> m30_60_90Device =
+            {mConfig60, mConfig90, mConfig72DifferentGroup, mConfig120DifferentGroup, mConfig30};
+
+    // Expected RefreshRate objects
+    RefreshRate mExpected60Config = {HWC_CONFIG_ID_60, mConfig60, "60fps", 60,
+                                     RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpectedAlmost60Config = {HWC_CONFIG_ID_60,
+                                           createConfig(HWC_CONFIG_ID_60, 0, 16666665), "60fps", 60,
+                                           RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpected90Config = {HWC_CONFIG_ID_90, mConfig90, "90fps", 90,
+                                     RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpected90DifferentGroupConfig = {HWC_CONFIG_ID_90, mConfig90DifferentGroup,
+                                                   "90fps", 90, RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpected90DifferentResolutionConfig = {HWC_CONFIG_ID_90,
+                                                        mConfig90DifferentResolution, "90fps", 90,
+                                                        RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpected72Config = {HWC_CONFIG_ID_72, mConfig72, "72fps", 72,
+                                     RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpected30Config = {HWC_CONFIG_ID_30, mConfig30, "30fps", 30,
+                                     RefreshRate::ConstructorTag(0)};
+    RefreshRate mExpected120Config = {HWC_CONFIG_ID_120, mConfig120, "120fps", 120,
+                                      RefreshRate::ConstructorTag(0)};
+
+    Hwc2::mock::Display mDisplay;
+
+private:
+    std::shared_ptr<const HWC2::Display::Config> createConfig(HwcConfigIndexType configId,
+                                                              int32_t configGroup,
+                                                              int64_t vsyncPeriod,
+                                                              int32_t hight = -1,
+                                                              int32_t width = -1);
 };
 
+using Builder = HWC2::Display::Config::Builder;
+
 RefreshRateConfigsTest::RefreshRateConfigsTest() {
     const ::testing::TestInfo* const test_info =
             ::testing::UnitTest::GetInstance()->current_test_info();
@@ -65,44 +149,1328 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
+std::shared_ptr<const HWC2::Display::Config> RefreshRateConfigsTest::createConfig(
+        HwcConfigIndexType configId, int32_t configGroup, int64_t vsyncPeriod, int32_t hight,
+        int32_t width) {
+    return HWC2::Display::Config::Builder(mDisplay, hal::HWConfigId(configId.value()))
+            .setVsyncPeriod(int32_t(vsyncPeriod))
+            .setConfigGroup(configGroup)
+            .setHeight(hight)
+            .setWidth(width)
+            .build();
+}
+
 namespace {
 /* ------------------------------------------------------------------------
  * Test cases
  */
-TEST_F(RefreshRateConfigsTest, oneDeviceConfig_isRejected) {
-    std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60}};
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_SwitchingSupported) {
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfig=*/0);
-    ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported());
+            std::make_unique<RefreshRateConfigs>(m60OnlyConfigDevice,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+}
+
+TEST_F(RefreshRateConfigsTest, invalidPolicy) {
+    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);
 }
 
 TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
-    std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60},
-                                                         {HWC2_CONFIG_ID_90, VSYNC_90}};
     auto refreshRateConfigs =
-            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
-                                                 /*currentConfig=*/0);
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
-    const auto& rates = refreshRateConfigs->getRefreshRateMap();
-    ASSERT_EQ(2, rates.size());
-    const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
-    const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE);
-    ASSERT_NE(rates.end(), defaultRate);
-    ASSERT_NE(rates.end(), performanceRate);
+    const auto& minRate = refreshRateConfigs->getMinRefreshRate();
+    const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
 
-    RefreshRate expectedDefaultConfig = {CONFIG_ID_60, "60fps", 60, VSYNC_60, HWC2_CONFIG_ID_60};
-    assertRatesEqual(expectedDefaultConfig, defaultRate->second);
-    RefreshRate expectedPerformanceConfig = {CONFIG_ID_90, "90fps", 90, VSYNC_90,
-                                             HWC2_CONFIG_ID_90};
-    assertRatesEqual(expectedPerformanceConfig, performanceRate->second);
+    ASSERT_EQ(mExpected60Config, minRate);
+    ASSERT_EQ(mExpected90Config, performanceRate);
 
-    assertRatesEqual(expectedDefaultConfig,
-                     refreshRateConfigs->getRefreshRateFromType(RefreshRateType::DEFAULT));
-    assertRatesEqual(expectedPerformanceConfig,
-                     refreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE));
+    const auto& minRateByPolicy = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    ASSERT_EQ(minRateByPolicy, minRate);
+    ASSERT_EQ(performanceRateByPolicy, performanceRate);
 }
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap_differentGroups) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    const auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
+    const auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+    ASSERT_EQ(mExpected60Config, minRate);
+    ASSERT_EQ(mExpected60Config, minRate60);
+    ASSERT_EQ(mExpected60Config, performanceRate60);
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60, 90}}), 0);
+    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+
+    const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+    ASSERT_EQ(mExpected90DifferentGroupConfig, performanceRate);
+    ASSERT_EQ(mExpected90DifferentGroupConfig, minRate90);
+    ASSERT_EQ(mExpected90DifferentGroupConfig, performanceRate90);
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap_differentResolutions) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentResolutions,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    const auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
+    const auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+    ASSERT_EQ(mExpected60Config, minRate);
+    ASSERT_EQ(mExpected60Config, minRate60);
+    ASSERT_EQ(mExpected60Config, performanceRate60);
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {60, 90}}), 0);
+    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+
+    const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+    ASSERT_EQ(mExpected90DifferentResolutionConfig, performanceRate);
+    ASSERT_EQ(mExpected90DifferentResolutionConfig, minRate90);
+    ASSERT_EQ(mExpected90DifferentResolutionConfig, performanceRate90);
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_policyChange) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
+    auto& performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+    ASSERT_EQ(mExpected60Config, minRate);
+    ASSERT_EQ(mExpected90Config, performanceRate);
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
+
+    auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    ASSERT_EQ(mExpected60Config, minRate60);
+    ASSERT_EQ(mExpected60Config, performanceRate60);
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getCurrentRefreshRate) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    {
+        auto& current = refreshRateConfigs->getCurrentRefreshRate();
+        EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_60);
+    }
+
+    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+    {
+        auto& current = refreshRateConfigs->getCurrentRefreshRate();
+        EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90);
+    }
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
+    {
+        auto& current = refreshRateConfigs->getCurrentRefreshRate();
+        EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90);
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    const auto makeLayerRequirements = [](float refreshRate) -> std::vector<LayerRequirement> {
+        return {{"testLayer", LayerVoteType::Heuristic, refreshRate, /*weight*/ 1.0f,
+                 /*focused*/ false}};
+    };
+
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0, 120}}), 0);
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_noLayers) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_72_90Device, /*currentConfigId=*/
+                                                 HWC_CONFIG_ID_72);
+
+    // If there are no layers we select the default frame rate, which is the max of the primary
+    // range.
+    auto layers = std::vector<LayerRequirement>{};
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_90) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    lr.name = "Min";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.vote = LayerVoteType::Max;
+    lr.name = "Max";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    lr.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 60.0f;
+    lr.name = "60Hz Heuristic";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 45.0f;
+    lr.name = "45Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 30.0f;
+    lr.name = "30Hz Heuristic";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 24.0f;
+    lr.name = "24Hz Heuristic";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.name = "";
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 30.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 30.0f;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {0, 120}}), 0);
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 30.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_60_72_90) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_72_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 30.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90_120) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(mExpected120Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 48.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 48.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "60Hz Heuristic";
+    EXPECT_EQ(mExpected120Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "60Hz Heuristic";
+    EXPECT_EQ(mExpected120Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "60Hz ExplicitDefault";
+    EXPECT_EQ(mExpected120Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.name = "24Hz Heuristic";
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz ExplicitDefault";
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.name = "24Hz ExplicitExactOrMultiple";
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.name = "90Hz ExplicitDefault";
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.name = "24Hz ExplicitDefault";
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.name = "90Hz ExplicitExactOrMultiple";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(mExpected30Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 30.0f;
+    EXPECT_EQ(mExpected30Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_72_90) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_72_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    lr.name = "Min";
+    EXPECT_EQ(mExpected30Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.vote = LayerVoteType::Max;
+    lr.name = "Max";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    lr.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.desiredRefreshRate = 60.0f;
+    lr.name = "60Hz Heuristic";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+
+    lr.desiredRefreshRate = 45.0f;
+    lr.name = "45Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+
+    lr.desiredRefreshRate = 30.0f;
+    lr.name = "30Hz Heuristic";
+    EXPECT_EQ(mExpected30Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+
+    lr.desiredRefreshRate = 24.0f;
+    lr.name = "24Hz Heuristic";
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+
+    lr.desiredRefreshRate = 24.0f;
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr.name = "24Hz ExplicitExactOrMultiple";
+    EXPECT_EQ(mExpected72Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_PriorityTest) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::Min;
+    lr2.vote = LayerVoteType::Max;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.vote = LayerVoteType::Min;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.vote = LayerVoteType::Min;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.vote = LayerVoteType::Max;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.vote = LayerVoteType::Max;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 15.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 30.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_24FpsVideo) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
+        lr.desiredRefreshRate = fps;
+        const auto& refreshRate =
+                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+        EXPECT_EQ(mExpected60Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent_Explicit) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 90.0f;
+    EXPECT_EQ(mExpected90Config, refreshRateConfigs->getRefreshRateForContent(layers));
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(mExpected60Config, refreshRateConfigs->getRefreshRateForContent(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getBestRefreshRate_Explicit) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 90.0f;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+TEST_F(RefreshRateConfigsTest, testInPolicy) {
+    ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(60.000004f, 60.000004f));
+    ASSERT_TRUE(mExpectedAlmost60Config.inPolicy(59.0f, 60.1f));
+    ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(75.0f, 90.0f));
+    ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(60.0011f, 90.0f));
+    ASSERT_FALSE(mExpectedAlmost60Config.inPolicy(50.0f, 59.998f));
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_75HzContent) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
+        lr.desiredRefreshRate = fps;
+        const auto& refreshRate =
+                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+        EXPECT_EQ(mExpected90Config, refreshRate) << fps << "Hz chooses " << refreshRate.getName();
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_Multiples) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.name = "90Hz ExplicitDefault";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Max;
+    lr2.name = "Max";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 30.0f;
+    lr1.name = "30Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 30.0f;
+    lr1.name = "30Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Max;
+    lr2.name = "Max";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::NoVote;
+    lr2.name = "NoVote";
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::NoVote;
+    lr2.name = "NoVote";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Max;
+    lr2.name = "Max";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false}));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Max;
+    lr2.name = "Max";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    // The other layer starts to provide buffers
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.name = "90Hz Heuristic";
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+TEST_F(RefreshRateConfigsTest, touchConsidered) {
+    RefreshRateConfigs::GlobalSignals consideredSignals;
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false}, &consideredSignals);
+    EXPECT_EQ(false, consideredSignals.touch);
+
+    refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = false}, &consideredSignals);
+    EXPECT_EQ(true, consideredSignals.touch);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.name = "60Hz Heuristic";
+    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+                                           &consideredSignals);
+    EXPECT_EQ(true, consideredSignals.touch);
+
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.name = "60Hz Heuristic";
+    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+                                           &consideredSignals);
+    EXPECT_EQ(false, consideredSignals.touch);
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.name = "60Hz Heuristic";
+    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+                                           &consideredSignals);
+    EXPECT_EQ(true, consideredSignals.touch);
+
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr1.desiredRefreshRate = 60.0f;
+    lr1.name = "60Hz ExplicitExactOrMultiple";
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.name = "60Hz Heuristic";
+    refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = false},
+                                           &consideredSignals);
+    EXPECT_EQ(false, consideredSignals.touch);
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_ExplicitDefault) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90_72_120Device, /*currentConfigId=*/
+                                                 HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    // Prepare a table with the vote and the expected refresh rate
+    const std::vector<std::pair<float, float>> testCases = {
+            {130, 120}, {120, 120}, {119, 120}, {110, 120},
+
+            {100, 90},  {90, 90},   {89, 90},
+
+            {80, 72},   {73, 72},   {72, 72},   {71, 72},   {70, 72},
+
+            {65, 60},   {60, 60},   {59, 60},   {58, 60},
+
+            {55, 90},   {50, 90},   {45, 90},
+
+            {42, 120},  {40, 120},  {39, 120},
+
+            {37, 72},   {36, 72},   {35, 72},
+
+            {30, 60},
+    };
+
+    for (const auto& test : testCases) {
+        lr.vote = LayerVoteType::ExplicitDefault;
+        lr.desiredRefreshRate = test.first;
+
+        std::stringstream ss;
+        ss << "ExplicitDefault " << test.first << " fps";
+        lr.name = ss.str();
+
+        const auto& refreshRate =
+                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+        EXPECT_FLOAT_EQ(refreshRate.getFps(), test.second)
+                << "Expecting " << test.first << "fps => " << test.second << "Hz";
+    }
+}
+
+TEST_F(RefreshRateConfigsTest,
+       getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresTouchFlag) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_90);
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+                      {HWC_CONFIG_ID_90, {90.f, 90.f}, {60.f, 90.f}}),
+              0);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    RefreshRateConfigs::GlobalSignals consideredSignals;
+    lr.vote = LayerVoteType::ExplicitDefault;
+    lr.desiredRefreshRate = 60.0f;
+    lr.name = "60Hz ExplicitDefault";
+    lr.focused = true;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = true, .idle = true},
+                                                     &consideredSignals));
+    EXPECT_EQ(false, consideredSignals.touch);
+}
+
+TEST_F(RefreshRateConfigsTest,
+       getBestRefreshRate_withDisplayManagerRequestingSingleRate_ignoresIdleFlag) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+                      {HWC_CONFIG_ID_60, {60.f, 60.f}, {60.f, 90.f}}),
+              0);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitDefault;
+    lr.desiredRefreshRate = 90.0f;
+    lr.name = "90Hz ExplicitDefault";
+    lr.focused = true;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = true}));
+}
+
+TEST_F(RefreshRateConfigsTest,
+       getBestRefreshRate_withDisplayManagerRequestingSingleRate_onlySwitchesRatesForExplicitFocusedLayers) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_90);
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+                      {HWC_CONFIG_ID_90, {90.f, 90.f}, {60.f, 90.f}}),
+              0);
+
+    RefreshRateConfigs::GlobalSignals consideredSignals;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false},
+                                                     &consideredSignals));
+    EXPECT_EQ(false, consideredSignals.touch);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr.desiredRefreshRate = 60.0f;
+    lr.name = "60Hz ExplicitExactOrMultiple";
+    lr.focused = false;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.focused = true;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.vote = LayerVoteType::ExplicitDefault;
+    lr.desiredRefreshRate = 60.0f;
+    lr.name = "60Hz ExplicitDefault";
+    lr.focused = false;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.focused = true;
+    EXPECT_EQ(mExpected60Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.vote = LayerVoteType::Heuristic;
+    lr.desiredRefreshRate = 60.0f;
+    lr.name = "60Hz Heuristic";
+    lr.focused = false;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.focused = true;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.vote = LayerVoteType::Max;
+    lr.desiredRefreshRate = 60.0f;
+    lr.name = "60Hz Max";
+    lr.focused = false;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.focused = true;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.vote = LayerVoteType::Min;
+    lr.desiredRefreshRate = 60.0f;
+    lr.name = "60Hz Min";
+    lr.focused = false;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+
+    lr.focused = true;
+    EXPECT_EQ(mExpected90Config,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false}));
+}
+
+TEST_F(RefreshRateConfigsTest, groupSwitching) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::ExplicitDefault;
+    layer.desiredRefreshRate = 90.0f;
+    layer.name = "90Hz ExplicitDefault";
+
+    ASSERT_EQ(HWC_CONFIG_ID_60,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                      .getConfigId());
+
+    RefreshRateConfigs::Policy policy;
+    policy.defaultConfig = refreshRateConfigs->getCurrentPolicy().defaultConfig;
+    policy.allowGroupSwitching = true;
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(policy), 0);
+    ASSERT_EQ(HWC_CONFIG_ID_90,
+              refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false})
+                      .getConfigId());
+}
+
+TEST_F(RefreshRateConfigsTest, primaryVsAppRequestPolicy) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m30_60_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,
+                            bool focused = true) -> HwcConfigIndexType {
+        layers[0].vote = voteType;
+        layers[0].desiredRefreshRate = fps;
+        layers[0].focused = focused;
+        return refreshRateConfigs->getBestRefreshRate(layers, {.touch = touchActive, .idle = false})
+                .getConfigId();
+    };
+
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy(
+                      {HWC_CONFIG_ID_60, {30.f, 60.f}, {30.f, 90.f}}),
+              0);
+    EXPECT_EQ(HWC_CONFIG_ID_60,
+              refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = false})
+                      .getConfigId());
+    EXPECT_EQ(HWC_CONFIG_ID_60, getFrameRate(LayerVoteType::NoVote, 90.f));
+    EXPECT_EQ(HWC_CONFIG_ID_30, 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_60, getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f));
+
+    // Layers not focused are not allowed to override primary config
+    EXPECT_EQ(HWC_CONFIG_ID_60,
+              getFrameRate(LayerVoteType::ExplicitDefault, 90.f, /*touch=*/false,
+                           /*focused=*/false));
+    EXPECT_EQ(HWC_CONFIG_ID_60,
+              getFrameRate(LayerVoteType::ExplicitExactOrMultiple, 90.f, /*touch=*/false,
+                           /*focused=*/false));
+
+    // 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_60,
+              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";
+
+    const auto getIdleFrameRate = [&](LayerVoteType voteType,
+                                      bool touchActive) -> HwcConfigIndexType {
+        layers[0].vote = voteType;
+        layers[0].desiredRefreshRate = 90.f;
+        RefreshRateConfigs::GlobalSignals consideredSignals;
+        const auto configId =
+                refreshRateConfigs
+                        ->getBestRefreshRate(layers, {.touch = touchActive, .idle = true},
+                                             &consideredSignals)
+                        .getConfigId();
+        // Refresh rate will be chosen by either touch state or idle state
+        EXPECT_EQ(!touchActive, consideredSignals.idle);
+        return configId;
+    };
+
+    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, /*touchActive=*/true));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/true));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/true));
+    EXPECT_EQ(HWC_CONFIG_ID_90, getIdleFrameRate(LayerVoteType::Heuristic, /*touchActive=*/true));
+    EXPECT_EQ(HWC_CONFIG_ID_90,
+              getIdleFrameRate(LayerVoteType::ExplicitDefault, /*touchActive=*/true));
+    EXPECT_EQ(HWC_CONFIG_ID_90,
+              getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, /*touchActive=*/true));
+
+    // With no layers, idle should still be lower priority than touch boost.
+    EXPECT_EQ(HWC_CONFIG_ID_90,
+              refreshRateConfigs->getBestRefreshRate({}, {.touch = true, .idle = true})
+                      .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, /*touchActive=*/false));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Min, /*touchActive=*/false));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Max, /*touchActive=*/false));
+    EXPECT_EQ(HWC_CONFIG_ID_60, getIdleFrameRate(LayerVoteType::Heuristic, /*touchActive=*/false));
+    EXPECT_EQ(HWC_CONFIG_ID_60,
+              getIdleFrameRate(LayerVoteType::ExplicitDefault, /*touchActive=*/false));
+    EXPECT_EQ(HWC_CONFIG_ID_60,
+              getIdleFrameRate(LayerVoteType::ExplicitExactOrMultiple, /*touchActive=*/false));
+
+    // Idle should be applied rather than the current config when there are no layers.
+    EXPECT_EQ(HWC_CONFIG_ID_60,
+              refreshRateConfigs->getBestRefreshRate({}, {.touch = false, .idle = true})
+                      .getConfigId());
+}
+
+TEST_F(RefreshRateConfigsTest, findClosestKnownFrameRate) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    for (float fps = 1.0f; fps <= 120.0f; fps += 0.1f) {
+        const auto knownFrameRate = findClosestKnownFrameRate(*refreshRateConfigs, fps);
+        float expectedFrameRate;
+        if (fps < 26.91f) {
+            expectedFrameRate = 24.0f;
+        } else if (fps < 37.51f) {
+            expectedFrameRate = 30.0f;
+        } else if (fps < 52.51f) {
+            expectedFrameRate = 45.0f;
+        } else if (fps < 66.01f) {
+            expectedFrameRate = 60.0f;
+        } else if (fps < 81.01f) {
+            expectedFrameRate = 72.0f;
+        } else {
+            expectedFrameRate = 90.0f;
+        }
+        EXPECT_FLOAT_EQ(expectedFrameRate, knownFrameRate)
+                << "findClosestKnownFrameRate(" << fps << ") = " << knownFrameRate;
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) {
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    struct ExpectedRate {
+        float rate;
+        const RefreshRate& expected;
+    };
+
+    /* clang-format off */
+    std::vector<ExpectedRate> knownFrameRatesExpectations = {
+        {24.0f, mExpected60Config},
+        {30.0f, mExpected60Config},
+        {45.0f, mExpected90Config},
+        {60.0f, mExpected60Config},
+        {72.0f, mExpected90Config},
+        {90.0f, mExpected90Config},
+    };
+    /* clang-format on */
+
+    // Make sure the test tests all the known frame rate
+    const auto knownFrameRateList = getKnownFrameRate(*refreshRateConfigs);
+    const auto equal = std::equal(knownFrameRateList.begin(), knownFrameRateList.end(),
+                                  knownFrameRatesExpectations.begin(),
+                                  [](float a, const ExpectedRate& b) { return a == b.rate; });
+    EXPECT_TRUE(equal);
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& layer = layers[0];
+    layer.vote = LayerVoteType::Heuristic;
+    for (const auto& expectedRate : knownFrameRatesExpectations) {
+        layer.desiredRefreshRate = expectedRate.rate;
+        const auto& refreshRate =
+                refreshRateConfigs->getBestRefreshRate(layers, {.touch = false, .idle = false});
+        EXPECT_EQ(expectedRate.expected, refreshRate);
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
+    EXPECT_TRUE(mExpected60Config < mExpected90Config);
+    EXPECT_FALSE(mExpected60Config < mExpected60Config);
+    EXPECT_FALSE(mExpected90Config < mExpected90Config);
+}
+
+TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) {
+    using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;
+
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(m60_90Device,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_90);
+    // SetPolicy(60, 90), current 90Hz => TurnOn.
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+    // SetPolicy(60, 90), current 60Hz => TurnOn.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 90}}), 0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());
+
+    // SetPolicy(60, 60), current 60Hz => NoChange, avoid extra calls.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
+    EXPECT_EQ(KernelIdleTimerAction::NoChange, refreshRateConfigs->getIdleTimerAction());
+
+    // SetPolicy(90, 90), current 90Hz => TurnOff.
+    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
+    EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
+}
+
 } // namespace
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
new file mode 100644
index 0000000..43b8e01
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -0,0 +1,288 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "EffectLayer.h"
+#include "Layer.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockDispSync.h"
+#include "mock/MockEventControlThread.h"
+#include "mock/MockEventThread.h"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+/**
+ * This class covers all the test that are related to refresh rate selection.
+ */
+class RefreshRateSelectionTest : public testing::Test {
+public:
+    RefreshRateSelectionTest();
+    ~RefreshRateSelectionTest() override;
+
+protected:
+    static constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
+    static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
+    static constexpr uint32_t WIDTH = 100;
+    static constexpr uint32_t HEIGHT = 100;
+    static constexpr uint32_t LAYER_FLAGS = 0;
+    static constexpr int32_t PRIORITY_UNSET = -1;
+
+    void setupScheduler();
+    void setupComposer(int virtualDisplayCount);
+    sp<BufferQueueLayer> createBufferQueueLayer();
+    sp<BufferStateLayer> createBufferStateLayer();
+    sp<EffectLayer> createEffectLayer();
+
+    void setParent(Layer* child, Layer* parent);
+    void commitTransaction(Layer* layer);
+
+    TestableSurfaceFlinger mFlinger;
+    Hwc2::mock::Composer* mComposer = nullptr;
+
+    sp<Client> mClient;
+    sp<Layer> mParent;
+    sp<Layer> mChild;
+    sp<Layer> mGrandChild;
+};
+
+RefreshRateSelectionTest::RefreshRateSelectionTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+    setupScheduler();
+    setupComposer(0);
+}
+
+RefreshRateSelectionTest::~RefreshRateSelectionTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+sp<BufferQueueLayer> RefreshRateSelectionTest::createBufferQueueLayer() {
+    sp<Client> client;
+    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+                           LAYER_FLAGS, LayerMetadata());
+    return new BufferQueueLayer(args);
+}
+
+sp<BufferStateLayer> RefreshRateSelectionTest::createBufferStateLayer() {
+    sp<Client> client;
+    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+                           LAYER_FLAGS, LayerMetadata());
+    return new BufferStateLayer(args);
+}
+
+sp<EffectLayer> RefreshRateSelectionTest::createEffectLayer() {
+    sp<Client> client;
+    LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS,
+                           LayerMetadata());
+    return new EffectLayer(args);
+}
+
+void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) {
+    child->setParent(parent);
+}
+
+void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
+    layer->commitTransaction(layer->getCurrentState());
+}
+
+void RefreshRateSelectionTest::setupScheduler() {
+    auto eventThread = std::make_unique<mock::EventThread>();
+    auto sfEventThread = std::make_unique<mock::EventThread>();
+
+    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*eventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+                                                       ISurfaceComposer::eConfigChangedSuppress)));
+
+    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+                                                       ISurfaceComposer::eConfigChangedSuppress)));
+
+    auto primaryDispSync = std::make_unique<mock::DispSync>();
+
+    EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*primaryDispSync, getPeriod())
+            .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+    EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
+    mFlinger.setupScheduler(std::move(primaryDispSync),
+                            std::make_unique<mock::EventControlThread>(), std::move(eventThread),
+                            std::move(sfEventThread));
+}
+
+void RefreshRateSelectionTest::setupComposer(int virtualDisplayCount) {
+    mComposer = new Hwc2::mock::Composer();
+    EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+    mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+    Mock::VerifyAndClear(mComposer);
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(RefreshRateSelectionTest, testPriorityOnBufferQueueLayers) {
+    mParent = createBufferQueueLayer();
+    mChild = createBufferQueueLayer();
+    setParent(mChild.get(), mParent.get());
+    mGrandChild = createBufferQueueLayer();
+    setParent(mGrandChild.get(), mChild.get());
+
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
+
+    // Child has its own priority.
+    mGrandChild->setFrameRateSelectionPriority(1);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+    // Child inherits from his parent.
+    mChild->setFrameRateSelectionPriority(1);
+    commitTransaction(mChild.get());
+    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mGrandChild.get());
+
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+    // Grandchild inherits from his grand parent.
+    mParent->setFrameRateSelectionPriority(1);
+    commitTransaction(mParent.get());
+    mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mChild.get());
+    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+}
+
+TEST_F(RefreshRateSelectionTest, testPriorityOnBufferStateLayers) {
+    mParent = createBufferStateLayer();
+    mChild = createBufferStateLayer();
+    setParent(mChild.get(), mParent.get());
+    mGrandChild = createBufferStateLayer();
+    setParent(mGrandChild.get(), mChild.get());
+
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
+
+    // Child has its own priority.
+    mGrandChild->setFrameRateSelectionPriority(1);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+    // Child inherits from his parent.
+    mChild->setFrameRateSelectionPriority(1);
+    commitTransaction(mChild.get());
+    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+    // Grandchild inherits from his grand parent.
+    mParent->setFrameRateSelectionPriority(1);
+    commitTransaction(mParent.get());
+    mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mChild.get());
+    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+}
+
+TEST_F(RefreshRateSelectionTest, testPriorityOnEffectLayers) {
+    mParent = createEffectLayer();
+    mChild = createEffectLayer();
+    setParent(mChild.get(), mParent.get());
+    mGrandChild = createEffectLayer();
+    setParent(mGrandChild.get(), mChild.get());
+
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
+
+    // Child has its own priority.
+    mGrandChild->setFrameRateSelectionPriority(1);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+    // Child inherits from his parent.
+    mChild->setFrameRateSelectionPriority(1);
+    commitTransaction(mChild.get());
+    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+    // Grandchild inherits from his grand parent.
+    mParent->setFrameRateSelectionPriority(1);
+    commitTransaction(mParent.get());
+    mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mChild.get());
+    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+}
+
+} // namespace
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index cec0b32..de66f8f 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "SchedulerUnittests"
 
@@ -22,9 +26,11 @@
 #include <thread>
 
 #include "Scheduler/RefreshRateStats.h"
+#include "mock/DisplayHardware/MockDisplay.h"
 #include "mock/MockTimeStats.h"
 
 using namespace std::chrono_literals;
+using android::hardware::graphics::composer::hal::PowerMode;
 using testing::_;
 using testing::AtLeast;
 
@@ -33,26 +39,31 @@
 
 class RefreshRateStatsTest : public testing::Test {
 protected:
-    static constexpr int CONFIG_ID_90 = 0;
-    static constexpr int CONFIG_ID_60 = 1;
+    static inline const auto CONFIG_ID_0 = HwcConfigIndexType(0);
+    static inline const auto CONFIG_ID_1 = HwcConfigIndexType(1);
+    static inline const auto CONFIG_GROUP_0 = 0;
     static constexpr int64_t VSYNC_90 = 11111111;
     static constexpr int64_t VSYNC_60 = 16666667;
 
     RefreshRateStatsTest();
     ~RefreshRateStatsTest();
 
-    void init(const std::vector<RefreshRateConfigs::InputConfig>& configs) {
-        mRefreshRateConfigs = std::make_unique<RefreshRateConfigs>(
-                /*refreshRateSwitching=*/true, configs, /*currentConfig=*/0);
-        mRefreshRateStats =
-                std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats,
-                                                   /*currentConfig=*/0,
-                                                   /*currentPowerMode=*/HWC_POWER_MODE_OFF);
+    void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
+        mRefreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(configs, /*currentConfig=*/CONFIG_ID_0);
+        mRefreshRateStats = std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats,
+                                                               /*currentConfigId=*/CONFIG_ID_0,
+                                                               /*currentPowerMode=*/PowerMode::OFF);
     }
 
+    Hwc2::mock::Display mDisplay;
     mock::TimeStats mTimeStats;
     std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
     std::unique_ptr<RefreshRateStats> mRefreshRateStats;
+
+    std::shared_ptr<const HWC2::Display::Config> createConfig(HwcConfigIndexType configId,
+                                                              int32_t configGroup,
+                                                              int64_t vsyncPeriod);
 };
 
 RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -67,12 +78,20 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
+std::shared_ptr<const HWC2::Display::Config> RefreshRateStatsTest::createConfig(
+        HwcConfigIndexType configId, int32_t configGroup, int64_t vsyncPeriod) {
+    return HWC2::Display::Config::Builder(mDisplay, configId.value())
+            .setVsyncPeriod(int32_t(vsyncPeriod))
+            .setConfigGroup(configGroup)
+            .build();
+}
+
 namespace {
 /* ------------------------------------------------------------------------
  * Test cases
  */
 TEST_F(RefreshRateStatsTest, oneConfigTest) {
-    init({{CONFIG_ID_90, VSYNC_90}});
+    init({createConfig(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90)});
 
     EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
@@ -91,8 +110,8 @@
     EXPECT_LT(screenOff, times["ScreenOff"]);
     EXPECT_EQ(0u, times.count("90fps"));
 
-    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
-    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    mRefreshRateStats->setConfigMode(CONFIG_ID_0);
+    mRefreshRateStats->setPowerMode(PowerMode::ON);
     screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
@@ -100,25 +119,26 @@
     ASSERT_EQ(1u, times.count("90fps"));
     EXPECT_LT(0, times["90fps"]);
 
-    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+    mRefreshRateStats->setPowerMode(PowerMode::DOZE);
     int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
 
-    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    mRefreshRateStats->setConfigMode(CONFIG_ID_0);
     screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
-    // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
+    // Because the power mode is not PowerMode::ON, switching the config
     // does not update refresh rates that come from the config.
     EXPECT_LT(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
 }
 
 TEST_F(RefreshRateStatsTest, twoConfigsTest) {
-    init({{CONFIG_ID_90, VSYNC_90}, {CONFIG_ID_60, VSYNC_60}});
+    init({createConfig(CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90),
+          createConfig(CONFIG_ID_1, CONFIG_GROUP_0, VSYNC_60)});
 
     EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
@@ -137,8 +157,8 @@
     times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
 
-    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
-    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    mRefreshRateStats->setConfigMode(CONFIG_ID_0);
+    mRefreshRateStats->setPowerMode(PowerMode::ON);
     screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
@@ -147,7 +167,7 @@
     EXPECT_LT(0, times["90fps"]);
 
     // When power mode is normal, time for configs updates.
-    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    mRefreshRateStats->setConfigMode(CONFIG_ID_1);
     int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
@@ -156,7 +176,7 @@
     ASSERT_EQ(1u, times.count("60fps"));
     EXPECT_LT(0, times["60fps"]);
 
-    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    mRefreshRateStats->setConfigMode(CONFIG_ID_0);
     int sixty = mRefreshRateStats->getTotalTimes()["60fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
@@ -164,7 +184,7 @@
     EXPECT_LT(ninety, times["90fps"]);
     EXPECT_EQ(sixty, times["60fps"]);
 
-    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    mRefreshRateStats->setConfigMode(CONFIG_ID_1);
     ninety = mRefreshRateStats->getTotalTimes()["90fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
@@ -172,10 +192,10 @@
     EXPECT_EQ(ninety, times["90fps"]);
     EXPECT_LT(sixty, times["60fps"]);
 
-    // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
+    // Because the power mode is not PowerMode::ON, switching the config
     // does not update refresh rates that come from the config.
-    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
-    mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+    mRefreshRateStats->setPowerMode(PowerMode::DOZE);
+    mRefreshRateStats->setConfigMode(CONFIG_ID_0);
     sixty = mRefreshRateStats->getTotalTimes()["60fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
@@ -183,7 +203,7 @@
     EXPECT_EQ(ninety, times["90fps"]);
     EXPECT_EQ(sixty, times["60fps"]);
 
-    mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+    mRefreshRateStats->setConfigMode(CONFIG_ID_1);
     screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
     times = mRefreshRateStats->getTotalTimes();
@@ -194,3 +214,6 @@
 } // namespace
 } // namespace scheduler
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
index 160f041..f19e554 100644
--- a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "RegionSamplingTest"
 
@@ -69,16 +73,16 @@
         n++;
         return pixel;
     });
+
     EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area),
-                testing::FloatNear(0.083f, 0.01f));
+                testing::FloatNear(0.16f, 0.01f));
 }
 
 TEST_F(RegionSamplingTest, bimodal_tiebreaker) {
     std::generate(buffer.begin(), buffer.end(),
                   [n = 0]() mutable { return (n++ % 2) ? kBlack : kWhite; });
-    // presently there's no tiebreaking strategy in place, accept either of the means
     EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area),
-                testing::AnyOf(testing::FloatEq(1.0), testing::FloatEq(0.0f)));
+                testing::FloatEq(0.5f));
 }
 
 TEST_F(RegionSamplingTest, bounds_checking) {
@@ -137,3 +141,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 571fdfd..1aa7320 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -1,3 +1,23 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "SchedulerUnittests"
 
@@ -10,7 +30,8 @@
 #include "Scheduler/EventControlThread.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/RefreshRateConfigs.h"
-#include "Scheduler/Scheduler.h"
+#include "TestableScheduler.h"
+#include "mock/DisplayHardware/MockDisplay.h"
 #include "mock/MockEventThread.h"
 
 using testing::_;
@@ -34,38 +55,16 @@
         MOCK_METHOD0(requestNextVsync, void());
     };
 
-    std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
-
-    /**
-     * This mock Scheduler class uses implementation of mock::EventThread but keeps everything else
-     * the same.
-     */
-    class MockScheduler : public android::Scheduler {
-    public:
-        MockScheduler(const scheduler::RefreshRateConfigs& refreshRateConfigs,
-                      std::unique_ptr<EventThread> eventThread)
-              : Scheduler([](bool) {}, refreshRateConfigs), mEventThread(std::move(eventThread)) {}
-
-        std::unique_ptr<EventThread> makeEventThread(
-                const char* /* connectionName */, DispSync* /* dispSync */,
-                nsecs_t /* phaseOffsetNs */, nsecs_t /* offsetThresholdForNextVsync */,
-                impl::EventThread::InterceptVSyncsCallback /* interceptCallback */) override {
-            return std::move(mEventThread);
-        }
-
-        MockScheduler() = default;
-        ~MockScheduler() override = default;
-
-        std::unique_ptr<EventThread> mEventThread;
-    };
-
     SchedulerTest();
     ~SchedulerTest() override;
 
-    sp<Scheduler::ConnectionHandle> mConnectionHandle;
+    std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+    std::unique_ptr<TestableScheduler> mScheduler;
+
+    Scheduler::ConnectionHandle mConnectionHandle;
     mock::EventThread* mEventThread;
-    std::unique_ptr<MockScheduler> mScheduler;
     sp<MockEventThreadConnection> mEventThreadConnection;
+    Hwc2::mock::Display mDisplay;
 };
 
 SchedulerTest::SchedulerTest() {
@@ -73,14 +72,18 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
-    mRefreshRateConfigs =
-            std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
-                                                            /*currentConfig=*/0);
+    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
+            HWC2::Display::Config::Builder(mDisplay, 0)
+                    .setVsyncPeriod(int32_t(16666667))
+                    .setConfigGroup(0)
+                    .build()};
+    mRefreshRateConfigs = std::make_unique<
+            scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
 
-    std::unique_ptr<mock::EventThread> eventThread = std::make_unique<mock::EventThread>();
+    mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs, false);
+
+    auto eventThread = std::make_unique<mock::EventThread>();
     mEventThread = eventThread.get();
-    mScheduler = std::make_unique<MockScheduler>(*mRefreshRateConfigs, std::move(eventThread));
     EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
 
     mEventThreadConnection = new MockEventThreadConnection(mEventThread);
@@ -90,9 +93,8 @@
     EXPECT_CALL(*mEventThread, createEventConnection(_, _))
             .WillRepeatedly(Return(mEventThreadConnection));
 
-    mConnectionHandle = mScheduler->createConnection("appConnection", 16, 16,
-                                                     impl::EventThread::InterceptVSyncsCallback());
-    EXPECT_TRUE(mConnectionHandle != nullptr);
+    mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
+    EXPECT_TRUE(mConnectionHandle);
 }
 
 SchedulerTest::~SchedulerTest() {
@@ -106,78 +108,48 @@
  * Test cases
  */
 
-TEST_F(SchedulerTest, testNullPtr) {
-    // Passing a null pointer for ConnectionHandle is a valid argument. The code doesn't throw any
-    // exceptions, just gracefully continues.
-    sp<IDisplayEventConnection> returnedValue;
-    ASSERT_NO_FATAL_FAILURE(
-            returnedValue =
-                    mScheduler->createDisplayEventConnection(nullptr,
-                                                             ISurfaceComposer::
-                                                                     eConfigChangedSuppress));
-    EXPECT_TRUE(returnedValue == nullptr);
-    EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr);
-    EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(nullptr, PHYSICAL_DISPLAY_ID, false));
-    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(nullptr));
-    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(nullptr));
-    std::string testString;
-    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(nullptr, testString));
-    EXPECT_TRUE(testString == "");
-    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(nullptr, 10));
-}
-
 TEST_F(SchedulerTest, invalidConnectionHandle) {
-    // Passing an invalid ConnectionHandle is a valid argument. The code doesn't throw any
-    // exceptions, just gracefully continues.
-    sp<Scheduler::ConnectionHandle> connectionHandle = new Scheduler::ConnectionHandle(20);
+    Scheduler::ConnectionHandle handle;
 
-    sp<IDisplayEventConnection> returnedValue;
+    sp<IDisplayEventConnection> connection;
     ASSERT_NO_FATAL_FAILURE(
-            returnedValue =
-                    mScheduler->createDisplayEventConnection(connectionHandle,
-                                                             ISurfaceComposer::
-                                                                     eConfigChangedSuppress));
-    EXPECT_TRUE(returnedValue == nullptr);
-    EXPECT_TRUE(mScheduler->getEventThread(connectionHandle) == nullptr);
-    EXPECT_TRUE(mScheduler->getEventConnection(connectionHandle) == nullptr);
+            connection = mScheduler->createDisplayEventConnection(handle,
+                                                                  ISurfaceComposer::
+                                                                          eConfigChangedSuppress));
+    EXPECT_FALSE(connection);
+    EXPECT_FALSE(mScheduler->getEventConnection(handle));
 
     // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads.
     EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(
-            mScheduler->hotplugReceived(connectionHandle, PHYSICAL_DISPLAY_ID, false));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false));
 
     EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(connectionHandle));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(handle));
 
     EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(connectionHandle));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(handle));
 
-    std::string testString;
+    std::string output;
     EXPECT_CALL(*mEventThread, dump(_)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(connectionHandle, testString));
-    EXPECT_TRUE(testString == "");
+    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(handle, output));
+    EXPECT_TRUE(output.empty());
 
     EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(connectionHandle, 10));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(handle, 10));
 }
 
 TEST_F(SchedulerTest, validConnectionHandle) {
-    sp<IDisplayEventConnection> returnedValue;
+    sp<IDisplayEventConnection> connection;
     ASSERT_NO_FATAL_FAILURE(
-            returnedValue =
-                    mScheduler->createDisplayEventConnection(mConnectionHandle,
-                                                             ISurfaceComposer::
-                                                                     eConfigChangedSuppress));
-    EXPECT_TRUE(returnedValue != nullptr);
-    ASSERT_EQ(returnedValue, mEventThreadConnection);
-
-    EXPECT_TRUE(mScheduler->getEventThread(mConnectionHandle) != nullptr);
-    EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle) != nullptr);
+            connection = mScheduler->createDisplayEventConnection(mConnectionHandle,
+                                                                  ISurfaceComposer::
+                                                                          eConfigChangedSuppress));
+    ASSERT_EQ(mEventThreadConnection, connection);
+    EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
 
     EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
     ASSERT_NO_FATAL_FAILURE(
-            mScheduler->hotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
+            mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
 
     EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
     ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle));
@@ -185,13 +157,22 @@
     EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1);
     ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle));
 
-    std::string testString("dump");
-    EXPECT_CALL(*mEventThread, dump(testString)).Times(1);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, testString));
-    EXPECT_TRUE(testString != "");
+    std::string output("dump");
+    EXPECT_CALL(*mEventThread, dump(output)).Times(1);
+    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, output));
+    EXPECT_FALSE(output.empty());
 
     EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
     ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
+
+    static constexpr size_t kEventConnections = 5;
+    ON_CALL(*mEventThread, getEventThreadConnectionCount())
+            .WillByDefault(Return(kEventConnections));
+    EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
 }
+
 } // namespace
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
index 5865579..5f6a715 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "SchedulerUnittests"
 
@@ -128,4 +132,6 @@
 
 } // namespace
 } // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
new file mode 100644
index 0000000..0d6c799
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -0,0 +1,473 @@
+/*
+ * 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 "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "EffectLayer.h"
+#include "Layer.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockDispSync.h"
+#include "mock/MockEventControlThread.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+using FrameRate = Layer::FrameRate;
+using FrameRateCompatibility = Layer::FrameRateCompatibility;
+
+class LayerFactory {
+public:
+    virtual ~LayerFactory() = default;
+
+    virtual std::string name() = 0;
+    virtual sp<Layer> createLayer(TestableSurfaceFlinger& flinger) = 0;
+
+protected:
+    static constexpr uint32_t WIDTH = 100;
+    static constexpr uint32_t HEIGHT = 100;
+    static constexpr uint32_t LAYER_FLAGS = 0;
+};
+
+class BufferQueueLayerFactory : public LayerFactory {
+public:
+    std::string name() override { return "BufferQueueLayer"; }
+    sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
+        sp<Client> client;
+        LayerCreationArgs args(flinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+                               LAYER_FLAGS, LayerMetadata());
+        return new BufferQueueLayer(args);
+    }
+};
+
+class BufferStateLayerFactory : public LayerFactory {
+public:
+    std::string name() override { return "BufferStateLayer"; }
+    sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
+        sp<Client> client;
+        LayerCreationArgs args(flinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+                               LAYER_FLAGS, LayerMetadata());
+        return new BufferStateLayer(args);
+    }
+};
+
+class EffectLayerFactory : public LayerFactory {
+public:
+    std::string name() override { return "EffectLayer"; }
+    sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
+        sp<Client> client;
+        LayerCreationArgs args(flinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS,
+                               LayerMetadata());
+        return new EffectLayer(args);
+    }
+};
+
+std::string PrintToStringParamName(
+        const ::testing::TestParamInfo<std::shared_ptr<LayerFactory>>& info) {
+    return info.param->name();
+}
+
+/**
+ * This class tests the behaviour of Layer::SetFrameRate and Layer::GetFrameRate
+ */
+class SetFrameRateTest : public ::testing::TestWithParam<std::shared_ptr<LayerFactory>> {
+protected:
+    const FrameRate FRAME_RATE_VOTE1 = FrameRate(67.f, FrameRateCompatibility::Default);
+    const FrameRate FRAME_RATE_VOTE2 = FrameRate(14.f, FrameRateCompatibility::ExactOrMultiple);
+    const FrameRate FRAME_RATE_VOTE3 = FrameRate(99.f, FrameRateCompatibility::NoVote);
+    const FrameRate FRAME_RATE_TREE = FrameRate(0, FrameRateCompatibility::NoVote);
+    const FrameRate FRAME_RATE_NO_VOTE = FrameRate(0, FrameRateCompatibility::Default);
+
+    SetFrameRateTest();
+
+    void setupScheduler();
+    void setupComposer(uint32_t virtualDisplayCount);
+
+    void addChild(sp<Layer> layer, sp<Layer> child);
+    void removeChild(sp<Layer> layer, sp<Layer> child);
+    void reparentChildren(sp<Layer> layer, sp<Layer> child);
+    void commitTransaction();
+
+    TestableSurfaceFlinger mFlinger;
+    Hwc2::mock::Composer* mComposer = nullptr;
+    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+
+    std::vector<sp<Layer>> mLayers;
+};
+
+SetFrameRateTest::SetFrameRateTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+    mFlinger.mutableUseFrameRateApi() = true;
+
+    setupScheduler();
+    setupComposer(0);
+
+    mFlinger.mutableEventQueue().reset(mMessageQueue);
+}
+void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) {
+    layer.get()->addChild(child.get());
+}
+
+void SetFrameRateTest::removeChild(sp<Layer> layer, sp<Layer> child) {
+    layer.get()->removeChild(child.get());
+}
+
+void SetFrameRateTest::reparentChildren(sp<Layer> parent, sp<Layer> newParent) {
+    parent.get()->reparentChildren(newParent);
+}
+
+void SetFrameRateTest::commitTransaction() {
+    for (auto layer : mLayers) {
+        layer.get()->commitTransaction(layer.get()->getCurrentState());
+    }
+}
+
+void SetFrameRateTest::setupScheduler() {
+    auto eventThread = std::make_unique<mock::EventThread>();
+    auto sfEventThread = std::make_unique<mock::EventThread>();
+
+    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*eventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+                                                       ISurfaceComposer::eConfigChangedSuppress)));
+
+    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+                                                       ISurfaceComposer::eConfigChangedSuppress)));
+
+    auto primaryDispSync = std::make_unique<mock::DispSync>();
+
+    EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*primaryDispSync, getPeriod())
+            .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+    EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
+    mFlinger.setupScheduler(std::move(primaryDispSync),
+                            std::make_unique<mock::EventControlThread>(), std::move(eventThread),
+                            std::move(sfEventThread));
+}
+
+void SetFrameRateTest::setupComposer(uint32_t virtualDisplayCount) {
+    mComposer = new Hwc2::mock::Composer();
+    EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+    mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+    Mock::VerifyAndClear(mComposer);
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_P(SetFrameRateTest, SetAndGet) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    layer->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetParent) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+
+    child2->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    child2->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetParentAllVote) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+
+    child2->setFrameRate(FRAME_RATE_VOTE1);
+    child1->setFrameRate(FRAME_RATE_VOTE2);
+    parent->setFrameRate(FRAME_RATE_VOTE3);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    child2->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+    child1->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+    parent->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChild) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+
+    parent->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+    parent->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChildAllVote) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+
+    child2->setFrameRate(FRAME_RATE_VOTE1);
+    child1->setFrameRate(FRAME_RATE_VOTE2);
+    parent->setFrameRate(FRAME_RATE_VOTE3);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    parent->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    child1->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    child2->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+
+    parent->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+
+    addChild(child1, child2);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+    parent->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+
+    parent->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+    removeChild(child1, child2);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+
+    parent->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2_1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+    addChild(child1, child2_1);
+
+    child2->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
+
+    child2->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetRearentChildren) {
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    const auto& layerFactory = GetParam();
+
+    auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto parent2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+    auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+    addChild(parent, child1);
+    addChild(child1, child2);
+
+    child2->setFrameRate(FRAME_RATE_VOTE1);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    reparentChildren(parent, parent2);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, parent2->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+    child2->setFrameRate(FRAME_RATE_NO_VOTE);
+    commitTransaction();
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+    EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest,
+                         testing::Values(std::make_shared<BufferQueueLayerFactory>(),
+                                         std::make_shared<BufferStateLayerFactory>(),
+                                         std::make_shared<EffectLayerFactory>()),
+                         PrintToStringParamName);
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
new file mode 100644
index 0000000..5406879
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include "Scheduler/StrongTyping.h"
+
+using namespace testing;
+
+namespace android {
+
+TEST(StrongTypeTest, comparison) {
+    using SpunkyType = StrongTyping<int, struct SpunkyTypeTag, Compare>;
+    SpunkyType f2(22);
+    SpunkyType f1(10);
+
+    EXPECT_TRUE(f1 == f1);
+    EXPECT_TRUE(SpunkyType(10) != SpunkyType(11));
+    EXPECT_FALSE(SpunkyType(31) != SpunkyType(31));
+
+    EXPECT_TRUE(SpunkyType(10) < SpunkyType(11));
+    EXPECT_TRUE(SpunkyType(-1) < SpunkyType(0));
+    EXPECT_FALSE(SpunkyType(-10) < SpunkyType(-20));
+
+    EXPECT_TRUE(SpunkyType(10) <= SpunkyType(11));
+    EXPECT_TRUE(SpunkyType(10) <= SpunkyType(10));
+    EXPECT_TRUE(SpunkyType(-10) <= SpunkyType(1));
+    EXPECT_FALSE(SpunkyType(10) <= SpunkyType(9));
+
+    EXPECT_TRUE(SpunkyType(11) >= SpunkyType(11));
+    EXPECT_TRUE(SpunkyType(12) >= SpunkyType(11));
+    EXPECT_FALSE(SpunkyType(11) >= SpunkyType(12));
+
+    EXPECT_FALSE(SpunkyType(11) > SpunkyType(12));
+    EXPECT_TRUE(SpunkyType(-11) < SpunkyType(7));
+}
+
+TEST(StrongTypeTest, addition) {
+    using FunkyType = StrongTyping<int, struct FunkyTypeTag, Compare, Add>;
+    FunkyType f2(22);
+    FunkyType f1(10);
+
+    EXPECT_THAT(f1 + f2, Eq(FunkyType(32)));
+    EXPECT_THAT(f2 + f1, Eq(FunkyType(32)));
+
+    EXPECT_THAT(++f1.value(), Eq(11));
+    EXPECT_THAT(f1.value(), Eq(11));
+    EXPECT_THAT(f1++.value(), Eq(11));
+    EXPECT_THAT(f1++.value(), Eq(12));
+    EXPECT_THAT(f1.value(), Eq(13));
+
+    auto f3 = f1;
+    EXPECT_THAT(f1, Eq(f3));
+    EXPECT_THAT(f1, Lt(f2));
+
+    f3 += f1;
+    EXPECT_THAT(f1.value(), Eq(13));
+    EXPECT_THAT(f3.value(), Eq(26));
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index cb6980e..d5ecae8 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -19,30 +19,49 @@
 #include <gmock/gmock.h>
 #include <gui/ISurfaceComposer.h>
 
+#include "Scheduler/DispSync.h"
 #include "Scheduler/EventThread.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/LayerHistory.h"
 #include "Scheduler/Scheduler.h"
 
 namespace android {
 
-class TestableScheduler : public Scheduler {
+class TestableScheduler : public Scheduler, private ISchedulerCallback {
 public:
-    TestableScheduler(const scheduler::RefreshRateConfigs& refreshRateConfig)
-          : Scheduler([](bool) {}, refreshRateConfig) {}
+    TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
+          : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {
+        if (mUseContentDetectionV2) {
+            mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
+        } else {
+            mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+        }
+    }
 
-    // Creates EventThreadConnection with the given eventThread. Creates Scheduler::Connection
-    // and adds it to the list of connectins. Returns the ConnectionHandle for the
-    // Scheduler::Connection. This allows plugging in mock::EventThread.
-    sp<Scheduler::ConnectionHandle> addConnection(std::unique_ptr<EventThread> eventThread) {
-        sp<EventThreadConnection> eventThreadConnection =
-                new EventThreadConnection(eventThread.get(), ResyncCallback(),
-                                          ISurfaceComposer::eConfigChangedSuppress);
-        const int64_t id = sNextId++;
-        mConnections.emplace(id,
-                             std::make_unique<Scheduler::Connection>(new ConnectionHandle(id),
-                                                                     eventThreadConnection,
-                                                                     std::move(eventThread)));
-        return mConnections[id]->handle;
+    TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
+                      std::unique_ptr<EventControlThread> eventControlThread,
+                      const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
+          : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
+                      useContentDetectionV2, true) {
+        if (mUseContentDetectionV2) {
+            mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs);
+        } else {
+            mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+        }
+    }
+
+    // Used to inject mock event thread.
+    ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
+        return Scheduler::createConnection(std::move(eventThread));
+    }
+
+    size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
+        if (mUseContentDetectionV2) {
+            return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get())
+                    ->mLayerInfos.size();
+        } else {
+            return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get())
+                    ->mLayerInfos.size();
+        }
     }
 
     /* ------------------------------------------------------------------------
@@ -53,6 +72,12 @@
     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());
+    }
 
     ~TestableScheduler() {
         // All these pointer and container clears help ensure that GMock does
@@ -62,7 +87,12 @@
         mutableEventControlThread().reset();
         mutablePrimaryDispSync().reset();
         mConnections.clear();
-    };
+    }
+
+private:
+    void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
+    void repaintEverythingForHWC() override {}
+    void kernelTimerChanged(bool /*expired*/) override {}
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 1c1b020..38bc8a1 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -17,17 +17,18 @@
 #pragma once
 
 #include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/CompositionEngine.h>
-#include <compositionengine/impl/LayerCompositionState.h>
+#include <compositionengine/impl/Display.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/mock/DisplaySurface.h>
 
 #include "BufferQueueLayer.h"
 #include "BufferStateLayer.h"
-#include "ColorLayer.h"
 #include "ContainerLayer.h"
 #include "DisplayDevice.h"
+#include "EffectLayer.h"
 #include "FakePhaseOffsets.h"
 #include "Layer.h"
 #include "NativeWindowSurface.h"
@@ -35,9 +36,10 @@
 #include "Scheduler/RefreshRateConfigs.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
-#include "SurfaceFlingerFactory.h"
+#include "SurfaceFlingerDefaultFactory.h"
 #include "SurfaceInterceptor.h"
-#include "TimeStats/TimeStats.h"
+#include "TestableScheduler.h"
+#include "mock/DisplayHardware/MockDisplay.h"
 
 namespace android {
 
@@ -55,72 +57,82 @@
 
 } // namespace Hwc2
 
+namespace hal = android::hardware::graphics::composer::hal;
+
 namespace surfaceflinger::test {
 
 class Factory final : public surfaceflinger::Factory {
 public:
     ~Factory() = default;
 
-    std::unique_ptr<DispSync> createDispSync(const char*, bool, int64_t) override {
-        // TODO: Use test-fixture controlled factory
+    std::unique_ptr<DispSync> createDispSync(const char*, bool) override {
         return nullptr;
     }
 
     std::unique_ptr<EventControlThread> createEventControlThread(
             std::function<void(bool)>) override {
-        // TODO: Use test-fixture controlled factory
         return nullptr;
     }
 
     std::unique_ptr<HWComposer> createHWComposer(const std::string&) override {
-        // TODO: Use test-fixture controlled factory
         return nullptr;
     }
 
     std::unique_ptr<MessageQueue> createMessageQueue() override {
-        // TODO: Use test-fixture controlled factory
         return std::make_unique<android::impl::MessageQueue>();
     }
 
-    std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
+    std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+            const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
         return std::make_unique<scheduler::FakePhaseOffsets>();
     }
 
     std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>,
-                                               const scheduler::RefreshRateConfigs&) override {
-        // TODO: Use test-fixture controlled factory
+                                               const scheduler::RefreshRateConfigs&,
+                                               ISchedulerCallback&) override {
         return nullptr;
     }
 
     std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger* flinger) override {
-        // TODO: Use test-fixture controlled factory
         return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
     }
 
     sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
-        // TODO: Use test-fixture controlled factory
         return new StartPropertySetThread(timestampPropertyValue);
     }
 
-    sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) override {
-        // TODO: Use test-fixture controlled factory
-        return new DisplayDevice(std::move(creationArgs));
+    sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) override {
+        return new DisplayDevice(creationArgs);
     }
 
     sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
                                           uint32_t layerCount, uint64_t usage,
                                           std::string requestorName) override {
-        // TODO: Use test-fixture controlled factory
         return new GraphicBuffer(width, height, format, layerCount, usage, requestorName);
     }
 
     void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
                            sp<IGraphicBufferConsumer>* outConsumer,
                            bool consumerIsSurfaceFlinger) override {
-        if (!mCreateBufferQueue) return;
+        if (!mCreateBufferQueue) {
+            BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
+            return;
+        }
         mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
     }
 
+    sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>& producer,
+                                                       const sp<SurfaceFlinger>& flinger,
+                                                       const wp<Layer>& layer) override {
+        return new MonitoredProducer(producer, flinger, layer);
+    }
+
+    sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>& consumer,
+                                                      renderengine::RenderEngine& renderEngine,
+                                                      uint32_t textureName, Layer* layer) override {
+        return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
+    }
+
     std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer>& producer) override {
         if (!mCreateNativeWindowSurface) return nullptr;
@@ -132,30 +144,19 @@
     }
 
     sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs&) override {
-        // TODO: Use test-fixture controlled factory
         return nullptr;
     }
 
     sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs&) override {
-        // TODO: Use test-fixture controlled factory
         return nullptr;
     }
 
-    sp<ColorLayer> createColorLayer(const LayerCreationArgs&) override {
-        // TODO: Use test-fixture controlled factory
-        return nullptr;
-    }
+    sp<EffectLayer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; }
 
     sp<ContainerLayer> createContainerLayer(const LayerCreationArgs&) override {
-        // TODO: Use test-fixture controlled factory
         return nullptr;
     }
 
-    std::shared_ptr<TimeStats> createTimeStats() override {
-        // TODO: Use test-fixture controlled factory
-        return std::make_shared<android::impl::TimeStats>();
-    }
-
     using CreateBufferQueueFunction =
             std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
                                sp<IGraphicBufferConsumer>* /* outConsumer */,
@@ -176,6 +177,11 @@
 
 class TestableSurfaceFlinger {
 public:
+    using HotplugEvent = SurfaceFlinger::HotplugEvent;
+
+    SurfaceFlinger* flinger() { return mFlinger.get(); }
+    TestableScheduler* scheduler() { return mScheduler; }
+
     // Extend this as needed for accessing SurfaceFlinger private (and public)
     // functions.
 
@@ -188,6 +194,45 @@
                 std::make_unique<impl::HWComposer>(std::move(composer)));
     }
 
+    void setupTimeStats(const std::shared_ptr<TimeStats>& timeStats) {
+        mFlinger->mCompositionEngine->setTimeStats(timeStats);
+    }
+
+    void setupScheduler(std::unique_ptr<DispSync> primaryDispSync,
+                        std::unique_ptr<EventControlThread> eventControlThread,
+                        std::unique_ptr<EventThread> appEventThread,
+                        std::unique_ptr<EventThread> sfEventThread,
+                        bool useContentDetectionV2 = false) {
+        std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{
+                HWC2::Display::Config::Builder(mDisplay, 0)
+                        .setVsyncPeriod(int32_t(16666667))
+                        .setConfigGroup(0)
+                        .build()};
+
+        mFlinger->mRefreshRateConfigs = std::make_unique<
+                scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
+        mFlinger->mRefreshRateStats = std::make_unique<
+                scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
+                                             /*currentConfig=*/HwcConfigIndexType(0),
+                                             /*powerMode=*/hal::PowerMode::OFF);
+        mFlinger->mPhaseConfiguration =
+                mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
+
+        mScheduler =
+                new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
+                                      *mFlinger->mRefreshRateConfigs, useContentDetectionV2);
+
+        mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
+        mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
+        resetScheduler(mScheduler);
+
+        mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
+                                          mFlinger->mSfConnectionHandle,
+                                          mFlinger->mPhaseConfiguration->getCurrentOffsets());
+    }
+
+    void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
+
     using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
     void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
         mFactory.mCreateBufferQueue = f;
@@ -203,26 +248,32 @@
         memcpy(&mFlinger->mInternalDisplayPrimaries, &primaries, sizeof(ui::DisplayPrimaries));
     }
 
-    using HotplugEvent = SurfaceFlinger::HotplugEvent;
+    static auto& mutableLayerCurrentState(const sp<Layer>& layer) { return layer->mCurrentState; }
+    static auto& mutableLayerDrawingState(const sp<Layer>& layer) { return layer->mDrawingState; }
 
-    auto& mutableLayerCurrentState(sp<Layer> layer) { return layer->mCurrentState; }
-    auto& mutableLayerDrawingState(sp<Layer> layer) { return layer->mDrawingState; }
+    auto& mutableStateLock() { return mFlinger->mStateLock; }
 
-    void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) {
-        layer->mDrawingState.sidebandStream = sidebandStream;
-        layer->mSidebandStream = sidebandStream;
-        layer->getCompositionLayer()->editState().frontEnd.sidebandStream = sidebandStream;
+    static auto findOutputLayerForDisplay(const sp<Layer>& layer,
+                                          const sp<const DisplayDevice>& display) {
+        return layer->findOutputLayerForDisplay(display.get());
     }
 
-    void setLayerCompositionType(sp<Layer> layer, HWC2::Composition type) {
-        auto outputLayer = layer->findOutputLayerForDisplay(mFlinger->getDefaultDisplayDevice());
+    static void setLayerSidebandStream(const sp<Layer>& layer,
+                                       const sp<NativeHandle>& sidebandStream) {
+        layer->mDrawingState.sidebandStream = sidebandStream;
+        layer->mSidebandStream = sidebandStream;
+        layer->editCompositionState()->sidebandStream = sidebandStream;
+    }
+
+    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 = static_cast<Hwc2::IComposerClient::Composition>(type);
-    };
+        (*state.hwc).hwcCompositionType = type;
+    }
 
-    void setLayerPotentialCursor(sp<Layer> layer, bool potentialCursor) {
+    static void setLayerPotentialCursor(const sp<Layer>& layer, bool potentialCursor) {
         layer->mPotentialCursor = potentialCursor;
     }
 
@@ -238,15 +289,16 @@
         return mFlinger->destroyDisplay(displayToken);
     }
 
-    auto resetDisplayState() { return mFlinger->resetDisplayState(); }
+    auto resetDisplayState() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->resetDisplayState(); }
 
-    auto setupNewDisplayDeviceInternal(const wp<IBinder>& displayToken,
-                                       const std::optional<DisplayId>& displayId,
-                                       const DisplayDeviceState& state,
-                                       const sp<compositionengine::DisplaySurface>& dispSurface,
-                                       const sp<IGraphicBufferProducer>& producer) {
-        return mFlinger->setupNewDisplayDeviceInternal(displayToken, displayId, state, dispSurface,
-                                                       producer);
+    auto setupNewDisplayDeviceInternal(
+            const wp<IBinder>& displayToken,
+            std::shared_ptr<compositionengine::Display> compositionDisplay,
+            const DisplayDeviceState& state,
+            const sp<compositionengine::DisplaySurface>& dispSurface,
+            const sp<IGraphicBufferProducer>& producer) NO_THREAD_SAFETY_ANALYSIS {
+        return mFlinger->setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+                                                       dispSurface, producer);
     }
 
     auto handleTransactionLocked(uint32_t transactionFlags) {
@@ -254,8 +306,8 @@
         return mFlinger->handleTransactionLocked(transactionFlags);
     }
 
-    auto onHotplugReceived(int32_t sequenceId, hwc2_display_t display,
-                           HWC2::Connection connection) {
+    auto onHotplugReceived(int32_t sequenceId, hal::HWDisplayId display,
+                           hal::Connection connection) {
         return mFlinger->onHotplugReceived(sequenceId, display, connection);
     }
 
@@ -271,19 +323,20 @@
 
     // Allow reading display state without locking, as if called on the SF main thread.
     auto setPowerModeInternal(const sp<DisplayDevice>& display,
-                              int mode) NO_THREAD_SAFETY_ANALYSIS {
+                              hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS {
         return mFlinger->setPowerModeInternal(display, mode);
     }
 
-    auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what); }
+    auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what, systemTime()); }
 
-    auto captureScreenImplLocked(
-            const RenderArea& renderArea, SurfaceFlinger::TraverseLayersFunction traverseLayers,
-            ANativeWindowBuffer* buffer, bool useIdentityTransform, bool forSystem, int* outSyncFd) {
+    auto captureScreenImplLocked(const RenderArea& renderArea,
+                                 SurfaceFlinger::TraverseLayersFunction traverseLayers,
+                                 ANativeWindowBuffer* buffer, bool useIdentityTransform,
+                                 bool forSystem, int* outSyncFd, bool regionSampling) {
         bool ignored;
         return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer,
                                                  useIdentityTransform, forSystem, outSyncFd,
-                                                 ignored);
+                                                 regionSampling, ignored);
     }
 
     auto traverseLayersInDisplay(const sp<const DisplayDevice>& display,
@@ -296,6 +349,22 @@
         return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
     }
 
+    auto& getTransactionQueue() { return mFlinger->mTransactionQueues; }
+
+    auto setTransactionState(const Vector<ComposerState>& states,
+                             const Vector<DisplayState>& displays, uint32_t flags,
+                             const sp<IBinder>& applyToken,
+                             const InputWindowCommands& inputWindowCommands,
+                             int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
+                             bool hasListenerCallbacks,
+                             std::vector<ListenerCallbacks>& listenerCallbacks) {
+        return mFlinger->setTransactionState(states, displays, flags, applyToken,
+                                             inputWindowCommands, desiredPresentTime, uncacheBuffer,
+                                             hasListenerCallbacks, listenerCallbacks);
+    }
+
+    auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); };
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
@@ -306,6 +375,7 @@
     auto& getHwComposer() const {
         return static_cast<impl::HWComposer&>(mFlinger->getHwComposer());
     }
+    auto& getCompositionEngine() const { return mFlinger->getCompositionEngine(); }
 
     const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; }
 
@@ -315,7 +385,6 @@
      */
 
     auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; }
-    auto& mutablePrimaryDisplayOrientation() { return SurfaceFlinger::primaryDisplayOrientation; }
     auto& mutableUseColorManagement() { return SurfaceFlinger::useColorManagement; }
 
     auto& mutableCurrentState() { return mFlinger->mCurrentState; }
@@ -332,18 +401,18 @@
     auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
     auto& mutableUseHwcVirtualDisplays() { return mFlinger->mUseHwcVirtualDisplays; }
     auto& mutablePowerAdvisor() { return mFlinger->mPowerAdvisor; }
+    auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; }
 
     auto& mutableComposerSequenceId() { return mFlinger->getBE().mComposerSequenceId; }
     auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
     auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
     auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
     auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
-    auto& mutableScheduler() { return mFlinger->mScheduler; }
-    auto& mutableAppConnectionHandle() { return mFlinger->mAppConnectionHandle; }
-    auto& mutableSfConnectionHandle() { return mFlinger->mSfConnectionHandle; }
-    auto& mutableRefreshRateConfigs() { return mFlinger->mRefreshRateConfigs; }
-    auto& mutableRefreshRateStats() { return mFlinger->mRefreshRateStats; }
-    auto& mutableTimeStats() { return mFlinger->mTimeStats; }
+    auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; }
+
+    auto fromHandle(const sp<IBinder>& handle) {
+        return mFlinger->fromHandle(handle);
+    }
 
     ~TestableSurfaceFlinger() {
         // All these pointer and container clears help ensure that GMock does
@@ -355,7 +424,7 @@
         mutableDrawingState().displays.clear();
         mutableEventQueue().reset();
         mutableInterceptor().reset();
-        mutableScheduler().reset();
+        mFlinger->mScheduler.reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
         mFlinger->mCompositionEngine->setRenderEngine(
                 std::unique_ptr<renderengine::RenderEngine>());
@@ -367,12 +436,12 @@
      */
     struct HWC2Display : public HWC2::impl::Display {
         HWC2Display(Hwc2::Composer& composer,
-                    const std::unordered_set<HWC2::Capability>& capabilities, hwc2_display_t id,
-                    HWC2::DisplayType type)
+                    const std::unordered_set<hal::Capability>& capabilities, hal::HWDisplayId id,
+                    hal::DisplayType type)
               : HWC2::impl::Display(composer, capabilities, id, type) {}
         ~HWC2Display() {
             // Prevents a call to disable vsyncs.
-            mType = HWC2::DisplayType::Invalid;
+            mType = hal::DisplayType::INVALID;
         }
 
         auto& mutableIsConnected() { return this->mIsConnected; }
@@ -382,19 +451,19 @@
 
     class FakeHwcDisplayInjector {
     public:
-        static constexpr hwc2_display_t DEFAULT_HWC_DISPLAY_ID = 1000;
+        static constexpr hal::HWDisplayId DEFAULT_HWC_DISPLAY_ID = 1000;
         static constexpr int32_t DEFAULT_WIDTH = 1920;
         static constexpr int32_t DEFAULT_HEIGHT = 1280;
         static constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'666;
+        static constexpr int32_t DEFAULT_CONFIG_GROUP = 7;
         static constexpr int32_t DEFAULT_DPI = 320;
-        static constexpr int32_t DEFAULT_ACTIVE_CONFIG = 0;
-        static constexpr int32_t DEFAULT_POWER_MODE = 2;
+        static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0;
+        static constexpr hal::PowerMode DEFAULT_POWER_MODE = hal::PowerMode::ON;
 
-        FakeHwcDisplayInjector(DisplayId displayId, HWC2::DisplayType hwcDisplayType,
-                               bool isPrimary)
+        FakeHwcDisplayInjector(DisplayId displayId, hal::DisplayType hwcDisplayType, bool isPrimary)
               : mDisplayId(displayId), mHwcDisplayType(hwcDisplayType), mIsPrimary(isPrimary) {}
 
-        auto& setHwcDisplayId(hwc2_display_t displayId) {
+        auto& setHwcDisplayId(hal::HWDisplayId displayId) {
             mHwcDisplayId = displayId;
             return *this;
         }
@@ -424,23 +493,23 @@
             return *this;
         }
 
-        auto& setActiveConfig(int32_t config) {
+        auto& setActiveConfig(hal::HWConfigId config) {
             mActiveConfig = config;
             return *this;
         }
 
-        auto& setCapabilities(const std::unordered_set<HWC2::Capability>* capabilities) {
+        auto& setCapabilities(const std::unordered_set<hal::Capability>* capabilities) {
             mCapabilities = capabilities;
             return *this;
         }
 
-        auto& setPowerMode(int mode) {
+        auto& setPowerMode(hal::PowerMode mode) {
             mPowerMode = mode;
             return *this;
         }
 
         void inject(TestableSurfaceFlinger* flinger, Hwc2::Composer* composer) {
-            static const std::unordered_set<HWC2::Capability> defaultCapabilities;
+            static const std::unordered_set<hal::Capability> defaultCapabilities;
             if (mCapabilities == nullptr) mCapabilities = &defaultCapabilities;
 
             // Caution - Make sure that any values passed by reference here do
@@ -456,44 +525,47 @@
             config.setVsyncPeriod(mRefreshRate);
             config.setDpiX(mDpiX);
             config.setDpiY(mDpiY);
-            display->mutableConfigs().emplace(mActiveConfig, config.build());
+            config.setConfigGroup(mConfigGroup);
+            display->mutableConfigs().emplace(static_cast<int32_t>(mActiveConfig), config.build());
             display->mutableIsConnected() = true;
-            display->setPowerMode(static_cast<HWC2::PowerMode>(mPowerMode));
+            display->setPowerMode(mPowerMode);
 
-            flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = display.get();
+            flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
 
-            if (mHwcDisplayType == HWC2::DisplayType::Physical) {
+            if (mHwcDisplayType == hal::DisplayType::PHYSICAL) {
                 flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, mDisplayId);
                 (mIsPrimary ? flinger->mutableInternalHwcDisplayId()
                             : flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId;
             }
-
-            flinger->mFakeHwcDisplays.push_back(std::move(display));
         }
 
     private:
         const DisplayId mDisplayId;
-        const HWC2::DisplayType mHwcDisplayType;
+        const hal::DisplayType mHwcDisplayType;
         const bool mIsPrimary;
 
-        hwc2_display_t mHwcDisplayId = DEFAULT_HWC_DISPLAY_ID;
+        hal::HWDisplayId mHwcDisplayId = DEFAULT_HWC_DISPLAY_ID;
         int32_t mWidth = DEFAULT_WIDTH;
         int32_t mHeight = DEFAULT_HEIGHT;
         int32_t mRefreshRate = DEFAULT_REFRESH_RATE;
         int32_t mDpiX = DEFAULT_DPI;
+        int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
         int32_t mDpiY = DEFAULT_DPI;
-        int32_t mActiveConfig = DEFAULT_ACTIVE_CONFIG;
-        int32_t mPowerMode = DEFAULT_POWER_MODE;
-        const std::unordered_set<HWC2::Capability>* mCapabilities = nullptr;
+        hal::HWConfigId mActiveConfig = DEFAULT_ACTIVE_CONFIG;
+        hal::PowerMode mPowerMode = DEFAULT_POWER_MODE;
+        const std::unordered_set<hal::Capability>* mCapabilities = nullptr;
     };
 
     class FakeDisplayDeviceInjector {
     public:
         FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger,
-                                  const std::optional<DisplayId>& displayId, bool isVirtual,
-                                  bool isPrimary)
-              : mFlinger(flinger), mCreationArgs(flinger.mFlinger.get(), mDisplayToken, displayId) {
-            mCreationArgs.isVirtual = isVirtual;
+                                  std::shared_ptr<compositionengine::Display> compositionDisplay,
+                                  std::optional<DisplayConnectionType> connectionType,
+                                  std::optional<hal::HWDisplayId> hwcDisplayId, bool isPrimary)
+              : mFlinger(flinger),
+                mCreationArgs(flinger.mFlinger.get(), mDisplayToken, compositionDisplay),
+                mHwcDisplayId(hwcDisplayId) {
+            mCreationArgs.connectionType = connectionType;
             mCreationArgs.isPrimary = isPrimary;
         }
 
@@ -532,7 +604,7 @@
             return *this;
         }
 
-        auto& setPowerMode(int mode) {
+        auto& setPowerMode(hal::PowerMode mode) {
             mCreationArgs.initialPowerMode = mode;
             return *this;
         }
@@ -549,19 +621,30 @@
             return *this;
         }
 
+        auto& setPhysicalOrientation(ui::Rotation orientation) {
+            mCreationArgs.physicalOrientation = orientation;
+            return *this;
+        }
+
         sp<DisplayDevice> inject() {
+            const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
+
             DisplayDeviceState state;
-            state.displayId = mCreationArgs.isVirtual ? std::nullopt : mCreationArgs.displayId;
+            if (const auto type = mCreationArgs.connectionType) {
+                LOG_ALWAYS_FATAL_IF(!displayId);
+                LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
+                state.physical = {*displayId, *type, *mHwcDisplayId};
+            }
+
             state.isSecure = mCreationArgs.isSecure;
 
-            sp<DisplayDevice> device = new DisplayDevice(std::move(mCreationArgs));
+            sp<DisplayDevice> device = new DisplayDevice(mCreationArgs);
             mFlinger.mutableDisplays().emplace(mDisplayToken, device);
             mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
             mFlinger.mutableDrawingState().displays.add(mDisplayToken, state);
 
-            if (!mCreationArgs.isVirtual) {
-                LOG_ALWAYS_FATAL_IF(!state.displayId);
-                mFlinger.mutablePhysicalDisplayTokens()[*state.displayId] = mDisplayToken;
+            if (const auto& physical = state.physical) {
+                mFlinger.mutablePhysicalDisplayTokens()[physical->id] = mDisplayToken;
             }
 
             return device;
@@ -571,13 +654,13 @@
         TestableSurfaceFlinger& mFlinger;
         sp<BBinder> mDisplayToken = new BBinder();
         DisplayDeviceCreationArgs mCreationArgs;
+        const std::optional<hal::HWDisplayId> mHwcDisplayId;
     };
 
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
-
-    // We need to keep a reference to these so they are properly destroyed.
-    std::vector<std::unique_ptr<HWC2Display>> mFakeHwcDisplays;
+    TestableScheduler* mScheduler = nullptr;
+    Hwc2::mock::Display mDisplay;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index f35758d..0a24650 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -14,33 +14,45 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
+#include <TimeStats/TimeStats.h>
+#include <android/util/ProtoOutputStream.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
 #include <log/log.h>
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
+#include <chrono>
 #include <random>
 #include <unordered_set>
 
-#include "TimeStats/TimeStats.h"
-
 #include "libsurfaceflinger_unittest_main.h"
 
 using namespace android::surfaceflinger;
 using namespace google::protobuf;
+using namespace std::chrono_literals;
 
 namespace android {
 namespace {
 
+using testing::_;
+using testing::AnyNumber;
 using testing::Contains;
+using testing::HasSubstr;
+using testing::InSequence;
 using testing::SizeIs;
+using testing::StrEq;
 using testing::UnorderedElementsAre;
 
+using PowerMode = hardware::graphics::composer::V2_4::IComposerClient::PowerMode;
+
 // clang-format off
 #define FMT_PROTO          true
 #define FMT_STRING         false
@@ -133,7 +145,44 @@
     }
 
     std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
-    std::unique_ptr<TimeStats> mTimeStats = std::make_unique<impl::TimeStats>();
+
+    class FakeStatsEventDelegate : public impl::TimeStats::StatsEventDelegate {
+    public:
+        FakeStatsEventDelegate() = default;
+        ~FakeStatsEventDelegate() override = default;
+
+        struct AStatsEvent* addStatsEventToPullData(AStatsEventList*) override {
+            return mEvent;
+        }
+        void setStatsPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata*,
+                                      AStatsManager_PullAtomCallback callback,
+                                      void* cookie) override {
+            mAtomTags.push_back(atom_tag);
+            mCallback = callback;
+            mCookie = cookie;
+        }
+
+        AStatsManager_PullAtomCallbackReturn makePullAtomCallback(int32_t atom_tag, void* cookie) {
+            return (*mCallback)(atom_tag, nullptr, cookie);
+        }
+
+        MOCK_METHOD1(clearStatsPullAtomCallback, void(int32_t));
+        MOCK_METHOD2(statsEventSetAtomId, void(AStatsEvent*, uint32_t));
+        MOCK_METHOD2(statsEventWriteInt32, void(AStatsEvent*, int32_t));
+        MOCK_METHOD2(statsEventWriteInt64, void(AStatsEvent*, int64_t));
+        MOCK_METHOD2(statsEventWriteString8, void(AStatsEvent*, const char*));
+        MOCK_METHOD3(statsEventWriteByteArray, void(AStatsEvent*, const uint8_t*, size_t));
+        MOCK_METHOD1(statsEventBuild, void(AStatsEvent*));
+
+        AStatsEvent* mEvent = AStatsEvent_obtain();
+        std::vector<int32_t> mAtomTags;
+        AStatsManager_PullAtomCallback mCallback = nullptr;
+        void* mCookie = nullptr;
+    };
+    FakeStatsEventDelegate* mDelegate = new FakeStatsEventDelegate;
+    std::unique_ptr<TimeStats> mTimeStats =
+            std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
+                                              std::nullopt, std::nullopt);
 };
 
 std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
@@ -171,8 +220,8 @@
     return result;
 }
 
-static std::string genLayerName(int32_t layerID) {
-    return (layerID < 0 ? "invalid.dummy" : "com.dummy#") + std::to_string(layerID);
+static std::string genLayerName(int32_t layerId) {
+    return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.example.fake#") + std::to_string(layerId);
 }
 
 void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
@@ -210,6 +259,25 @@
     return distr(mRandomEngine);
 }
 
+TEST_F(TimeStatsTest, disabledByDefault) {
+    ASSERT_FALSE(mTimeStats->isEnabled());
+}
+
+TEST_F(TimeStatsTest, setsCallbacksAfterBoot) {
+    mTimeStats->onBootFinished();
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+}
+
+TEST_F(TimeStatsTest, clearsCallbacksOnDestruction) {
+    EXPECT_CALL(*mDelegate,
+                clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+    EXPECT_CALL(*mDelegate,
+                clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    mTimeStats.reset();
+}
+
 TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
     ASSERT_TRUE(mTimeStats->isEnabled());
@@ -246,6 +314,125 @@
     EXPECT_EQ(CLIENT_COMPOSITION_FRAMES, globalProto.client_composition_frames());
 }
 
+TEST_F(TimeStatsTest, canIncreaseLateAcquireFrames) {
+    // this stat is not in the proto so verify by checking the string dump
+    constexpr size_t LATE_ACQUIRE_FRAMES = 2;
+
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
+        mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
+    }
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 2, 2000000);
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    const std::string expectedResult = "lateAcquireFrames = " + std::to_string(LATE_ACQUIRE_FRAMES);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canIncreaseBadDesiredPresent) {
+    // this stat is not in the proto so verify by checking the string dump
+    constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 2;
+
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
+        mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
+    }
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 2, 2000000);
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    const std::string expectedResult =
+            "badDesiredPresentFrames = " + std::to_string(BAD_DESIRED_PRESENT_FRAMES);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) {
+    // this stat is not in the proto so verify by checking the string dump
+    constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2;
+
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    for (size_t i = 0; i < CLIENT_COMPOSITION_REUSED_FRAMES; i++) {
+        ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionReusedFrames());
+    }
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    const std::string expectedResult =
+            "clientCompositionReusedFrames = " + std::to_string(CLIENT_COMPOSITION_REUSED_FRAMES);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canIncreaseRefreshRateSwitches) {
+    // this stat is not in the proto so verify by checking the string dump
+    constexpr size_t REFRESH_RATE_SWITCHES = 2;
+
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    for (size_t i = 0; i < REFRESH_RATE_SWITCHES; i++) {
+        ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementRefreshRateSwitches());
+    }
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    const std::string expectedResult =
+            "refreshRateSwitches = " + std::to_string(REFRESH_RATE_SWITCHES);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canIncreaseCompositionStrategyChanges) {
+    // this stat is not in the proto so verify by checking the string dump
+    constexpr size_t COMPOSITION_STRATEGY_CHANGES = 2;
+
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    for (size_t i = 0; i < COMPOSITION_STRATEGY_CHANGES; i++) {
+        ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementCompositionStrategyChanges());
+    }
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    const std::string expectedResult =
+            "compositionStrategyChanges = " + std::to_string(COMPOSITION_STRATEGY_CHANGES);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canAverageFrameDuration) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    mTimeStats->setPowerMode(PowerMode::ON);
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                          .count());
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(16ms)
+                                          .count());
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    EXPECT_THAT(result, HasSubstr("averageFrameDuration = 10.000 ms"));
+}
+
+TEST_F(TimeStatsTest, canAverageRenderEngineTimings) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms)
+                                                   .count(),
+                                           std::make_shared<FenceTime>(
+                                                   std::chrono::duration_cast<
+                                                           std::chrono::nanoseconds>(3ms)
+                                                           .count()));
+
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+                                                   .count(),
+                                           std::chrono::duration_cast<std::chrono::nanoseconds>(8ms)
+                                                   .count());
+
+    // Push a fake present fence to trigger flushing the RenderEngine timings.
+    mTimeStats->setPowerMode(PowerMode::ON);
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 3.000 ms"));
+}
+
 TEST_F(TimeStatsTest, canInsertGlobalPresentToPresent) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
@@ -254,13 +441,13 @@
     ASSERT_NO_FATAL_FAILURE(
             mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(2000000)));
 
-    ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(PowerMode::ON));
     ASSERT_NO_FATAL_FAILURE(
             mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000)));
     ASSERT_NO_FATAL_FAILURE(
             mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000)));
 
-    ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_OFF));
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(PowerMode::OFF));
     ASSERT_NO_FATAL_FAILURE(
             mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(6000000)));
     ASSERT_NO_FATAL_FAILURE(
@@ -275,6 +462,65 @@
     EXPECT_EQ(2, histogramProto.time_millis());
 }
 
+TEST_F(TimeStatsTest, canInsertGlobalFrameDuration) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->setPowerMode(PowerMode::OFF);
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(5ms)
+                                          .count());
+    mTimeStats->setPowerMode(PowerMode::ON);
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(3ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                          .count());
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.frame_duration_size());
+    const SFTimeStatsHistogramBucketProto& histogramProto = globalProto.frame_duration().Get(0);
+    EXPECT_EQ(1, histogramProto.frame_count());
+    EXPECT_EQ(3, histogramProto.time_millis());
+}
+
+TEST_F(TimeStatsTest, canInsertGlobalRenderEngineTiming) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms)
+                                                   .count(),
+                                           std::make_shared<FenceTime>(
+                                                   std::chrono::duration_cast<
+                                                           std::chrono::nanoseconds>(3ms)
+                                                           .count()));
+
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+                                                   .count(),
+                                           std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                                   .count());
+
+    // First verify that flushing RenderEngine durations did not occur yet.
+    SFTimeStatsGlobalProto preFlushProto;
+    ASSERT_TRUE(preFlushProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+    ASSERT_EQ(0, preFlushProto.render_engine_timing_size());
+
+    // Push a fake present fence to trigger flushing the RenderEngine timings.
+    mTimeStats->setPowerMode(PowerMode::ON);
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+
+    // Now we can verify that RenderEngine durations were flushed now.
+    SFTimeStatsGlobalProto postFlushProto;
+    ASSERT_TRUE(postFlushProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, postFlushProto.render_engine_timing_size());
+    const SFTimeStatsHistogramBucketProto& histogramProto =
+            postFlushProto.render_engine_timing().Get(0);
+    EXPECT_EQ(2, histogramProto.frame_count());
+    EXPECT_EQ(2, histogramProto.time_millis());
+}
+
 TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
@@ -495,7 +741,16 @@
     ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementTotalFrames());
     ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames());
     ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
-    ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(PowerMode::ON));
+
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(3ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                          .count());
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+                                                   .count(),
+                                           std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                                   .count());
     ASSERT_NO_FATAL_FAILURE(
             mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
     ASSERT_NO_FATAL_FAILURE(
@@ -512,9 +767,38 @@
     EXPECT_EQ(0, globalProto.missed_frames());
     EXPECT_EQ(0, globalProto.client_composition_frames());
     EXPECT_EQ(0, globalProto.present_to_present_size());
+    EXPECT_EQ(0, globalProto.frame_duration_size());
+    EXPECT_EQ(0, globalProto.render_engine_timing_size());
     EXPECT_EQ(0, globalProto.stats_size());
 }
 
+TEST_F(TimeStatsTest, canClearDumpOnlyTimeStats) {
+    // These stats are not in the proto so verify by checking the string dump.
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionReusedFrames());
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementRefreshRateSwitches());
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementCompositionStrategyChanges());
+    mTimeStats->setPowerMode(PowerMode::ON);
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(5ms)
+                                          .count());
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+                                                   .count(),
+                                           std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                                   .count());
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+    EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    EXPECT_THAT(result, HasSubstr("clientCompositionReusedFrames = 0"));
+    EXPECT_THAT(result, HasSubstr("refreshRateSwitches = 0"));
+    EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0"));
+    EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
+    EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
+}
+
 TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
@@ -549,6 +833,359 @@
     ASSERT_EQ(0, globalProto.stats_size());
 }
 
+TEST_F(TimeStatsTest, noInfInAverageFPS) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 1000000);
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    EXPECT_THAT(result, HasSubstr("averageFPS = 0.000"));
+}
+
+namespace {
+std::string buildExpectedHistogramBytestring(const std::vector<int32_t>& times,
+                                             const std::vector<int32_t>& frameCounts) {
+    util::ProtoOutputStream proto;
+    for (int i = 0; i < times.size(); i++) {
+        ALOGE("Writing time: %d", times[i]);
+        proto.write(util::FIELD_TYPE_INT32 | util::FIELD_COUNT_REPEATED | 1 /* field id */,
+                    (int32_t)times[i]);
+        ALOGE("Writing count: %d", frameCounts[i]);
+        proto.write(util::FIELD_TYPE_INT64 | util::FIELD_COUNT_REPEATED | 2 /* field id */,
+                    (int64_t)frameCounts[i]);
+    }
+    std::string byteString;
+    proto.serializeToString(&byteString);
+    return byteString;
+}
+
+std::string dumpByteStringHex(const std::string& str) {
+    std::stringstream ss;
+    ss << std::hex;
+    for (const char& c : str) {
+        ss << (int)c << " ";
+    }
+
+    return ss.str();
+}
+
+} // namespace
+
+MATCHER_P2(BytesEq, bytes, size, "") {
+    std::string expected;
+    expected.append((const char*)bytes, size);
+    std::string actual;
+    actual.append((const char*)arg, size);
+
+    *result_listener << "Bytes are not equal! \n";
+    *result_listener << "size: " << size << "\n";
+    *result_listener << "expected: " << dumpByteStringHex(expected).c_str() << "\n";
+    *result_listener << "actual: " << dumpByteStringHex(actual).c_str() << "\n";
+
+    return expected == actual;
+}
+
+TEST_F(TimeStatsTest, globalStatsCallback) {
+    constexpr size_t TOTAL_FRAMES = 5;
+    constexpr size_t MISSED_FRAMES = 4;
+    constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
+    constexpr size_t DISPLAY_EVENT_CONNECTIONS = 14;
+
+    mTimeStats->onBootFinished();
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    for (size_t i = 0; i < TOTAL_FRAMES; i++) {
+        mTimeStats->incrementTotalFrames();
+    }
+    for (size_t i = 0; i < MISSED_FRAMES; i++) {
+        mTimeStats->incrementMissedFrames();
+    }
+    for (size_t i = 0; i < CLIENT_COMPOSITION_FRAMES; i++) {
+        mTimeStats->incrementClientCompositionFrames();
+    }
+
+    mTimeStats->recordDisplayEventConnectionCount(DISPLAY_EVENT_CONNECTIONS);
+    mTimeStats->setPowerMode(PowerMode::ON);
+    mTimeStats->recordFrameDuration(1000000, 3000000);
+    mTimeStats->recordRenderEngineDuration(2000000, 4000000);
+    mTimeStats->recordRenderEngineDuration(2000000, std::make_shared<FenceTime>(3000000));
+
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    EXPECT_NE(nullptr, mDelegate->mCallback);
+    EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+    std::string expectedFrameDuration = buildExpectedHistogramBytestring({2}, {1});
+    std::string expectedRenderEngineTiming = buildExpectedHistogramBytestring({1, 2}, {1, 1});
+
+    {
+        InSequence seq;
+        EXPECT_CALL(*mDelegate,
+                    statsEventSetAtomId(mDelegate->mEvent,
+                                        android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, TOTAL_FRAMES));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, MISSED_FRAMES));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, CLIENT_COMPOSITION_FRAMES));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, _));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 2));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, DISPLAY_EVENT_CONNECTIONS));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)expectedFrameDuration.c_str(),
+                                                     expectedFrameDuration.size()),
+                                             expectedFrameDuration.size()));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)
+                                                             expectedRenderEngineTiming.c_str(),
+                                                     expectedRenderEngineTiming.size()),
+                                             expectedRenderEngineTiming.size()));
+        EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
+    }
+    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
+              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                              mDelegate->mCookie));
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    EXPECT_EQ(0, globalProto.total_frames());
+    EXPECT_EQ(0, globalProto.missed_frames());
+    EXPECT_EQ(0, globalProto.client_composition_frames());
+    EXPECT_EQ(0, globalProto.present_to_present_size());
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) {
+    constexpr size_t LATE_ACQUIRE_FRAMES = 2;
+    constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3;
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->onBootFinished();
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
+        mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
+    }
+    for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
+        mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
+    }
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    EXPECT_NE(nullptr, mDelegate->mCallback);
+    EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+    std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1}, {1});
+    std::string expectedPostToPresent = buildExpectedHistogramBytestring({4}, {1});
+    std::string expectedAcquireToPresent = buildExpectedHistogramBytestring({3}, {1});
+    std::string expectedLatchToPresent = buildExpectedHistogramBytestring({2}, {1});
+    std::string expectedDesiredToPresent = buildExpectedHistogramBytestring({1}, {1});
+    std::string expectedPostToAcquire = buildExpectedHistogramBytestring({1}, {1});
+    {
+        InSequence seq;
+        EXPECT_CALL(*mDelegate,
+                    statsEventSetAtomId(mDelegate->mEvent,
+                                        android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteString8(mDelegate->mEvent,
+                                           StrEq(genLayerName(LAYER_ID_0).c_str())));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 0));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)
+                                                             expectedPresentToPresent.c_str(),
+                                                     expectedPresentToPresent.size()),
+                                             expectedPresentToPresent.size()));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)expectedPostToPresent.c_str(),
+                                                     expectedPostToPresent.size()),
+                                             expectedPostToPresent.size()));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)
+                                                             expectedAcquireToPresent.c_str(),
+                                                     expectedAcquireToPresent.size()),
+                                             expectedAcquireToPresent.size()));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)expectedLatchToPresent.c_str(),
+                                                     expectedLatchToPresent.size()),
+                                             expectedLatchToPresent.size()));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)
+                                                             expectedDesiredToPresent.c_str(),
+                                                     expectedDesiredToPresent.size()),
+                                             expectedDesiredToPresent.size()));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)expectedPostToAcquire.c_str(),
+                                                     expectedPostToAcquire.size()),
+                                             expectedPostToAcquire.size()));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, LATE_ACQUIRE_FRAMES));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteInt64(mDelegate->mEvent, BAD_DESIRED_PRESENT_FRAMES));
+        EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
+    }
+    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
+              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                              mDelegate->mCookie));
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    EXPECT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->onBootFinished();
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    EXPECT_NE(nullptr, mDelegate->mCallback);
+    EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+    EXPECT_CALL(*mDelegate,
+                statsEventSetAtomId(mDelegate->mEvent,
+                                    android::util::SURFACEFLINGER_STATS_LAYER_INFO))
+            .Times(2);
+    EXPECT_CALL(*mDelegate,
+                statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_0).c_str())));
+    EXPECT_CALL(*mDelegate,
+                statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
+    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
+              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                              mDelegate->mCookie));
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleBuckets) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->onBootFinished();
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
+
+    // Now make sure that TimeStats flushes global stats to set the callback.
+    mTimeStats->setPowerMode(PowerMode::ON);
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    EXPECT_NE(nullptr, mDelegate->mCallback);
+    EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1, 2}, {2, 1});
+    {
+        InSequence seq;
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)
+                                                             expectedPresentToPresent.c_str(),
+                                                     expectedPresentToPresent.size()),
+                                             expectedPresentToPresent.size()));
+        EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
+                .Times(AnyNumber());
+    }
+    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
+              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                              mDelegate->mCookie));
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_limitsHistogramBuckets) {
+    mDelegate = new FakeStatsEventDelegate;
+    mTimeStats =
+            std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
+                                              std::nullopt, 1);
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->onBootFinished();
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    EXPECT_NE(nullptr, mDelegate->mCallback);
+    EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1}, {2});
+    {
+        InSequence seq;
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)
+                                                             expectedPresentToPresent.c_str(),
+                                                     expectedPresentToPresent.size()),
+                                             expectedPresentToPresent.size()));
+        EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
+                .Times(AnyNumber());
+    }
+    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
+              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                              mDelegate->mCookie));
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_limitsLayers) {
+    mDelegate = new FakeStatsEventDelegate;
+    mTimeStats =
+            std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate), 1,
+                                              std::nullopt);
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->onBootFinished();
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 4, 5000000);
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    EXPECT_NE(nullptr, mDelegate->mCallback);
+    EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+    EXPECT_CALL(*mDelegate,
+                statsEventSetAtomId(mDelegate->mEvent,
+                                    android::util::SURFACEFLINGER_STATS_LAYER_INFO))
+            .Times(1);
+    EXPECT_CALL(*mDelegate,
+                statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
+    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
+              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                              mDelegate->mCookie));
+}
+
 TEST_F(TimeStatsTest, canSurviveMonkey) {
     if (g_noSlowTests) {
         GTEST_SKIP();
@@ -557,24 +1194,27 @@
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
     for (size_t i = 0; i < 10000000; ++i) {
-        const int32_t layerID = genRandomInt32(-1, 10);
+        const int32_t layerId = genRandomInt32(-1, 10);
         const int32_t frameNumber = genRandomInt32(1, 10);
         switch (genRandomInt32(0, 100)) {
             case 0:
                 ALOGV("removeTimeRecord");
-                ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(layerID, frameNumber));
+                ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(layerId, frameNumber));
                 continue;
             case 1:
                 ALOGV("onDestroy");
-                ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(layerID));
+                ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(layerId));
                 continue;
         }
         TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
         const int32_t ts = genRandomInt32(1, 1000000000);
-        ALOGV("type[%d], layerID[%d], frameNumber[%d], ts[%d]", type, layerID, frameNumber, ts);
-        setTimeStamp(type, layerID, frameNumber, ts);
+        ALOGV("type[%d], layerId[%d], frameNumber[%d], ts[%d]", type, layerId, frameNumber, ts);
+        setTimeStamp(type, layerId, frameNumber, ts);
     }
 }
 
 } // namespace
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
new file mode 100644
index 0000000..2a48a22
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -0,0 +1,330 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "CompositionTest"
+
+#include <compositionengine/Display.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+#include <utils/String8.h>
+
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/MockDispSync.h"
+#include "mock/MockEventControlThread.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
+
+namespace android {
+
+using testing::_;
+using testing::Return;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+class TransactionApplicationTest : public testing::Test {
+public:
+    TransactionApplicationTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+        mFlinger.mutableEventQueue().reset(mMessageQueue);
+        setupScheduler();
+    }
+
+    ~TransactionApplicationTest() {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    }
+
+    void setupScheduler() {
+        auto eventThread = std::make_unique<mock::EventThread>();
+        auto sfEventThread = std::make_unique<mock::EventThread>();
+
+        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*eventThread, createEventConnection(_, _))
+                .WillOnce(Return(
+                        new EventThreadConnection(eventThread.get(), ResyncCallback(),
+                                                  ISurfaceComposer::eConfigChangedSuppress)));
+
+        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+                .WillOnce(Return(
+                        new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+                                                  ISurfaceComposer::eConfigChangedSuppress)));
+
+        EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*mPrimaryDispSync, getPeriod())
+                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+
+        mFlinger.setupScheduler(std::unique_ptr<mock::DispSync>(mPrimaryDispSync),
+                                std::make_unique<mock::EventControlThread>(),
+                                std::move(eventThread), std::move(sfEventThread));
+    }
+
+    TestableScheduler* mScheduler;
+    TestableSurfaceFlinger mFlinger;
+
+    std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
+    mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
+
+    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+    mock::DispSync* mPrimaryDispSync = new mock::DispSync();
+
+    struct TransactionInfo {
+        Vector<ComposerState> states;
+        Vector<DisplayState> displays;
+        uint32_t flags = 0;
+        sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+        InputWindowCommands inputWindowCommands;
+        int64_t desiredPresentTime = -1;
+        client_cache_t uncacheBuffer;
+    };
+
+    void checkEqual(TransactionInfo info, SurfaceFlinger::TransactionState state) {
+        EXPECT_EQ(0, info.states.size());
+        EXPECT_EQ(0, state.states.size());
+
+        EXPECT_EQ(0, info.displays.size());
+        EXPECT_EQ(0, state.displays.size());
+        EXPECT_EQ(info.flags, state.flags);
+        EXPECT_EQ(info.desiredPresentTime, state.desiredPresentTime);
+    }
+
+    void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows,
+                     int64_t desiredPresentTime) {
+        mTransactionNumber++;
+        transaction.flags |= flags; // ISurfaceComposer::eSynchronous;
+        transaction.inputWindowCommands.syncInputWindows = syncInputWindows;
+        transaction.desiredPresentTime = desiredPresentTime;
+    }
+
+    void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
+        ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+        // called in SurfaceFlinger::signalTransaction
+        EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillOnce(Return(systemTime()));
+        TransactionInfo transaction;
+        setupSingle(transaction, flags, syncInputWindows,
+                    /*desiredPresentTime*/ -1);
+        nsecs_t applicationTime = systemTime();
+        mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags,
+                                     transaction.applyToken, transaction.inputWindowCommands,
+                                     transaction.desiredPresentTime, transaction.uncacheBuffer,
+                                     mHasListenerCallbacks, mCallbacks);
+
+        // This transaction should not have been placed on the transaction queue.
+        // If transaction is synchronous or syncs input windows, SF
+        // applyTransactionState should time out (5s) wating for SF to commit
+        // the transaction or to receive a signal that syncInputWindows has
+        // completed.  If this is animation, it should not time out waiting.
+        nsecs_t returnedTime = systemTime();
+        if (flags & ISurfaceComposer::eSynchronous || syncInputWindows) {
+            EXPECT_GE(returnedTime, applicationTime + s2ns(5));
+        } else {
+            EXPECT_LE(returnedTime, applicationTime + s2ns(5));
+        }
+        auto transactionQueue = mFlinger.getTransactionQueue();
+        EXPECT_EQ(0, transactionQueue.size());
+    }
+
+    void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
+        ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+        // called in SurfaceFlinger::signalTransaction
+        EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+        // first check will see desired present time has not passed,
+        // but afterwards it will look like the desired present time has passed
+        nsecs_t time = systemTime();
+        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
+                .WillOnce(Return(time + nsecs_t(5 * 1e8)));
+        TransactionInfo transaction;
+        setupSingle(transaction, flags, syncInputWindows,
+                    /*desiredPresentTime*/ time + s2ns(1));
+        nsecs_t applicationSentTime = systemTime();
+        mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags,
+                                     transaction.applyToken, transaction.inputWindowCommands,
+                                     transaction.desiredPresentTime, transaction.uncacheBuffer,
+                                     mHasListenerCallbacks, mCallbacks);
+
+        nsecs_t returnedTime = systemTime();
+        EXPECT_LE(returnedTime, applicationSentTime + s2ns(5));
+        // This transaction should have been placed on the transaction queue
+        auto transactionQueue = mFlinger.getTransactionQueue();
+        EXPECT_EQ(1, transactionQueue.size());
+    }
+
+    void BlockedByPriorTransaction(uint32_t flags, bool syncInputWindows) {
+        ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+        // called in SurfaceFlinger::signalTransaction
+        nsecs_t time = systemTime();
+        EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
+                .WillOnce(Return(time + nsecs_t(5 * 1e8)));
+        // transaction that should go on the pending thread
+        TransactionInfo transactionA;
+        setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
+                    /*desiredPresentTime*/ time + s2ns(1));
+
+        // transaction that would not have gone on the pending thread if not
+        // blocked
+        TransactionInfo transactionB;
+        setupSingle(transactionB, flags, syncInputWindows,
+                    /*desiredPresentTime*/ -1);
+
+        nsecs_t applicationSentTime = systemTime();
+        mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags,
+                                     transactionA.applyToken, transactionA.inputWindowCommands,
+                                     transactionA.desiredPresentTime, transactionA.uncacheBuffer,
+                                     mHasListenerCallbacks, mCallbacks);
+
+        // This thread should not have been blocked by the above transaction
+        // (5s is the timeout period that applyTransactionState waits for SF to
+        // commit the transaction)
+        EXPECT_LE(systemTime(), applicationSentTime + s2ns(5));
+
+        applicationSentTime = systemTime();
+        mFlinger.setTransactionState(transactionB.states, transactionB.displays, transactionB.flags,
+                                     transactionB.applyToken, transactionB.inputWindowCommands,
+                                     transactionB.desiredPresentTime, transactionB.uncacheBuffer,
+                                     mHasListenerCallbacks, mCallbacks);
+
+        // this thread should have been blocked by the above transaction
+        // if this is an animation, this thread should be blocked for 5s
+        // in setTransactionState waiting for transactionA to flush.  Otherwise,
+        // the transaction should be placed on the pending queue
+        if (flags & ISurfaceComposer::eAnimation) {
+            EXPECT_GE(systemTime(), applicationSentTime + s2ns(5));
+        } else {
+            EXPECT_LE(systemTime(), applicationSentTime + s2ns(5));
+        }
+
+        // check that there is one binder on the pending queue.
+        auto transactionQueue = mFlinger.getTransactionQueue();
+        EXPECT_EQ(1, transactionQueue.size());
+
+        auto& [applyToken, transactionStates] = *(transactionQueue.begin());
+        EXPECT_EQ(2, transactionStates.size());
+
+        auto& transactionStateA = transactionStates.front();
+        transactionStates.pop();
+        checkEqual(transactionA, transactionStateA);
+        auto& transactionStateB = transactionStates.front();
+        checkEqual(transactionB, transactionStateB);
+    }
+
+    bool mHasListenerCallbacks = false;
+    std::vector<ListenerCallbacks> mCallbacks;
+    int mTransactionNumber = 0;
+};
+
+TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
+    ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+    // called in SurfaceFlinger::signalTransaction
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    // nsecs_t time = systemTime();
+    EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
+            .WillOnce(Return(nsecs_t(5 * 1e8)))
+            .WillOnce(Return(s2ns(2)));
+    TransactionInfo transactionA; // transaction to go on pending queue
+    setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
+                /*desiredPresentTime*/ s2ns(1));
+    mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags,
+                                 transactionA.applyToken, transactionA.inputWindowCommands,
+                                 transactionA.desiredPresentTime, transactionA.uncacheBuffer,
+                                 mHasListenerCallbacks, mCallbacks);
+
+    auto& transactionQueue = mFlinger.getTransactionQueue();
+    ASSERT_EQ(1, transactionQueue.size());
+
+    auto& [applyToken, transactionStates] = *(transactionQueue.begin());
+    ASSERT_EQ(1, transactionStates.size());
+
+    auto& transactionState = transactionStates.front();
+    checkEqual(transactionA, transactionState);
+
+    // because flushing uses the cached expected present time, we send an empty
+    // transaction here (sending a null applyToken to fake it as from a
+    // different process) to re-query and reset the cached expected present time
+    TransactionInfo empty;
+    empty.applyToken = sp<IBinder>();
+    mFlinger.setTransactionState(empty.states, empty.displays, empty.flags, empty.applyToken,
+                                 empty.inputWindowCommands, empty.desiredPresentTime,
+                                 empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks);
+
+    // flush transaction queue should flush as desiredPresentTime has
+    // passed
+    mFlinger.flushTransactionQueues();
+
+    EXPECT_EQ(0, transactionQueue.size());
+}
+
+TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Synchronous) {
+    NotPlacedOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
+}
+
+TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Animation) {
+    NotPlacedOnTransactionQueue(ISurfaceComposer::eAnimation, /*syncInputWindows*/ false);
+}
+
+TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_SyncInputWindows) {
+    NotPlacedOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true);
+}
+
+TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Synchronous) {
+    PlaceOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
+}
+
+TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Animation) {
+    PlaceOnTransactionQueue(ISurfaceComposer::eAnimation, /*syncInputWindows*/ false);
+}
+
+TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_SyncInputWindows) {
+    PlaceOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true);
+}
+
+TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Synchronous) {
+    BlockedByPriorTransaction(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
+}
+
+TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Animation) {
+    BlockedByPriorTransaction(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
+}
+
+TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_SyncInputWindows) {
+    BlockedByPriorTransaction(/*flags*/ 0, /*syncInputWindows*/ true);
+}
+
+TEST_F(TransactionApplicationTest, FromHandle) {
+    sp<IBinder> badHandle;
+    auto ret = mFlinger.fromHandle(badHandle);
+    EXPECT_EQ(nullptr, ret.promote().get());
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
new file mode 100644
index 0000000..be49ef3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+#include "Scheduler/TimeKeeper.h"
+#include "Scheduler/Timer.h"
+#include "Scheduler/VSyncDispatchTimerQueue.h"
+#include "Scheduler/VSyncTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+using namespace testing;
+using namespace std::literals;
+
+namespace android::scheduler {
+
+template <typename Rep, typename Per>
+constexpr nsecs_t toNs(std::chrono::duration<Rep, Per> const& tp) {
+    return std::chrono::duration_cast<std::chrono::nanoseconds>(tp).count();
+}
+
+class FixedRateIdealStubTracker : public VSyncTracker {
+public:
+    FixedRateIdealStubTracker() : mPeriod{toNs(3ms)} {}
+
+    bool addVsyncTimestamp(nsecs_t) final { return true; }
+
+    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
+        auto const floor = timePoint % mPeriod;
+        if (floor == 0) {
+            return timePoint;
+        }
+        return timePoint - floor + mPeriod;
+    }
+
+    nsecs_t currentPeriod() const final { return mPeriod; }
+
+    void setPeriod(nsecs_t) final {}
+    void resetModel() final {}
+    void dump(std::string&) const final {}
+
+private:
+    nsecs_t const mPeriod;
+};
+
+class VRRStubTracker : public VSyncTracker {
+public:
+    VRRStubTracker(nsecs_t period) : mPeriod{period} {}
+
+    bool addVsyncTimestamp(nsecs_t) final { return true; }
+
+    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        auto const normalized_to_base = time_point - mBase;
+        auto const floor = (normalized_to_base) % mPeriod;
+        if (floor == 0) {
+            return time_point;
+        }
+        return normalized_to_base - floor + mPeriod + mBase;
+    }
+
+    void set_interval(nsecs_t interval, nsecs_t last_known) {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        mPeriod = interval;
+        mBase = last_known;
+    }
+
+    nsecs_t currentPeriod() const final {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        return mPeriod;
+    }
+
+    void setPeriod(nsecs_t) final {}
+    void resetModel() final {}
+    void dump(std::string&) const final {}
+
+private:
+    std::mutex mutable mMutex;
+    nsecs_t mPeriod;
+    nsecs_t mBase = 0;
+};
+
+struct VSyncDispatchRealtimeTest : testing::Test {
+    static nsecs_t constexpr mDispatchGroupThreshold = toNs(100us);
+    static nsecs_t constexpr mVsyncMoveThreshold = toNs(500us);
+    static size_t constexpr mIterations = 20;
+};
+
+class RepeatingCallbackReceiver {
+public:
+    RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t wl)
+          : mWorkload(wl),
+            mCallback(
+                    dispatch, [&](auto time, auto) { callback_called(time); }, "repeat0") {}
+
+    void repeatedly_schedule(size_t iterations, std::function<void(nsecs_t)> const& onEachFrame) {
+        mCallbackTimes.reserve(iterations);
+        mCallback.schedule(mWorkload, systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload);
+
+        for (auto i = 0u; i < iterations - 1; i++) {
+            std::unique_lock<decltype(mMutex)> lk(mMutex);
+            mCv.wait(lk, [&] { return mCalled; });
+            mCalled = false;
+            auto last = mLastTarget;
+            lk.unlock();
+
+            onEachFrame(last);
+
+            mCallback.schedule(mWorkload, last + mWorkload);
+        }
+
+        // wait for the last callback.
+        std::unique_lock<decltype(mMutex)> lk(mMutex);
+        mCv.wait(lk, [&] { return mCalled; });
+    }
+
+    void with_callback_times(std::function<void(std::vector<nsecs_t> const&)> const& fn) const {
+        fn(mCallbackTimes);
+    }
+
+private:
+    void callback_called(nsecs_t time) {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        mCallbackTimes.push_back(time);
+        mCalled = true;
+        mLastTarget = time;
+        mCv.notify_all();
+    }
+
+    nsecs_t const mWorkload;
+    VSyncCallbackRegistration mCallback;
+
+    std::mutex mMutex;
+    std::condition_variable mCv;
+    bool mCalled = false;
+    nsecs_t mLastTarget = 0;
+    std::vector<nsecs_t> mCallbackTimes;
+};
+
+TEST_F(VSyncDispatchRealtimeTest, triple_alarm) {
+    FixedRateIdealStubTracker tracker;
+    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+                                     mVsyncMoveThreshold);
+
+    static size_t constexpr num_clients = 3;
+    std::array<RepeatingCallbackReceiver, num_clients>
+            cb_receiver{RepeatingCallbackReceiver(dispatch, toNs(1500us)),
+                        RepeatingCallbackReceiver(dispatch, toNs(0h)),
+                        RepeatingCallbackReceiver(dispatch, toNs(1ms))};
+
+    auto const on_each_frame = [](nsecs_t) {};
+    std::array<std::thread, num_clients> threads{
+            std::thread([&] { cb_receiver[0].repeatedly_schedule(mIterations, on_each_frame); }),
+            std::thread([&] { cb_receiver[1].repeatedly_schedule(mIterations, on_each_frame); }),
+            std::thread([&] { cb_receiver[2].repeatedly_schedule(mIterations, on_each_frame); }),
+    };
+
+    for (auto it = threads.rbegin(); it != threads.rend(); it++) {
+        it->join();
+    }
+
+    for (auto const& cbs : cb_receiver) {
+        cbs.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
+    }
+}
+
+// starts at 333hz, slides down to 43hz
+TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) {
+    auto next_vsync_interval = toNs(3ms);
+    VRRStubTracker tracker(next_vsync_interval);
+    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+                                     mVsyncMoveThreshold);
+
+    RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
+
+    auto const on_each_frame = [&](nsecs_t last_known) {
+        tracker.set_interval(next_vsync_interval += toNs(1ms), last_known);
+    };
+
+    std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
+    eventThread.join();
+
+    cb_receiver.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
+}
+
+// starts at 333hz, jumps to 200hz at frame 10
+TEST_F(VSyncDispatchRealtimeTest, fixed_jump) {
+    VRRStubTracker tracker(toNs(3ms));
+    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+                                     mVsyncMoveThreshold);
+
+    RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
+
+    auto jump_frame_counter = 0u;
+    auto constexpr jump_frame_at = 10u;
+    auto const on_each_frame = [&](nsecs_t last_known) {
+        if (jump_frame_counter++ == jump_frame_at) {
+            tracker.set_interval(toNs(5ms), last_known);
+        }
+    };
+    std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
+    eventThread.join();
+
+    cb_receiver.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
+}
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
new file mode 100644
index 0000000..d940dc5
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -0,0 +1,925 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/TimeKeeper.h"
+#include "Scheduler/VSyncDispatchTimerQueue.h"
+#include "Scheduler/VSyncTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+using namespace testing;
+using namespace std::literals;
+namespace android::scheduler {
+
+class MockVSyncTracker : public VSyncTracker {
+public:
+    MockVSyncTracker(nsecs_t period) : mPeriod{period} {
+        ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_))
+                .WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime));
+        ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true));
+    }
+
+    MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
+    MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+    MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+    MOCK_METHOD1(setPeriod, void(nsecs_t));
+    MOCK_METHOD0(resetModel, void());
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+
+    nsecs_t nextVSyncTime(nsecs_t timePoint) const {
+        if (timePoint % mPeriod == 0) {
+            return timePoint;
+        }
+        return (timePoint - (timePoint % mPeriod) + mPeriod);
+    }
+
+protected:
+    nsecs_t const mPeriod;
+};
+
+class ControllableClock : public TimeKeeper {
+public:
+    ControllableClock() {
+        ON_CALL(*this, alarmIn(_, _))
+                .WillByDefault(Invoke(this, &ControllableClock::alarmInDefaultBehavior));
+        ON_CALL(*this, now()).WillByDefault(Invoke(this, &ControllableClock::fakeTime));
+    }
+
+    MOCK_CONST_METHOD0(now, nsecs_t());
+    MOCK_METHOD2(alarmIn, void(std::function<void()> const&, nsecs_t time));
+    MOCK_METHOD0(alarmCancel, void());
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+
+    void alarmInDefaultBehavior(std::function<void()> const& callback, nsecs_t time) {
+        mCallback = callback;
+        mNextCallbackTime = time + mCurrentTime;
+    }
+
+    nsecs_t fakeTime() const { return mCurrentTime; }
+
+    void advanceToNextCallback() {
+        mCurrentTime = mNextCallbackTime;
+        if (mCallback) {
+            mCallback();
+        }
+    }
+
+    void advanceBy(nsecs_t advancement) {
+        mCurrentTime += advancement;
+        if (mCurrentTime >= (mNextCallbackTime + mLag) && mCallback) {
+            mCallback();
+        }
+    };
+
+    void setLag(nsecs_t lag) { mLag = lag; }
+
+private:
+    std::function<void()> mCallback;
+    nsecs_t mNextCallbackTime = 0;
+    nsecs_t mCurrentTime = 0;
+    nsecs_t mLag = 0;
+};
+
+class CountingCallback {
+public:
+    CountingCallback(VSyncDispatch& dispatch)
+          : mDispatch(dispatch),
+            mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this,
+                                                       std::placeholders::_1,
+                                                       std::placeholders::_2),
+                                             "test")) {}
+    ~CountingCallback() { mDispatch.unregisterCallback(mToken); }
+
+    operator VSyncDispatch::CallbackToken() const { return mToken; }
+
+    void counter(nsecs_t time, nsecs_t) { mCalls.push_back(time); }
+
+    VSyncDispatch& mDispatch;
+    VSyncDispatch::CallbackToken mToken;
+    std::vector<nsecs_t> mCalls;
+};
+
+class PausingCallback {
+public:
+    PausingCallback(VSyncDispatch& dispatch, std::chrono::milliseconds pauseAmount)
+          : mDispatch(dispatch),
+            mToken(dispatch.registerCallback(std::bind(&PausingCallback::pause, this,
+                                                       std::placeholders::_1,
+                                                       std::placeholders::_2),
+                                             "test")),
+            mRegistered(true),
+            mPauseAmount(pauseAmount) {}
+    ~PausingCallback() { unregister(); }
+
+    operator VSyncDispatch::CallbackToken() const { return mToken; }
+
+    void pause(nsecs_t, nsecs_t) {
+        std::unique_lock<std::mutex> lk(mMutex);
+        mPause = true;
+        mCv.notify_all();
+
+        mCv.wait_for(lk, mPauseAmount, [this] { return !mPause; });
+
+        mResourcePresent = (mResource.lock() != nullptr);
+    }
+
+    bool waitForPause() {
+        std::unique_lock<std::mutex> lk(mMutex);
+        auto waiting = mCv.wait_for(lk, 10s, [this] { return mPause; });
+        return waiting;
+    }
+
+    void stashResource(std::weak_ptr<void> const& resource) { mResource = resource; }
+
+    bool resourcePresent() { return mResourcePresent; }
+
+    void unpause() {
+        std::unique_lock<std::mutex> lk(mMutex);
+        mPause = false;
+        mCv.notify_all();
+    }
+
+    void unregister() {
+        if (mRegistered) {
+            mDispatch.unregisterCallback(mToken);
+            mRegistered = false;
+        }
+    }
+
+    VSyncDispatch& mDispatch;
+    VSyncDispatch::CallbackToken mToken;
+    bool mRegistered = true;
+
+    std::mutex mMutex;
+    std::condition_variable mCv;
+    bool mPause = false;
+    std::weak_ptr<void> mResource;
+    bool mResourcePresent = false;
+    std::chrono::milliseconds const mPauseAmount;
+};
+
+class VSyncDispatchTimerQueueTest : public testing::Test {
+protected:
+    std::unique_ptr<TimeKeeper> createTimeKeeper() {
+        class TimeKeeperWrapper : public TimeKeeper {
+        public:
+            TimeKeeperWrapper(TimeKeeper& control) : mControllableClock(control) {}
+            void alarmIn(std::function<void()> const& callback, nsecs_t time) final {
+                mControllableClock.alarmIn(callback, time);
+            }
+            void alarmCancel() final { mControllableClock.alarmCancel(); }
+            nsecs_t now() const final { return mControllableClock.now(); }
+            void dump(std::string&) const final {}
+
+        private:
+            TimeKeeper& mControllableClock;
+        };
+        return std::make_unique<TimeKeeperWrapper>(mMockClock);
+    }
+
+    ~VSyncDispatchTimerQueueTest() {
+        // destructor of  dispatch will cancelAlarm(). Ignore final cancel in common test.
+        Mock::VerifyAndClearExpectations(&mMockClock);
+    }
+
+    void advanceToNextCallback() { mMockClock.advanceToNextCallback(); }
+
+    NiceMock<ControllableClock> mMockClock;
+    static nsecs_t constexpr mDispatchGroupThreshold = 5;
+    nsecs_t const mPeriod = 1000;
+    nsecs_t const mVsyncMoveThreshold = 300;
+    NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
+    VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
+                                      mVsyncMoveThreshold};
+};
+
+TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+    {
+        VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
+                                          mVsyncMoveThreshold};
+        CountingCallback cb(mDispatch);
+        EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled);
+    }
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) {
+    auto intended = mPeriod - 230;
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 100, intended), ScheduleResult::Scheduled);
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
+    EXPECT_CALL(mMockClock, alarmIn(_, 1050));
+
+    CountingCallback cb(mDispatch);
+    mDispatch.schedule(cb, 100, mPeriod);
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb.mCalls[0], Eq(1150));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingAdjustmentPast) {
+    auto const now = 234;
+    mMockClock.advanceBy(234);
+    auto const workDuration = 10 * mPeriod;
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + workDuration))
+            .WillOnce(Return(mPeriod * 11));
+    EXPECT_CALL(mMockClock, alarmIn(_, mPeriod - now));
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, workDuration, mPeriod), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+    mMockClock.advanceBy(950);
+    EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
+    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+
+    std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
+    EXPECT_TRUE(cb.waitForPause());
+    EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
+    cb.unpause();
+    pausingThread.join();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    auto resource = std::make_shared<int>(110);
+
+    PausingCallback cb(mDispatch, 50ms);
+    cb.stashResource(resource);
+    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+
+    std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
+    EXPECT_TRUE(cb.waitForPause());
+
+    cb.unregister();
+    resource.reset();
+
+    cb.unpause();
+    pausingThread.join();
+
+    EXPECT_TRUE(cb.resourcePresent());
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+            .Times(4)
+            .WillOnce(Return(1055))
+            .WillOnce(Return(1063))
+            .WillOnce(Return(1063))
+            .WillOnce(Return(1075));
+
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 955)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 813)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 162)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 100, mPeriod);
+    mDispatch.schedule(cb1, 250, mPeriod);
+
+    advanceToNextCallback();
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb0.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb0.mCalls[0], Eq(1075));
+    ASSERT_THAT(cb1.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb1.mCalls[0], Eq(1063));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(4)
+            .WillOnce(Return(10000))
+            .WillOnce(Return(1000))
+            .WillOnce(Return(10000))
+            .WillOnce(Return(10000));
+
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 750)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 100, mPeriod * 10);
+    mDispatch.schedule(cb1, 250, mPeriod);
+    mDispatch.cancel(cb1);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 400, 1000);
+    mDispatch.schedule(cb1, 200, 1000);
+    mDispatch.schedule(cb1, 300, 1000);
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, necessaryRearmsWhenModifying) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 400, 1000);
+    mDispatch.schedule(cb1, 200, 1000);
+    mDispatch.schedule(cb1, 500, 1000);
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 990)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 10)).InSequence(seq);
+
+    auto offset = 400;
+    auto closeOffset = offset + mDispatchGroupThreshold - 1;
+    auto notCloseOffset = offset + 2 * mDispatchGroupThreshold;
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 400, 1000);
+    mDispatch.schedule(cb1, 200, 1000);
+    mDispatch.schedule(cb1, closeOffset, 1000);
+
+    advanceToNextCallback();
+    ASSERT_THAT(cb0.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb0.mCalls[0], Eq(mPeriod));
+    ASSERT_THAT(cb1.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod));
+
+    mDispatch.schedule(cb0, 400, 2000);
+    mDispatch.schedule(cb1, notCloseOffset, 2000);
+    advanceToNextCallback();
+    ASSERT_THAT(cb1.mCalls.size(), Eq(2));
+    EXPECT_THAT(cb1.mCalls[1], Eq(2000));
+
+    advanceToNextCallback();
+    ASSERT_THAT(cb0.mCalls.size(), Eq(2));
+    EXPECT_THAT(cb0.mCalls[1], Eq(2000));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenEndingAndDoesntCancel) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmIn(_, 800));
+    EXPECT_CALL(mMockClock, alarmIn(_, 100));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 100, 1000);
+    mDispatch.schedule(cb1, 200, 1000);
+    advanceToNextCallback();
+    EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(3)
+            .WillOnce(Return(950))
+            .WillOnce(Return(1975))
+            .WillOnce(Return(2950));
+
+    CountingCallback cb(mDispatch);
+    mDispatch.schedule(cb, 100, 920);
+
+    mMockClock.advanceBy(850);
+    EXPECT_THAT(cb.mCalls.size(), Eq(1));
+
+    mDispatch.schedule(cb, 100, 1900);
+    mMockClock.advanceBy(900);
+    EXPECT_THAT(cb.mCalls.size(), Eq(1));
+    mMockClock.advanceBy(125);
+    EXPECT_THAT(cb.mCalls.size(), Eq(2));
+
+    mDispatch.schedule(cb, 100, 2900);
+    mMockClock.advanceBy(975);
+    EXPECT_THAT(cb.mCalls.size(), Eq(3));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+
+    VSyncDispatch::CallbackToken tmp;
+    tmp = mDispatch.registerCallback([&](auto, auto) { mDispatch.schedule(tmp, 100, 2000); },
+                                     "o.o");
+
+    mDispatch.schedule(tmp, 100, 1000);
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) {
+    VSyncDispatch::CallbackToken tmp;
+    std::optional<nsecs_t> lastTarget;
+    tmp = mDispatch.registerCallback(
+            [&](auto timestamp, auto) {
+                EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp - mVsyncMoveThreshold),
+                          ScheduleResult::Scheduled);
+                EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp), ScheduleResult::Scheduled);
+                EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp + mVsyncMoveThreshold),
+                          ScheduleResult::Scheduled);
+                lastTarget = timestamp;
+            },
+            "oo");
+
+    mDispatch.schedule(tmp, 999, 1000);
+    advanceToNextCallback();
+    EXPECT_THAT(lastTarget, Eq(1000));
+
+    advanceToNextCallback();
+    EXPECT_THAT(lastTarget, Eq(2000));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 200)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 150)).InSequence(seq);
+
+    CountingCallback cb(mDispatch);
+    mDispatch.schedule(cb, 0, 1000);
+
+    mMockClock.advanceBy(750);
+    mDispatch.schedule(cb, 50, 1000);
+
+    advanceToNextCallback();
+    mDispatch.schedule(cb, 50, 2000);
+
+    mMockClock.advanceBy(800);
+    mDispatch.schedule(cb, 100, 2000);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, lateModifications) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 400)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 350)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 950)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 500, 1000);
+    mDispatch.schedule(cb1, 100, 1000);
+
+    advanceToNextCallback();
+    mDispatch.schedule(cb0, 200, 2000);
+    mDispatch.schedule(cb1, 150, 1000);
+
+    advanceToNextCallback();
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, doesntCancelPriorValidTimerForFutureMod) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+    mDispatch.schedule(cb0, 500, 1000);
+    mDispatch.schedule(cb1, 500, 20000);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    mDispatch.schedule(cb0, 500, 1000);
+    mDispatch.cancel(cb0);
+    mDispatch.schedule(cb0, 100, 1000);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
+    VSyncDispatch::CallbackToken token(100);
+    EXPECT_THAT(mDispatch.schedule(token, 100, 1000), Eq(ScheduleResult::Error));
+    EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
+    CountingCallback cb0(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::Scheduled);
+}
+
+// b/1450138150
+TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 500));
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+    mMockClock.advanceBy(400);
+
+    EXPECT_EQ(mDispatch.schedule(cb, 800, 1000), ScheduleResult::Scheduled);
+    advanceToNextCallback();
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+            .Times(2)
+            .WillOnce(Return(1000))
+            .WillOnce(Return(1002));
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+    mMockClock.advanceBy(400);
+    EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
+    CountingCallback cb0(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+    advanceToNextCallback();
+    EXPECT_EQ(mDispatch.schedule(cb0, 1100, 2000), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    CountingCallback cb0(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+    advanceToNextCallback();
+    EXPECT_EQ(mDispatch.schedule(cb0, 1900, 2000), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 600));
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+
+    EXPECT_EQ(mDispatch.schedule(cb, 1400, 1000), ScheduleResult::Scheduled);
+
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+    EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
+
+    VSyncCallbackRegistration cb(
+            mDispatch, [](auto, auto) {}, "");
+    VSyncCallbackRegistration cb1(std::move(cb));
+    cb.schedule(100, 1000);
+    cb.cancel();
+
+    cb1.schedule(500, 1000);
+    cb1.cancel();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+    EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
+
+    VSyncCallbackRegistration cb(
+            mDispatch, [](auto, auto) {}, "");
+    VSyncCallbackRegistration cb1(
+            mDispatch, [](auto, auto) {}, "");
+    cb1 = std::move(cb);
+    cb.schedule(100, 1000);
+    cb.cancel();
+
+    cb1.schedule(500, 1000);
+    cb1.cancel();
+}
+
+// b/154303580
+TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 1200)).InSequence(seq);
+    CountingCallback cb1(mDispatch);
+    CountingCallback cb2(mDispatch);
+
+    EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+
+    mMockClock.setLag(100);
+    mMockClock.advanceBy(620);
+
+    EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+    mMockClock.advanceBy(80);
+
+    EXPECT_THAT(cb1.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb2.mCalls.size(), Eq(0));
+}
+
+// b/154303580.
+// If the same callback tries to reschedule itself after it's too late, timer opts to apply the
+// update later, as opposed to blocking the calling thread.
+TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminentSameCallback) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 930)).InSequence(seq);
+    CountingCallback cb(mDispatch);
+
+    EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+
+    mMockClock.setLag(100);
+    mMockClock.advanceBy(620);
+
+    EXPECT_EQ(mDispatch.schedule(cb, 370, 2000), ScheduleResult::Scheduled);
+    mMockClock.advanceBy(80);
+
+    EXPECT_THAT(cb.mCalls.size(), Eq(1));
+}
+
+// b/154303580.
+TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+    CountingCallback cb1(mDispatch);
+    CountingCallback cb2(mDispatch);
+
+    EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+
+    mMockClock.setLag(100);
+    mMockClock.advanceBy(620);
+
+    EXPECT_EQ(mDispatch.cancel(cb2), CancelResult::Cancelled);
+
+    mMockClock.advanceBy(80);
+
+    EXPECT_THAT(cb1.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb2.mCalls.size(), Eq(0));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 1280)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+    CountingCallback cb1(mDispatch);
+    CountingCallback cb2(mDispatch);
+
+    EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled);
+
+    mMockClock.setLag(100);
+    mMockClock.advanceBy(620);
+
+    EXPECT_EQ(mDispatch.cancel(cb1), CancelResult::Cancelled);
+
+    EXPECT_THAT(cb1.mCalls.size(), Eq(0));
+    EXPECT_THAT(cb2.mCalls.size(), Eq(0));
+    mMockClock.advanceToNextCallback();
+
+    EXPECT_THAT(cb1.mCalls.size(), Eq(0));
+    EXPECT_THAT(cb2.mCalls.size(), Eq(1));
+}
+
+class VSyncDispatchTimerQueueEntryTest : public testing::Test {
+protected:
+    nsecs_t const mPeriod = 1000;
+    nsecs_t const mVsyncMoveThreshold = 200;
+    NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
+};
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
+    std::string name("basicname");
+    VSyncDispatchTimerQueueEntry entry(
+            name, [](auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_THAT(entry.name(), Eq(name));
+    EXPECT_FALSE(entry.lastExecutedVsyncTarget());
+    EXPECT_FALSE(entry.wakeupTime());
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+
+    EXPECT_FALSE(entry.wakeupTime());
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    auto const wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(900));
+
+    entry.disarm();
+    EXPECT_FALSE(entry.wakeupTime());
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, stateSchedulingReallyLongWakeupLatency) {
+    auto const duration = 500;
+    auto const now = 8750;
+
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration))
+            .Times(1)
+            .WillOnce(Return(10000));
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+
+    EXPECT_FALSE(entry.wakeupTime());
+    EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled));
+    auto const wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(9500));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) {
+    auto callCount = 0;
+    auto vsyncCalledTime = 0;
+    auto wakeupCalledTime = 0;
+    VSyncDispatchTimerQueueEntry entry(
+            "test",
+            [&](auto vsyncTime, auto wakeupTime) {
+                callCount++;
+                vsyncCalledTime = vsyncTime;
+                wakeupCalledTime = wakeupTime;
+            },
+            mVsyncMoveThreshold);
+
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    auto const wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(900));
+
+    entry.callback(entry.executing(), *wakeup);
+
+    EXPECT_THAT(callCount, Eq(1));
+    EXPECT_THAT(vsyncCalledTime, Eq(mPeriod));
+    EXPECT_THAT(wakeupCalledTime, Eq(*wakeup));
+    EXPECT_FALSE(entry.wakeupTime());
+    auto lastCalledTarget = entry.lastExecutedVsyncTarget();
+    ASSERT_TRUE(lastCalledTarget);
+    EXPECT_THAT(*lastCalledTarget, Eq(mPeriod));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(2)
+            .WillOnce(Return(1000))
+            .WillOnce(Return(1020));
+
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+
+    EXPECT_FALSE(entry.wakeupTime());
+    entry.update(mStubTracker, 0);
+    EXPECT_FALSE(entry.wakeupTime());
+
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    auto wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(wakeup, Eq(900));
+
+    entry.update(mStubTracker, 0);
+    wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(920));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    entry.update(mStubTracker, 0);
+
+    auto const wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(wakeup));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    entry.executing(); // 1000 is executing
+    // had 1000 not been executing, this could have been scheduled for time 800.
+    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
+
+    EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
+
+    EXPECT_THAT(entry.schedule(200, 1001, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest,
+       willRequestNextEstimateWhenSnappingToNextTargettableVSync) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+
+    Sequence seq;
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+            .InSequence(seq)
+            .WillOnce(Return(1000));
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+            .InSequence(seq)
+            .WillOnce(Return(1000));
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold))
+            .InSequence(seq)
+            .WillOnce(Return(2000));
+
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+
+    entry.executing(); // 1000 is executing
+
+    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) {
+    static constexpr auto effectualOffset = 200;
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
+    entry.addPendingWorkloadUpdate(100, 400);
+    entry.addPendingWorkloadUpdate(effectualOffset, 700);
+    EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
+    entry.update(mStubTracker, 0);
+    EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
+    EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset));
+}
+
+} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
new file mode 100644
index 0000000..9c1ec07
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp
@@ -0,0 +1,301 @@
+/*
+ * 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 "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/VSyncModulator.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace testing;
+
+namespace android::scheduler {
+
+class MockScheduler : public IPhaseOffsetControl {
+public:
+    void setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
+        mPhaseOffset[handle] = phaseOffset;
+    }
+
+    nsecs_t getOffset(ConnectionHandle handle) { return mPhaseOffset[handle]; }
+
+private:
+    std::unordered_map<ConnectionHandle, nsecs_t> mPhaseOffset;
+};
+
+class VSyncModulatorTest : public testing::Test {
+protected:
+    static constexpr auto MIN_EARLY_FRAME_COUNT_TRANSACTION =
+            VSyncModulator::MIN_EARLY_FRAME_COUNT_TRANSACTION;
+    // Add a 1ms slack to avoid strange timer race conditions.
+    static constexpr auto MARGIN_FOR_TX_APPLY = VSyncModulator::MARGIN_FOR_TX_APPLY + 1ms;
+
+    // Used to enumerate the different offsets we have
+    enum {
+        SF_LATE,
+        APP_LATE,
+        SF_EARLY,
+        APP_EARLY,
+        SF_EARLY_GL,
+        APP_EARLY_GL,
+    };
+
+    std::unique_ptr<VSyncModulator> mVSyncModulator;
+    MockScheduler mMockScheduler;
+    ConnectionHandle mAppConnection{1};
+    ConnectionHandle mSfConnection{2};
+    VSyncModulator::OffsetsConfig mOffsets = {{SF_EARLY, APP_EARLY},
+                                              {SF_EARLY_GL, APP_EARLY_GL},
+                                              {SF_LATE, APP_LATE}};
+
+    void SetUp() override {
+        mVSyncModulator = std::make_unique<VSyncModulator>(mMockScheduler, mAppConnection,
+                                                           mSfConnection, mOffsets);
+        mVSyncModulator->setPhaseOffsets(mOffsets);
+
+        EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+    };
+
+    void TearDown() override { mVSyncModulator.reset(); }
+};
+
+TEST_F(VSyncModulatorTest, Normal) {
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+    }
+}
+
+TEST_F(VSyncModulatorTest, EarlyEnd) {
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->onRefreshed(false);
+    EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStart) {
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->onRefreshed(false);
+    EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartWithEarly) {
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Early);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->onRefreshed(false);
+    EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartWithMoreTransactions) {
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+        mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
+        std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->onRefreshed(false);
+    EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEnd) {
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->onRefreshed(false);
+    EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) {
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) {
+        mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal);
+        std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd);
+    std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY);
+    mVSyncModulator->onTransactionHandled();
+    EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+
+    for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) {
+        mVSyncModulator->onRefreshed(false);
+        EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection));
+        EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection));
+    }
+
+    mVSyncModulator->onRefreshed(false);
+    EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection));
+    EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection));
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
new file mode 100644
index 0000000..fc39235
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -0,0 +1,470 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/VSyncPredictor.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <algorithm>
+#include <chrono>
+#include <utility>
+
+using namespace testing;
+using namespace std::literals;
+
+namespace android::scheduler {
+
+MATCHER_P2(IsCloseTo, value, tolerance, "is within tolerance") {
+    return arg <= value + tolerance && arg >= value - tolerance;
+}
+
+std::vector<nsecs_t> generateVsyncTimestamps(size_t count, nsecs_t period, nsecs_t bias) {
+    std::vector<nsecs_t> vsyncs(count);
+    std::generate(vsyncs.begin(), vsyncs.end(),
+                  [&, n = 0]() mutable { return n++ * period + bias; });
+    return vsyncs;
+}
+
+struct VSyncPredictorTest : testing::Test {
+    nsecs_t mNow = 0;
+    nsecs_t mPeriod = 1000;
+    static constexpr size_t kHistorySize = 10;
+    static constexpr size_t kMinimumSamplesForPrediction = 6;
+    static constexpr size_t kOutlierTolerancePercent = 25;
+    static constexpr nsecs_t mMaxRoundingError = 100;
+
+    VSyncPredictor tracker{mPeriod, kHistorySize, kMinimumSamplesForPrediction,
+                           kOutlierTolerancePercent};
+};
+
+TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) {
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+
+    EXPECT_THAT(slope, Eq(mPeriod));
+    EXPECT_THAT(intercept, Eq(0));
+
+    auto const changedPeriod = 2000;
+    tracker.setPeriod(changedPeriod);
+    std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, Eq(changedPeriod));
+    EXPECT_THAT(intercept, Eq(0));
+}
+
+TEST_F(VSyncPredictorTest, reportsSamplesNeededWhenHasNoDataPoints) {
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_TRUE(tracker.needsMoreSamples(mNow += mPeriod));
+        tracker.addVsyncTimestamp(mNow);
+    }
+    EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+}
+
+TEST_F(VSyncPredictorTest, reportsSamplesNeededAfterExplicitRateChange) {
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        tracker.addVsyncTimestamp(mNow += mPeriod);
+    }
+    EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+
+    auto const changedPeriod = mPeriod * 2;
+    tracker.setPeriod(changedPeriod);
+    EXPECT_TRUE(tracker.needsMoreSamples(mNow));
+
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_TRUE(tracker.needsMoreSamples(mNow += changedPeriod));
+        tracker.addVsyncTimestamp(mNow);
+    }
+    EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+}
+
+TEST_F(VSyncPredictorTest, transitionsToModelledPointsAfterSynthetic) {
+    auto last = mNow;
+    auto const bias = 10;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+        mNow += mPeriod - bias;
+        last = mNow;
+        tracker.addVsyncTimestamp(mNow);
+        mNow += bias;
+    }
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod - bias));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod - bias));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 990), Eq(mNow + 2 * mPeriod - bias));
+}
+
+TEST_F(VSyncPredictorTest, uponNotifiedOfInaccuracyUsesSynthetic) {
+    auto const slightlyLessPeriod = mPeriod - 10;
+    auto const changedPeriod = mPeriod - 1;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        tracker.addVsyncTimestamp(mNow += slightlyLessPeriod);
+    }
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + slightlyLessPeriod));
+    tracker.setPeriod(changedPeriod);
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + changedPeriod));
+}
+
+// b/159882858
+TEST_F(VSyncPredictorTest, updatesTimebaseForSyntheticAfterIdleTime) {
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_TRUE(tracker.addVsyncTimestamp(mNow += mPeriod));
+    }
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+
+    auto const halfPeriod = mPeriod >> 2;
+    nsecs_t relativelyLongGapWithDrift = mPeriod * 100 + halfPeriod;
+
+    EXPECT_FALSE(tracker.addVsyncTimestamp(mNow += relativelyLongGapWithDrift));
+
+    tracker.resetModel();
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+}
+
+TEST_F(VSyncPredictorTest, uponBadVsyncWillSwitchToSyntheticWhileRecalibrating) {
+    auto const slightlyMorePeriod = mPeriod + 10;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_TRUE(tracker.addVsyncTimestamp(mNow += slightlyMorePeriod));
+    }
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + slightlyMorePeriod));
+
+    auto const halfPeriod = mPeriod >> 2;
+    EXPECT_FALSE(tracker.addVsyncTimestamp(mNow += halfPeriod));
+
+    tracker.resetModel();
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+}
+
+TEST_F(VSyncPredictorTest, adaptsToFenceTimelines_60hzHighVariance) {
+    // these are precomputed simulated 16.6s vsyncs with uniform distribution +/- 1.6ms error
+    std::vector<nsecs_t> const simulatedVsyncs{
+            15492949,  32325658,  49534984,  67496129,  84652891,
+            100332564, 117737004, 132125931, 149291099, 165199602,
+    };
+    auto constexpr idealPeriod = 16600000;
+    auto constexpr expectedPeriod = 16639242;
+    auto constexpr expectedIntercept = 1049341;
+
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, adaptsToFenceTimelines_90hzLowVariance) {
+    // these are precomputed simulated 11.1 vsyncs with uniform distribution +/- 1ms error
+    std::vector<nsecs_t> const simulatedVsyncs{
+            11167047, 22603464, 32538479, 44938134, 56321268,
+            66730346, 78062637, 88171429, 99707843, 111397621,
+    };
+    auto idealPeriod = 11110000;
+    auto expectedPeriod = 11089413;
+    auto expectedIntercept = 94421;
+
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, adaptsToFenceTimelinesDiscontinuous_22hzLowVariance) {
+    // these are 11.1s vsyncs with low variance, randomly computed, between -1 and 1ms
+    std::vector<nsecs_t> const simulatedVsyncs{
+            45259463,   // 0
+            91511026,   // 1
+            136307650,  // 2
+            1864501714, // 40
+            1908641034, // 41
+            1955278544, // 42
+            4590180096, // 100
+            4681594994, // 102
+            5499224734, // 120
+            5591378272, // 122
+    };
+    auto idealPeriod = 45454545;
+    auto expectedPeriod = 45450152;
+    auto expectedIntercept = 469647;
+
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, againstOutliersDiscontinuous_500hzLowVariance) {
+    std::vector<nsecs_t> const simulatedVsyncs{
+            1992548,    // 0
+            4078038,    // 1
+            6165794,    // 2
+            7958171,    // 3
+            10193537,   // 4
+            2401840200, // 1200
+            2403000000, // an outlier that should be excluded (1201 and a half)
+            2405803629, // 1202
+            2408028599, // 1203
+            2410121051, // 1204
+    };
+    auto idealPeriod = 2000000;
+    auto expectedPeriod = 1999892;
+    auto expectedIntercept = 86342;
+
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, handlesVsyncChange) {
+    auto const fastPeriod = 100;
+    auto const fastTimeBase = 100;
+    auto const slowPeriod = 400;
+    auto const slowTimeBase = 800;
+    auto const simulatedVsyncsFast =
+            generateVsyncTimestamps(kMinimumSamplesForPrediction, fastPeriod, fastTimeBase);
+    auto const simulatedVsyncsSlow =
+            generateVsyncTimestamps(kMinimumSamplesForPrediction, slowPeriod, slowTimeBase);
+
+    tracker.setPeriod(fastPeriod);
+    for (auto const& timestamp : simulatedVsyncsFast) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+
+    auto const mMaxRoundingError = 100;
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(fastPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+
+    tracker.setPeriod(slowPeriod);
+    for (auto const& timestamp : simulatedVsyncsSlow) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(slowPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) {
+    auto const fastPeriod = 101000;
+    auto const fastTimeBase = fastPeriod - 500;
+    auto const fastPeriod2 = 99000;
+
+    auto const slowPeriod = 400000;
+    auto const slowTimeBase = 800000 - 201;
+    auto const simulatedVsyncsFast =
+            generateVsyncTimestamps(kMinimumSamplesForPrediction, fastPeriod, fastTimeBase);
+    auto const simulatedVsyncsSlow =
+            generateVsyncTimestamps(kMinimumSamplesForPrediction, slowPeriod, slowTimeBase);
+    auto const simulatedVsyncsFast2 =
+            generateVsyncTimestamps(kMinimumSamplesForPrediction, fastPeriod2, fastTimeBase);
+
+    auto idealPeriod = 100000;
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncsFast) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, Eq(fastPeriod));
+    EXPECT_THAT(intercept, Eq(0));
+
+    tracker.setPeriod(slowPeriod);
+    for (auto const& timestamp : simulatedVsyncsSlow) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+
+    // we had a model for 100ns mPeriod before, use that until the new samples are
+    // sufficiently built up
+    tracker.setPeriod(idealPeriod);
+    std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, Eq(fastPeriod));
+    EXPECT_THAT(intercept, Eq(0));
+
+    for (auto const& timestamp : simulatedVsyncsFast2) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, Eq(fastPeriod2));
+    EXPECT_THAT(intercept, Eq(0));
+}
+
+TEST_F(VSyncPredictorTest, willBecomeInaccurateAfterA_longTimeWithNoSamples) {
+    auto const simulatedVsyncs = generateVsyncTimestamps(kMinimumSamplesForPrediction, mPeriod, 0);
+
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    auto const mNow = *simulatedVsyncs.rbegin();
+    EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+
+    // TODO: would be better to decay this as a result of the variance of the samples
+    static auto constexpr aLongTimeOut = 1000000000;
+    EXPECT_TRUE(tracker.needsMoreSamples(mNow + aLongTimeOut));
+}
+
+TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) {
+    auto const simulatedVsyncs =
+            generateVsyncTimestamps(kMinimumSamplesForPrediction + 1, mPeriod, 0);
+    nsecs_t const mNow = 0;
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mPeriod));
+
+    nsecs_t const aBitOfTime = 422;
+
+    for (auto i = 0; i < kMinimumSamplesForPrediction; i++) {
+        tracker.addVsyncTimestamp(simulatedVsyncs[i]);
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(simulatedVsyncs[i] + aBitOfTime),
+                    Eq(mPeriod + simulatedVsyncs[i]));
+    }
+
+    for (auto i = kMinimumSamplesForPrediction; i < simulatedVsyncs.size(); i++) {
+        tracker.addVsyncTimestamp(simulatedVsyncs[i]);
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(simulatedVsyncs[i] + aBitOfTime),
+                    Eq(mPeriod + simulatedVsyncs[i]));
+    }
+}
+
+// See b/145667109, and comment in prod code under test.
+TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) {
+    std::vector<nsecs_t> const simulatedVsyncs{
+            158929578733000,
+            158929306806205, // oldest TS in ringbuffer
+            158929650879052,
+            158929661969209,
+            158929684198847,
+            158929695268171,
+            158929706370359,
+    };
+    auto const idealPeriod = 11111111;
+    auto const expectedPeriod = 11113919;
+    auto const expectedIntercept = -1195945;
+
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+
+    // (timePoint - oldestTS) % expectedPeriod works out to be: 395334
+    // (timePoint - oldestTS) / expectedPeriod works out to be: 38.96
+    // so failure to account for the offset will floor the ordinal to 38, which was in the past.
+    auto const timePoint = 158929728723871;
+    auto const prediction = tracker.nextAnticipatedVSyncTimeFrom(timePoint);
+    EXPECT_THAT(prediction, Ge(timePoint));
+}
+
+// See b/151146131
+TEST_F(VSyncPredictorTest, hasEnoughPrecision) {
+    VSyncPredictor tracker{mPeriod, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+    std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675,
+                                               840923581635, 840940161584, 840956868096,
+                                               840973702473, 840990256277, 841007116851,
+                                               841023722530, 841040452167, 841057073002,
+                                               841073800920, 841090474360, 841107278632,
+                                               841123898634, 841140750875, 841157287127,
+                                               841591357014, 840856664232
+
+    };
+    auto const idealPeriod = 16666666;
+    auto const expectedPeriod = 16698426;
+    auto const expectedIntercept = 58055;
+
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, resetsWhenInstructed) {
+    auto const idealPeriod = 10000;
+    auto const realPeriod = 10500;
+    tracker.setPeriod(idealPeriod);
+    for (auto i = 0; i < kMinimumSamplesForPrediction; i++) {
+        tracker.addVsyncTimestamp(i * realPeriod);
+    }
+
+    EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
+                IsCloseTo(realPeriod, mMaxRoundingError));
+    tracker.resetModel();
+    EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
+                IsCloseTo(idealPeriod, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, slopeAlwaysValid) {
+    constexpr auto kNumVsyncs = 100;
+    auto invalidPeriod = mPeriod;
+    auto now = 0;
+    for (int i = 0; i < kNumVsyncs; i++) {
+        tracker.addVsyncTimestamp(now);
+        now += invalidPeriod;
+        invalidPeriod *= 0.9f;
+
+        auto [slope, intercept] = tracker.getVSyncPredictionModel();
+        EXPECT_THAT(slope, IsCloseTo(mPeriod, mPeriod * kOutlierTolerancePercent / 100.f));
+
+        // When VsyncPredictor returns the period it means that it doesn't know how to predict and
+        // it needs to get more samples
+        if (slope == mPeriod && intercept == 0) {
+            EXPECT_TRUE(tracker.needsMoreSamples(now));
+        }
+    }
+}
+
+constexpr nsecs_t operator""_years(unsigned long long years) noexcept {
+    using namespace std::chrono_literals;
+    return years * 365 * 24 * 3600 *
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+}
+TEST_F(VSyncPredictorTest, aPhoneThatHasBeenAroundAWhileCanStillComputePeriod) {
+    constexpr nsecs_t timeBase = 100_years;
+
+    for (auto i = 0; i < kHistorySize; i++) {
+        tracker.addVsyncTimestamp(timeBase + i * mPeriod);
+    }
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(mPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, Eq(0));
+}
+
+} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
new file mode 100644
index 0000000..a972562
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -0,0 +1,742 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/TimeKeeper.h"
+#include "Scheduler/VSyncDispatch.h"
+#include "Scheduler/VSyncReactor.h"
+#include "Scheduler/VSyncTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <ui/Fence.h>
+#include <ui/FenceTime.h>
+#include <array>
+
+using namespace testing;
+using namespace std::literals;
+namespace android::scheduler {
+
+class MockVSyncTracker : public VSyncTracker {
+public:
+    MockVSyncTracker() { ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); }
+    MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
+    MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+    MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+    MOCK_METHOD1(setPeriod, void(nsecs_t));
+    MOCK_METHOD0(resetModel, void());
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+class VSyncTrackerWrapper : public VSyncTracker {
+public:
+    VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
+
+    bool addVsyncTimestamp(nsecs_t timestamp) final {
+        return mTracker->addVsyncTimestamp(timestamp);
+    }
+    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
+        return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
+    }
+    nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
+    void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
+    void resetModel() final { mTracker->resetModel(); }
+    void dump(std::string& result) const final { mTracker->dump(result); }
+
+private:
+    std::shared_ptr<VSyncTracker> const mTracker;
+};
+
+class MockClock : public Clock {
+public:
+    MOCK_CONST_METHOD0(now, nsecs_t());
+};
+
+class ClockWrapper : public Clock {
+public:
+    ClockWrapper(std::shared_ptr<Clock> const& clock) : mClock(clock) {}
+
+    nsecs_t now() const { return mClock->now(); }
+
+private:
+    std::shared_ptr<Clock> const mClock;
+};
+
+class MockVSyncDispatch : public VSyncDispatch {
+public:
+    MOCK_METHOD2(registerCallback,
+                 CallbackToken(std::function<void(nsecs_t, nsecs_t)> const&, std::string));
+    MOCK_METHOD1(unregisterCallback, void(CallbackToken));
+    MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
+    MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+class VSyncDispatchWrapper : public VSyncDispatch {
+public:
+    VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {}
+    CallbackToken registerCallback(std::function<void(nsecs_t, nsecs_t)> const& callbackFn,
+                                   std::string callbackName) final {
+        return mDispatch->registerCallback(callbackFn, callbackName);
+    }
+
+    void unregisterCallback(CallbackToken token) final { mDispatch->unregisterCallback(token); }
+
+    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
+                            nsecs_t earliestVsync) final {
+        return mDispatch->schedule(token, workDuration, earliestVsync);
+    }
+
+    CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
+
+    void dump(std::string& result) const final { return mDispatch->dump(result); }
+
+private:
+    std::shared_ptr<VSyncDispatch> const mDispatch;
+};
+
+std::shared_ptr<FenceTime> generateInvalidFence() {
+    sp<Fence> fence = new Fence();
+    return std::make_shared<FenceTime>(fence);
+}
+
+std::shared_ptr<FenceTime> generatePendingFence() {
+    sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+    return std::make_shared<FenceTime>(fence);
+}
+
+void signalFenceWithTime(std::shared_ptr<FenceTime> const& fence, nsecs_t time) {
+    FenceTime::Snapshot snap(time);
+    fence->applyTrustedSnapshot(snap);
+}
+
+std::shared_ptr<FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
+    sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+    std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
+    signalFenceWithTime(ft, time);
+    return ft;
+}
+
+class StubCallback : public DispSync::Callback {
+public:
+    void onDispSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) final {
+        std::lock_guard<std::mutex> lk(mMutex);
+        mLastCallTime = when;
+    }
+    std::optional<nsecs_t> lastCallTime() const {
+        std::lock_guard<std::mutex> lk(mMutex);
+        return mLastCallTime;
+    }
+
+private:
+    std::mutex mutable mMutex;
+    std::optional<nsecs_t> mLastCallTime GUARDED_BY(mMutex);
+};
+
+class VSyncReactorTest : public testing::Test {
+protected:
+    VSyncReactorTest()
+          : mMockDispatch(std::make_shared<NiceMock<MockVSyncDispatch>>()),
+            mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
+            mMockClock(std::make_shared<NiceMock<MockClock>>()),
+            mReactor(std::make_unique<ClockWrapper>(mMockClock),
+                     std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
+                     std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit,
+                     false /* supportKernelIdleTimer */) {
+        ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
+        ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
+    }
+
+    std::shared_ptr<MockVSyncDispatch> mMockDispatch;
+    std::shared_ptr<MockVSyncTracker> mMockTracker;
+    std::shared_ptr<MockClock> mMockClock;
+    static constexpr size_t kPendingLimit = 3;
+    static constexpr nsecs_t mDummyTime = 47;
+    static constexpr nsecs_t mPhase = 3000;
+    static constexpr nsecs_t mAnotherPhase = 5200;
+    static constexpr nsecs_t period = 10000;
+    static constexpr nsecs_t mFakeVSyncTime = 2093;
+    static constexpr nsecs_t mFakeWakeupTime = 1892;
+    static constexpr nsecs_t mFakeNow = 2214;
+    static constexpr const char mName[] = "callbacky";
+    VSyncDispatch::CallbackToken const mFakeToken{2398};
+
+    nsecs_t lastCallbackTime = 0;
+    StubCallback outerCb;
+    std::function<void(nsecs_t, nsecs_t)> innerCb;
+
+    VSyncReactor mReactor;
+};
+
+TEST_F(VSyncReactorTest, addingNullFenceCheck) {
+    EXPECT_FALSE(mReactor.addPresentFence(nullptr));
+}
+
+TEST_F(VSyncReactorTest, addingInvalidFenceSignalsNeedsMoreInfo) {
+    EXPECT_TRUE(mReactor.addPresentFence(generateInvalidFence()));
+}
+
+TEST_F(VSyncReactorTest, addingSignalledFenceAddsToTracker) {
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
+}
+
+TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) {
+    nsecs_t anotherDummyTime = 2919019201;
+
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(0);
+    auto pendingFence = generatePendingFence();
+    EXPECT_FALSE(mReactor.addPresentFence(pendingFence));
+    Mock::VerifyAndClearExpectations(mMockTracker.get());
+
+    signalFenceWithTime(pendingFence, mDummyTime);
+
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(anotherDummyTime));
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(anotherDummyTime)));
+}
+
+TEST_F(VSyncReactorTest, limitsPendingFences) {
+    std::array<std::shared_ptr<FenceTime>, kPendingLimit * 2> fences;
+    std::array<nsecs_t, fences.size()> fakeTimes;
+    std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); });
+    std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable {
+        i++;
+        return i * i;
+    });
+
+    for (auto const& fence : fences) {
+        mReactor.addPresentFence(fence);
+    }
+
+    for (auto i = fences.size() - kPendingLimit; i < fences.size(); i++) {
+        EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimes[i]));
+    }
+
+    for (auto i = 0u; i < fences.size(); i++) {
+        signalFenceWithTime(fences[i], fakeTimes[i]);
+    }
+    mReactor.addPresentFence(generatePendingFence());
+}
+
+TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) {
+    static constexpr size_t aFewTimes = 8;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime)).Times(1);
+
+    mReactor.setIgnorePresentFences(true);
+    for (auto i = 0; i < aFewTimes; i++) {
+        mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime));
+    }
+
+    mReactor.setIgnorePresentFences(false);
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
+}
+
+TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) {
+    bool periodFlushed = true;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
+    mReactor.setIgnorePresentFences(true);
+
+    nsecs_t const newPeriod = 5000;
+    mReactor.setPeriod(newPeriod);
+
+    EXPECT_TRUE(mReactor.addResyncSample(0, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_FALSE(mReactor.addResyncSample(newPeriod, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
+    nsecs_t const fakeTimestamp = 4839;
+    EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
+    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(1)
+            .WillOnce(Return(fakeTimestamp));
+
+    EXPECT_THAT(mReactor.computeNextRefresh(0, mMockClock->now()), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForExpectedPresentTime) {
+    nsecs_t const fakeTimestamp = 4839;
+    EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
+    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(1)
+            .WillOnce(Return(fakeTimestamp));
+
+    EXPECT_THAT(mReactor.expectedPresentTime(mMockClock->now()), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
+    nsecs_t const fakeTimestamp = 4839;
+    nsecs_t const fakePeriod = 1010;
+    nsecs_t const mFakeNow = 2214;
+    int const numPeriodsOut = 3;
+    EXPECT_CALL(*mMockClock, now()).WillOnce(Return(mFakeNow));
+    EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
+    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(mFakeNow + numPeriodsOut * fakePeriod))
+            .WillOnce(Return(fakeTimestamp));
+    EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut, mMockClock->now()), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, getPeriod) {
+    nsecs_t const fakePeriod = 1010;
+    EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
+    EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod));
+}
+
+TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) {
+    nsecs_t const newPeriod = 5000;
+    EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
+    mReactor.setPeriod(newPeriod);
+
+    bool periodFlushed = true;
+    EXPECT_TRUE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+
+    EXPECT_TRUE(mReactor.addResyncSample(20000, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+
+    Mock::VerifyAndClearExpectations(mMockTracker.get());
+    EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
+
+    EXPECT_FALSE(mReactor.addResyncSample(25000, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) {
+    nsecs_t sampleTime = 0;
+    nsecs_t const newPeriod = 5000;
+    mReactor.setPeriod(newPeriod);
+    bool periodFlushed = true;
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+
+    mReactor.setPeriod(period);
+    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, changingToAThirdPeriodWillWaitForLastPeriod) {
+    nsecs_t sampleTime = 0;
+    nsecs_t const secondPeriod = 5000;
+    nsecs_t const thirdPeriod = 2000;
+
+    mReactor.setPeriod(secondPeriod);
+    bool periodFlushed = true;
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    mReactor.setPeriod(thirdPeriod);
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += thirdPeriod, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSync) {
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
+            .WillOnce(Return(false))
+            .WillOnce(Return(true))
+            .WillOnce(Return(true));
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+
+    nsecs_t skewyPeriod = period >> 1;
+    bool periodFlushed = false;
+    nsecs_t sampleTime = 0;
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSyncPendingFence) {
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
+            .Times(2)
+            .WillOnce(Return(false))
+            .WillOnce(Return(true));
+
+    auto fence = generatePendingFence();
+    EXPECT_FALSE(mReactor.addPresentFence(fence));
+    signalFenceWithTime(fence, period >> 1);
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
+TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
+    nsecs_t const newPeriod = 5000;
+    mReactor.setPeriod(newPeriod);
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
+TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) {
+    nsecs_t const newPeriod = 5000;
+    EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
+    mReactor.setPeriod(newPeriod);
+
+    bool periodFlushed = true;
+    EXPECT_TRUE(mReactor.addResyncSample(5000, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    Mock::VerifyAndClearExpectations(mMockTracker.get());
+
+    EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
+    EXPECT_FALSE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, addResyncSampleTypical) {
+    nsecs_t const fakeTimestamp = 3032;
+    bool periodFlushed = false;
+
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
+    EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) {
+    bool periodFlushed = false;
+    nsecs_t const newPeriod = 4000;
+
+    mReactor.setPeriod(newPeriod);
+
+    auto time = 0;
+    auto constexpr numTimestampSubmissions = 10;
+    for (auto i = 0; i < numTimestampSubmissions; i++) {
+        time += period;
+        EXPECT_TRUE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+        EXPECT_FALSE(periodFlushed);
+    }
+
+    time += newPeriod;
+    EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+
+    for (auto i = 0; i < numTimestampSubmissions; i++) {
+        time += newPeriod;
+        EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+        EXPECT_FALSE(periodFlushed);
+    }
+}
+
+TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsHwVsync) {
+    auto time = 0;
+    bool periodFlushed = false;
+    nsecs_t const newPeriod = 4000;
+    mReactor.setPeriod(newPeriod);
+
+    time += period;
+    mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+
+    time += newPeriod;
+    mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
+
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
+static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) {
+    return period - phase;
+}
+
+TEST_F(VSyncReactorTest, addEventListener) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, addEventListenerTwiceChangesPhase) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _)) // mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, eventListenerGetsACallbackAndReschedules) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
+            .Times(2)
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    ASSERT_TRUE(innerCb);
+    innerCb(mFakeVSyncTime, mFakeWakeupTime);
+    innerCb(mFakeVSyncTime, mFakeWakeupTime);
+}
+
+TEST_F(VSyncReactorTest, callbackTimestampDistributedIsWakeupTime) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, _))
+            .InSequence(seq)
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
+            .InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    ASSERT_TRUE(innerCb);
+    innerCb(mFakeVSyncTime, mFakeWakeupTime);
+    EXPECT_THAT(outerCb.lastCallTime(), Optional(mFakeWakeupTime));
+}
+
+TEST_F(VSyncReactorTest, eventListenersRemovedOnDestruction) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+}
+
+// b/149221293
+TEST_F(VSyncReactorTest, selfRemovingEventListenerStopsCallbacks) {
+    class SelfRemovingCallback : public DispSync::Callback {
+    public:
+        SelfRemovingCallback(VSyncReactor& vsr) : mVsr(vsr) {}
+        void onDispSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) final {
+            mVsr.removeEventListener(this, &when);
+        }
+
+    private:
+        VSyncReactor& mVsr;
+    } selfRemover(mReactor);
+
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &selfRemover, lastCallbackTime);
+    innerCb(0, 0);
+}
+
+TEST_F(VSyncReactorTest, addEventListenerChangePeriod) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(period, mAnotherPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, changingPeriodChangesOffsetsOnNextCb) {
+    static constexpr nsecs_t anotherPeriod = 23333;
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockTracker, setPeriod(anotherPeriod));
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(anotherPeriod, mPhase), mFakeNow))
+            .InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+
+    bool periodFlushed = false;
+    mReactor.setPeriod(anotherPeriod);
+    EXPECT_TRUE(mReactor.addResyncSample(anotherPeriod, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, std::nullopt, &periodFlushed));
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, offsetsAppliedOnNextOpportunity) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), _))
+            .InSequence(seq)
+            .WillOnce(Return(ScheduleResult::Scheduled));
+
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
+            .InSequence(seq)
+            .WillOnce(Return(ScheduleResult::Scheduled));
+
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
+            .InSequence(seq)
+            .WillOnce(Return(ScheduleResult::Scheduled));
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.changePhaseOffset(&outerCb, mAnotherPhase);
+    ASSERT_TRUE(innerCb);
+    innerCb(mFakeVSyncTime, mFakeWakeupTime);
+}
+
+TEST_F(VSyncReactorTest, negativeOffsetsApplied) {
+    nsecs_t const negativePhase = -4000;
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(period, negativePhase), mFakeNow))
+            .InSequence(seq);
+    mReactor.addEventListener(mName, negativePhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, beginResyncResetsModel) {
+    EXPECT_CALL(*mMockTracker, resetModel());
+    mReactor.beginResync();
+}
+
+TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) {
+    bool periodFlushed = true;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(3);
+    mReactor.setIgnorePresentFences(true);
+
+    nsecs_t const newPeriod = 5000;
+    mReactor.setPeriod(newPeriod);
+
+    EXPECT_TRUE(mReactor.addResyncSample(0, 0, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_TRUE(mReactor.addResyncSample(newPeriod, 0, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_FALSE(mReactor.addResyncSample(newPeriod, newPeriod, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
+TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) {
+    // Create a reactor which supports the kernel idle timer
+    auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock),
+                                    std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
+                                    std::make_unique<VSyncTrackerWrapper>(mMockTracker),
+                                    kPendingLimit, true /* supportKernelIdleTimer */);
+
+    bool periodFlushed = true;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(5);
+    idleReactor.setIgnorePresentFences(true);
+
+    // First, set the same period, which should only be confirmed when we receive two
+    // matching callbacks
+    idleReactor.setPeriod(10000);
+    EXPECT_TRUE(idleReactor.addResyncSample(0, 0, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    // Correct period but incorrect timestamp delta
+    EXPECT_TRUE(idleReactor.addResyncSample(0, 10000, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    // Correct period and correct timestamp delta
+    EXPECT_FALSE(idleReactor.addResyncSample(10000, 10000, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+
+    // Then, set a new period, which should be confirmed as soon as we receive a callback
+    // reporting the new period
+    nsecs_t const newPeriod = 5000;
+    idleReactor.setPeriod(newPeriod);
+    // Incorrect timestamp delta and period
+    EXPECT_TRUE(idleReactor.addResyncSample(20000, 10000, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    // Incorrect timestamp delta but correct period
+    EXPECT_FALSE(idleReactor.addResyncSample(20000, 5000, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+
+    EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
+using VSyncReactorDeathTest = VSyncReactorTest;
+TEST_F(VSyncReactorDeathTest, invalidRemoval) {
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+    EXPECT_DEATH(mReactor.removeEventListener(&outerCb, &lastCallbackTime), ".*");
+}
+
+TEST_F(VSyncReactorDeathTest, invalidChange) {
+    EXPECT_DEATH(mReactor.changePhaseOffset(&outerCb, mPhase), ".*");
+
+    // the current DispSync-interface usage pattern has evolved around an implementation quirk,
+    // which is a callback is assumed to always exist, and it is valid api usage to change the
+    // offset of an object that is in the removed state.
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+    mReactor.changePhaseOffset(&outerCb, mPhase);
+}
+
+TEST_F(VSyncReactorDeathTest, cannotScheduleOnRegistration) {
+    ON_CALL(*mMockDispatch, schedule(_, _, _))
+            .WillByDefault(Return(ScheduleResult::CannotSchedule));
+    EXPECT_DEATH(mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime), ".*");
+}
+
+TEST_F(VSyncReactorDeathTest, cannotScheduleOnCallback) {
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(_, _, _)).WillOnce(Return(ScheduleResult::Scheduled));
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    ASSERT_TRUE(innerCb);
+    Mock::VerifyAndClearExpectations(mMockDispatch.get());
+
+    ON_CALL(*mMockDispatch, schedule(_, _, _))
+            .WillByDefault(Return(ScheduleResult::CannotSchedule));
+    EXPECT_DEATH(innerCb(mFakeVSyncTime, mFakeWakeupTime), ".*");
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
index 7ed57b9..0780af1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "mock/DisplayHardware/MockComposer.h"
 
 namespace android {
@@ -27,3 +31,6 @@
 } // namespace mock
 } // namespace Hwc2
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 3c7e1da..c2c5072 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -39,12 +39,13 @@
 using android::hardware::graphics::composer::V2_1::Display;
 using android::hardware::graphics::composer::V2_1::Error;
 using android::hardware::graphics::composer::V2_1::IComposer;
-using android::hardware::graphics::composer::V2_1::IComposerCallback;
 using android::hardware::graphics::composer::V2_1::Layer;
-using android::hardware::graphics::composer::V2_3::IComposerClient;
+using android::hardware::graphics::composer::V2_4::IComposerCallback;
+using android::hardware::graphics::composer::V2_4::IComposerClient;
 
 class Composer : public Hwc2::Composer {
 public:
+    using Display = android::hardware::graphics::composer::V2_1::Display;
     Composer();
     ~Composer() override;
 
@@ -71,7 +72,6 @@
     MOCK_METHOD2(getDisplayName, Error(Display, std::string*));
     MOCK_METHOD4(getDisplayRequests,
                  Error(Display, uint32_t*, std::vector<Layer>*, std::vector<uint32_t>*));
-    MOCK_METHOD2(getDisplayType, Error(Display, IComposerClient::DisplayType*));
     MOCK_METHOD2(getDozeSupport, Error(Display, bool*));
     MOCK_METHOD5(getHdrCapabilities, Error(Display, std::vector<Hdr>*, float*, float*, float*));
     MOCK_METHOD1(getPerFrameMetadataKeys,
@@ -118,10 +118,29 @@
     MOCK_METHOD4(setDisplayContentSamplingEnabled, Error(Display, bool, uint8_t, uint64_t));
     MOCK_METHOD4(getDisplayedContentSample,
                  Error(Display, uint64_t, uint64_t, DisplayedFrameStats*));
-    MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
     MOCK_METHOD3(setLayerPerFrameMetadataBlobs,
                  Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadataBlob>&));
     MOCK_METHOD2(setDisplayBrightness, Error(Display, float));
+    MOCK_METHOD0(isVsyncPeriodSwitchSupported, bool());
+    MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
+    MOCK_METHOD2(getDisplayConnectionType,
+                 V2_4::Error(Display, IComposerClient::DisplayConnectionType*));
+    MOCK_METHOD3(getSupportedDisplayVsyncPeriods,
+                 V2_4::Error(Display, Config, std::vector<VsyncPeriodNanos>*));
+    MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, VsyncPeriodNanos*));
+    MOCK_METHOD4(setActiveConfigWithConstraints,
+                 V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
+                             VsyncPeriodChangeTimeline*));
+    MOCK_METHOD2(setAutoLowLatencyMode, V2_4::Error(Display, bool));
+    MOCK_METHOD2(getSupportedContentTypes,
+                 V2_4::Error(Display, std::vector<IComposerClient::ContentType>*));
+    MOCK_METHOD2(setContentType, V2_4::Error(Display, IComposerClient::ContentType));
+    MOCK_METHOD5(setLayerGenericMetadata,
+                 V2_4::Error(Display, Layer, const std::string&, bool,
+                             const std::vector<uint8_t>&));
+    MOCK_METHOD1(getLayerGenericMetadataKeys,
+                 V2_4::Error(std::vector<IComposerClient::LayerGenericMetadataKey>*));
+    MOCK_METHOD2(getClientTargetProperty, Error(Display, IComposerClient::ClientTargetProperty*));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
index 6dc28bc..fe99e77 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
@@ -20,66 +20,82 @@
 
 #include "DisplayHardware/HWC2.h"
 
-using HWC2::Error;
-using HWC2::Layer;
+using android::HWC2::Layer;
 
 namespace android {
 namespace Hwc2 {
 namespace mock {
 
+namespace hal = android::hardware::graphics::composer::hal;
+
 class Display : public HWC2::Display {
 public:
+    using Layer = ::Layer;
+
     Display();
     ~Display();
 
-    MOCK_CONST_METHOD0(getId, hwc2_layer_t());
+    MOCK_CONST_METHOD0(getId, hal::HWDisplayId());
     MOCK_CONST_METHOD0(isConnected, bool());
     MOCK_METHOD1(setConnected, void(bool));
-    MOCK_CONST_METHOD0(getCapabilities, const std::unordered_set<HWC2::DisplayCapability>&());
+    MOCK_CONST_METHOD0(getCapabilities, const std::unordered_set<hal::DisplayCapability>&());
 
-    MOCK_METHOD0(acceptChanges, Error());
-    MOCK_METHOD1(createLayer, Error(Layer**));
-    MOCK_METHOD1(destroyLayer, Error(Layer*));
-    MOCK_CONST_METHOD1(getActiveConfig, Error(std::shared_ptr<const Config>*));
-    MOCK_CONST_METHOD1(getActiveConfigIndex, Error(int* outIndex));
-    MOCK_METHOD1(getChangedCompositionTypes, Error(std::unordered_map<Layer*, HWC2::Composition>*));
-    MOCK_CONST_METHOD1(getColorModes, Error(std::vector<android::ui::ColorMode>*));
+    MOCK_METHOD0(acceptChanges, hal::Error());
+    MOCK_METHOD1(createLayer, hal::Error(Layer**));
+    MOCK_METHOD1(destroyLayer, hal::Error(Layer*));
+    MOCK_CONST_METHOD1(getActiveConfig, hal::Error(std::shared_ptr<const Config>*));
+    MOCK_CONST_METHOD1(getActiveConfigIndex, hal::Error(int* outIndex));
+    MOCK_METHOD1(getChangedCompositionTypes,
+                 hal::Error(std::unordered_map<Layer*, hal::Composition>*));
+    MOCK_CONST_METHOD1(getColorModes, hal::Error(std::vector<hal::ColorMode>*));
 
     MOCK_CONST_METHOD0(getSupportedPerFrameMetadata, int32_t());
     MOCK_CONST_METHOD2(getRenderIntents,
-                       Error(android::ui::ColorMode, std::vector<android::ui::RenderIntent>*));
-    MOCK_METHOD2(getDataspaceSaturationMatrix, Error(android::ui::Dataspace, android::mat4*));
+                       hal::Error(hal::ColorMode, std::vector<hal::RenderIntent>*));
+    MOCK_METHOD2(getDataspaceSaturationMatrix, hal::Error(hal::Dataspace, android::mat4*));
     MOCK_CONST_METHOD0(getConfigs, std::vector<std::shared_ptr<const Config>>());
 
-    MOCK_CONST_METHOD1(getName, Error(std::string*));
+    MOCK_CONST_METHOD1(getName, hal::Error(std::string*));
     MOCK_METHOD2(getRequests,
-                 Error(HWC2::DisplayRequest*, std::unordered_map<Layer*, HWC2::LayerRequest>*));
-    MOCK_CONST_METHOD1(getType, Error(HWC2::DisplayType*));
-    MOCK_CONST_METHOD1(supportsDoze, Error(bool*));
-    MOCK_CONST_METHOD1(getHdrCapabilities, Error(android::HdrCapabilities*));
+                 hal::Error(hal::DisplayRequest*, std::unordered_map<Layer*, hal::LayerRequest>*));
+    MOCK_CONST_METHOD1(getType, hal::Error(hal::DisplayType*));
+    MOCK_CONST_METHOD1(supportsDoze, hal::Error(bool*));
+    MOCK_CONST_METHOD1(getHdrCapabilities, hal::Error(android::HdrCapabilities*));
     MOCK_CONST_METHOD3(getDisplayedContentSamplingAttributes,
-                       Error(android::ui::PixelFormat*, android::ui::Dataspace*, uint8_t*));
-    MOCK_CONST_METHOD3(setDisplayContentSamplingEnabled, Error(bool, uint8_t, uint64_t));
+                       hal::Error(hal::PixelFormat*, hal::Dataspace*, uint8_t*));
+    MOCK_CONST_METHOD3(setDisplayContentSamplingEnabled, hal::Error(bool, uint8_t, uint64_t));
     MOCK_CONST_METHOD3(getDisplayedContentSample,
-                       Error(uint64_t, uint64_t, android::DisplayedFrameStats*));
-    MOCK_CONST_METHOD1(getReleaseFences,
-                       Error(std::unordered_map<Layer*, android::sp<android::Fence>>* outFences));
-    MOCK_METHOD1(present, Error(android::sp<android::Fence>*));
-    MOCK_METHOD1(setActiveConfig, Error(const std::shared_ptr<const HWC2::Display::Config>&));
+                       hal::Error(uint64_t, uint64_t, android::DisplayedFrameStats*));
+    MOCK_CONST_METHOD1(
+            getReleaseFences,
+            hal::Error(std::unordered_map<Layer*, android::sp<android::Fence>>* outFences));
+    MOCK_METHOD1(present, hal::Error(android::sp<android::Fence>*));
+    MOCK_METHOD1(setActiveConfig, hal::Error(const std::shared_ptr<const HWC2::Display::Config>&));
     MOCK_METHOD4(setClientTarget,
-                 Error(uint32_t, const android::sp<android::GraphicBuffer>&,
-                       const android::sp<android::Fence>&, android::ui::Dataspace));
-    MOCK_METHOD2(setColorMode, Error(android::ui::ColorMode, android::ui::RenderIntent));
-    MOCK_METHOD2(setColorTransform, Error(const android::mat4&, android_color_transform_t));
+                 hal::Error(uint32_t, const android::sp<android::GraphicBuffer>&,
+                            const android::sp<android::Fence>&, hal::Dataspace));
+    MOCK_METHOD2(setColorMode, hal::Error(hal::ColorMode, hal::RenderIntent));
+    MOCK_METHOD2(setColorTransform, hal::Error(const android::mat4&, hal::ColorTransform));
     MOCK_METHOD2(setOutputBuffer,
-                 Error(const android::sp<android::GraphicBuffer>&,
-                       const android::sp<android::Fence>&));
-    MOCK_METHOD1(setPowerMode, Error(HWC2::PowerMode));
-    MOCK_METHOD1(setVsyncEnabled, Error(HWC2::Vsync));
-    MOCK_METHOD2(validate, Error(uint32_t*, uint32_t*));
+                 hal::Error(const android::sp<android::GraphicBuffer>&,
+                            const android::sp<android::Fence>&));
+    MOCK_METHOD1(setPowerMode, hal::Error(hal::PowerMode));
+    MOCK_METHOD1(setVsyncEnabled, hal::Error(hal::Vsync));
+    MOCK_METHOD2(validate, hal::Error(uint32_t*, uint32_t*));
     MOCK_METHOD4(presentOrValidate,
-                 Error(uint32_t*, uint32_t*, android::sp<android::Fence>*, uint32_t*));
-    MOCK_CONST_METHOD1(setDisplayBrightness, Error(float));
+                 hal::Error(uint32_t*, uint32_t*, android::sp<android::Fence>*, uint32_t*));
+    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_METHOD1(setAutoLowLatencyMode, hal::Error(bool on));
+    MOCK_CONST_METHOD1(getSupportedContentTypes, hal::Error(std::vector<hal::ContentType>*));
+    MOCK_METHOD1(setContentType, hal::Error(hal::ContentType));
+    MOCK_METHOD1(getClientTargetProperty, hal::Error(hal::ClientTargetProperty*));
+    MOCK_CONST_METHOD1(getConnectionType, hal::Error(android::DisplayConnectionType*));
+    MOCK_CONST_METHOD0(isVsyncPeriodSwitchSupported, bool());
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index 7c65f95..e22d0cf 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -29,7 +29,9 @@
     PowerAdvisor();
     ~PowerAdvisor() override;
 
+    MOCK_METHOD0(onBootFinished, void());
     MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
+    MOCK_METHOD0(notifyDisplayUpdateImminent, void());
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
index f6c4f62..1c8c44d 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
@@ -17,6 +17,7 @@
 #include "mock/MockDispSync.h"
 #include <thread>
 
+using namespace std::chrono_literals;
 namespace android {
 namespace mock {
 
@@ -54,8 +55,9 @@
 void DispSync::triggerCallback() {
     if (mCallback.callback == nullptr) return;
 
-    mCallback.callback->onDispSyncEvent(
-            std::chrono::steady_clock::now().time_since_epoch().count());
+    const std::chrono::nanoseconds now = std::chrono::steady_clock::now().time_since_epoch();
+    const auto expectedVSyncTime = now + 16ms;
+    mCallback.callback->onDispSyncEvent(now.count(), expectedVSyncTime.count());
 }
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
index 9ca116d..b39487c 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
@@ -31,15 +31,15 @@
     MOCK_METHOD0(reset, void());
     MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
     MOCK_METHOD0(beginResync, void());
-    MOCK_METHOD2(addResyncSample, bool(nsecs_t, bool*));
+    MOCK_METHOD3(addResyncSample, bool(nsecs_t, std::optional<nsecs_t>, bool*));
     MOCK_METHOD0(endResync, void());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(getPeriod, nsecs_t());
     MOCK_METHOD0(getIntendedPeriod, nsecs_t());
     MOCK_METHOD1(setRefreshSkipCount, void(int));
-    MOCK_CONST_METHOD1(computeNextRefresh, nsecs_t(int));
+    MOCK_CONST_METHOD2(computeNextRefresh, nsecs_t(int, nsecs_t));
     MOCK_METHOD1(setIgnorePresentFences, void(bool));
-    MOCK_METHOD0(expectedPresentTime, nsecs_t());
+    MOCK_METHOD1(expectedPresentTime, nsecs_t(nsecs_t));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
 
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index ed35ebf..054aaf8 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -33,14 +33,16 @@
     MOCK_METHOD0(onScreenReleased, void());
     MOCK_METHOD0(onScreenAcquired, void());
     MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
-    MOCK_METHOD2(onConfigChanged, void(PhysicalDisplayId, int32_t));
+    MOCK_METHOD3(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType, nsecs_t));
     MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset));
     MOCK_METHOD1(registerDisplayEventConnection,
                  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_METHOD1(requestLatestConfig, void(const sp<android::EventThreadConnection> &));
     MOCK_METHOD1(pauseVsyncCallback, void(bool));
+    MOCK_METHOD0(getEventThreadConnectionCount, size_t());
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
similarity index 75%
rename from services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp
rename to services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
index a7fd667..358dfdb 100644
--- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-#include "mock/gui/MockGraphicBufferProducer.h"
+#include "mock/MockFrameTracer.h"
 
 namespace android {
 namespace mock {
 
 // Explicit default instantiation is recommended.
-GraphicBufferProducer::GraphicBufferProducer() = default;
-GraphicBufferProducer::~GraphicBufferProducer() = default;
+FrameTracer::FrameTracer() = default;
+FrameTracer::~FrameTracer() = default;
 
 } // namespace mock
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
new file mode 100644
index 0000000..f768b81
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "FrameTracer/FrameTracer.h"
+
+namespace android {
+namespace mock {
+
+class FrameTracer : public android::FrameTracer {
+public:
+    FrameTracer();
+    ~FrameTracer();
+
+    MOCK_METHOD0(initialize, void());
+    MOCK_METHOD0(registerDataSource, void());
+    MOCK_METHOD2(traceNewLayer, void(int32_t, const std::string&));
+    MOCK_METHOD6(traceTimestamp,
+                 void(int32_t, uint64_t, uint64_t, nsecs_t, FrameEvent::BufferEventType, nsecs_t));
+    MOCK_METHOD6(traceFence,
+                 void(int32_t, uint64_t, uint64_t, const std::shared_ptr<FenceTime>&,
+                      FrameEvent::BufferEventType, nsecs_t));
+    MOCK_METHOD0(miniDump, std::string());
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
new file mode 100644
index 0000000..078d8e07
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Layer.h"
+
+namespace android::mock {
+
+class MockLayer : public Layer {
+public:
+    MockLayer(SurfaceFlinger* flinger, std::string name)
+          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 800, 600, 0, {})) {}
+    explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
+
+    MOCK_CONST_METHOD0(getType, const char*());
+    MOCK_METHOD0(getFrameSelectionPriority, int32_t());
+    MOCK_CONST_METHOD0(isVisible, bool());
+    MOCK_METHOD0(createClone, sp<Layer>());
+    MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
+};
+
+} // namespace android::mock
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 1b1c1a7..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:
@@ -30,13 +29,11 @@
     ~MessageQueue() override;
 
     MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
-    MOCK_METHOD2(setEventThread, void(android::EventThread*, ResyncCallback));
     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/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
index 4129328..7e925b9 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "mock/MockSurfaceInterceptor.h"
 
 namespace android {
@@ -25,3 +29,6 @@
 
 } // namespace mock
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
index 458b2f3..5beee1c 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
@@ -39,7 +39,7 @@
                       const Vector<DisplayState>&, uint32_t));
     MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&));
     MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&));
-    MOCK_METHOD4(saveBufferUpdate, void(const sp<const Layer>&, uint32_t, uint32_t, uint64_t));
+    MOCK_METHOD4(saveBufferUpdate, void(int32_t, uint32_t, uint32_t, uint64_t));
     MOCK_METHOD1(saveDisplayCreation, void(const DisplayDeviceState&));
     MOCK_METHOD1(saveDisplayDeletion, void(int32_t));
     MOCK_METHOD2(savePowerModeUpdate, void(int32_t, int32_t));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index b1634a8..4186e2b 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -28,13 +28,23 @@
     TimeStats();
     ~TimeStats() override;
 
+    MOCK_METHOD0(onBootFinished, void());
     MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
     MOCK_METHOD0(isEnabled, bool());
     MOCK_METHOD0(miniDump, std::string());
     MOCK_METHOD0(incrementTotalFrames, void());
     MOCK_METHOD0(incrementMissedFrames, void());
     MOCK_METHOD0(incrementClientCompositionFrames, void());
+    MOCK_METHOD0(incrementClientCompositionReusedFrames, void());
+    MOCK_METHOD0(incrementRefreshRateSwitches, void());
+    MOCK_METHOD0(incrementCompositionStrategyChanges, void());
+    MOCK_METHOD1(recordDisplayEventConnectionCount, void(int32_t));
+    MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
+    MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
+    MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
     MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
+    MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason));
+    MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId));
     MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
@@ -43,7 +53,8 @@
     MOCK_METHOD3(setPresentFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
     MOCK_METHOD1(onDestroy, void(int32_t));
     MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
-    MOCK_METHOD1(setPowerMode, void(int32_t));
+    MOCK_METHOD1(setPowerMode,
+                 void(hardware::graphics::composer::V2_4::IComposerClient::PowerMode));
     MOCK_METHOD2(recordRefreshRate, void(uint32_t, nsecs_t));
     MOCK_METHOD1(setPresentFenceGlobal, void(const std::shared_ptr<FenceTime>&));
 };
diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.cpp b/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.cpp
deleted file mode 100644
index a17b73f..0000000
--- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "mock/gui/MockGraphicBufferConsumer.h"
-
-namespace android {
-namespace mock {
-
-// Explicit default instantiation is recommended.
-GraphicBufferConsumer::GraphicBufferConsumer() = default;
-GraphicBufferConsumer::~GraphicBufferConsumer() = default;
-
-} // namespace mock
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.h b/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.h
deleted file mode 100644
index 98f24c2..0000000
--- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include <gui/IGraphicBufferConsumer.h>
-
-#include <utils/RefBase.h>
-
-namespace android {
-namespace mock {
-
-class GraphicBufferConsumer : public BnGraphicBufferConsumer, public virtual android::RefBase {
-public:
-    GraphicBufferConsumer();
-    ~GraphicBufferConsumer() override;
-
-    MOCK_METHOD3(acquireBuffer, status_t(BufferItem*, nsecs_t, uint64_t));
-    MOCK_METHOD1(detachBuffer, status_t(int));
-    MOCK_METHOD2(attachBuffer, status_t(int*, const sp<GraphicBuffer>&));
-    MOCK_METHOD5(releaseBuffer, status_t(int, uint64_t, EGLDisplay, EGLSyncKHR, const sp<Fence>&));
-    MOCK_METHOD2(consumerConnect, status_t(const sp<IConsumerListener>&, bool));
-    MOCK_METHOD0(consumerDisconnect, status_t());
-    MOCK_METHOD1(getReleasedBuffers, status_t(uint64_t*));
-    MOCK_METHOD2(setDefaultBufferSize, status_t(uint32_t, uint32_t));
-    MOCK_METHOD1(setMaxBufferCount, status_t(int));
-    MOCK_METHOD1(setMaxAcquiredBufferCount, status_t(int));
-    MOCK_METHOD1(setConsumerName, status_t(const String8&));
-    MOCK_METHOD1(setDefaultBufferFormat, status_t(PixelFormat));
-    MOCK_METHOD1(setDefaultBufferDataSpace, status_t(android_dataspace));
-    MOCK_METHOD1(setConsumerUsageBits, status_t(uint64_t));
-    MOCK_METHOD1(setConsumerIsProtected, status_t(bool));
-    MOCK_METHOD1(setTransformHint, status_t(uint32_t));
-    MOCK_CONST_METHOD1(getSidebandStream, status_t(sp<NativeHandle>*));
-    MOCK_METHOD2(getOccupancyHistory, status_t(bool, std::vector<OccupancyTracker::Segment>*));
-    MOCK_METHOD0(discardFreeBuffers, status_t());
-    MOCK_CONST_METHOD2(dumpState, status_t(const String8&, String8*));
-};
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.h b/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.h
deleted file mode 100644
index c98f39f..0000000
--- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include <gui/IGraphicBufferProducer.h>
-
-#include <utils/RefBase.h>
-
-namespace android {
-namespace mock {
-
-class GraphicBufferProducer : public BnGraphicBufferProducer, public virtual android::RefBase {
-public:
-    GraphicBufferProducer();
-    ~GraphicBufferProducer() override;
-
-    MOCK_METHOD2(requestBuffer, status_t(int, sp<GraphicBuffer>*));
-    MOCK_METHOD1(setMaxDequeuedBufferCount, status_t(int));
-    MOCK_METHOD1(setAsyncMode, status_t(bool));
-    MOCK_METHOD8(dequeueBuffer,
-                 status_t(int*, sp<Fence>*, uint32_t, uint32_t, PixelFormat, uint64_t, uint64_t*,
-                          FrameEventHistoryDelta*));
-    MOCK_METHOD1(detachBuffer, status_t(int));
-    MOCK_METHOD2(detachNextBuffer, status_t(sp<GraphicBuffer>*, sp<Fence>*));
-    MOCK_METHOD2(attachBuffer, status_t(int*, const sp<GraphicBuffer>&));
-    MOCK_METHOD3(queueBuffer, status_t(int, const QueueBufferInput&, QueueBufferOutput*));
-    MOCK_METHOD2(cancelBuffer, status_t(int, const sp<Fence>&));
-    MOCK_METHOD2(query, int(int, int*));
-    MOCK_METHOD4(connect, status_t(const sp<IProducerListener>&, int, bool, QueueBufferOutput*));
-    MOCK_METHOD2(disconnect, status_t(int, DisconnectMode));
-    MOCK_METHOD1(setSidebandStream, status_t(const sp<NativeHandle>&));
-    MOCK_METHOD4(allocateBuffers, void(uint32_t, uint32_t, PixelFormat, uint64_t));
-    MOCK_METHOD1(allowAllocation, status_t(bool));
-    MOCK_METHOD1(setGenerationNumber, status_t(uint32_t));
-    MOCK_CONST_METHOD0(getConsumerName, String8());
-    MOCK_METHOD1(setSharedBufferMode, status_t(bool));
-    MOCK_METHOD1(setAutoRefresh, status_t(bool));
-    MOCK_METHOD1(setDequeueTimeout, status_t(nsecs_t));
-    MOCK_METHOD3(getLastQueuedBuffer, status_t(sp<GraphicBuffer>*, sp<Fence>*, float[16]));
-    MOCK_METHOD1(getFrameTimestamps, void(FrameEventHistoryDelta*));
-    MOCK_CONST_METHOD1(getUniqueId, status_t(uint64_t*));
-    MOCK_CONST_METHOD1(getConsumerUsage, status_t(uint64_t*));
-};
-
-} // namespace mock
-} // namespace android
diff --git a/services/surfaceflinger/tests/utils/CallbackUtils.h b/services/surfaceflinger/tests/utils/CallbackUtils.h
new file mode 100644
index 0000000..1318deb
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/CallbackUtils.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+#include <ui/Fence.h>
+#include <utils/Timers.h>
+#include <thread>
+
+namespace android {
+
+namespace {
+
+struct CallbackData {
+    CallbackData() = default;
+    CallbackData(nsecs_t time, const sp<Fence>& fence,
+                 const std::vector<SurfaceControlStats>& stats)
+          : latchTime(time), presentFence(fence), surfaceControlStats(stats) {}
+
+    nsecs_t latchTime;
+    sp<Fence> presentFence;
+    std::vector<SurfaceControlStats> surfaceControlStats;
+};
+
+class ExpectedResult {
+public:
+    enum Transaction {
+        NOT_PRESENTED = 0,
+        PRESENTED,
+    };
+
+    enum Buffer {
+        NOT_ACQUIRED = 0,
+        ACQUIRED,
+    };
+
+    enum PreviousBuffer {
+        NOT_RELEASED = 0,
+        RELEASED,
+        UNKNOWN,
+    };
+
+    void reset() {
+        mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+        mExpectedSurfaceResults.clear();
+    }
+
+    void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer,
+                    ExpectedResult::Buffer bufferResult = ACQUIRED,
+                    ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
+        mTransactionResult = transactionResult;
+        mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer),
+                                        std::forward_as_tuple(bufferResult, previousBufferResult));
+    }
+
+    void addSurfaces(ExpectedResult::Transaction transactionResult,
+                     const std::vector<sp<SurfaceControl>>& layers,
+                     ExpectedResult::Buffer bufferResult = ACQUIRED,
+                     ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) {
+        for (const auto& layer : layers) {
+            addSurface(transactionResult, layer, bufferResult, previousBufferResult);
+        }
+    }
+
+    void addExpectedPresentTime(nsecs_t expectedPresentTime) {
+        mExpectedPresentTime = expectedPresentTime;
+    }
+
+    void verifyCallbackData(const CallbackData& callbackData) const {
+        const auto& [latchTime, presentFence, surfaceControlStats] = callbackData;
+        if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) {
+            ASSERT_GE(latchTime, 0) << "bad latch time";
+            ASSERT_NE(presentFence, nullptr);
+            if (mExpectedPresentTime >= 0) {
+                ASSERT_EQ(presentFence->wait(3000), NO_ERROR);
+                ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6));
+                // if the panel is running at 30 hz, at the worst case, our expected time just
+                // misses vsync and we have to wait another 33.3ms
+                ASSERT_LE(presentFence->getSignalTime(),
+                          mExpectedPresentTime + nsecs_t(66.666666 * 1e6));
+            }
+        } else {
+            ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented";
+            ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched";
+        }
+
+        ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size())
+                << "wrong number of surfaces";
+
+        for (const auto& stats : surfaceControlStats) {
+            ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control";
+
+            const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl);
+            ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end())
+                    << "unexpected surface control";
+            expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime);
+        }
+    }
+
+private:
+    class ExpectedSurfaceResult {
+    public:
+        ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult,
+                              ExpectedResult::PreviousBuffer previousBufferResult)
+              : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {}
+
+        void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats,
+                                       nsecs_t latchTime) const {
+            const auto&
+                    [surfaceControl, latch, acquireTime, presentFence, previousReleaseFence,
+                     transformHint,
+                     frameEvents] = surfaceControlStats;
+
+            ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED)
+                    << "bad acquire time";
+            ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time";
+
+            if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) {
+                ASSERT_NE(previousReleaseFence, nullptr)
+                        << "failed to set release prev buffer fence";
+            } else if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::NOT_RELEASED) {
+                ASSERT_EQ(previousReleaseFence, nullptr)
+                        << "should not have set released prev buffer fence";
+            }
+        }
+
+    private:
+        ExpectedResult::Buffer mBufferResult;
+        ExpectedResult::PreviousBuffer mPreviousBufferResult;
+    };
+
+    struct SCHash {
+        std::size_t operator()(const sp<SurfaceControl>& sc) const {
+            return std::hash<IBinder*>{}(sc->getHandle().get());
+        }
+    };
+    ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED;
+    nsecs_t mExpectedPresentTime = -1;
+    std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults;
+};
+
+class CallbackHelper {
+public:
+    static void function(void* callbackContext, nsecs_t latchTime, const sp<Fence>& presentFence,
+                         const std::vector<SurfaceControlStats>& stats) {
+        if (!callbackContext) {
+            ALOGE("failed to get callback context");
+        }
+        CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext);
+        std::lock_guard lock(helper->mMutex);
+        helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats);
+        helper->mConditionVariable.notify_all();
+    }
+
+    void getCallbackData(CallbackData* outData) {
+        std::unique_lock lock(mMutex);
+
+        if (mCallbackDataQueue.empty()) {
+            ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)),
+                      std::cv_status::timeout)
+                    << "did not receive callback";
+        }
+
+        *outData = std::move(mCallbackDataQueue.front());
+        mCallbackDataQueue.pop();
+    }
+
+    void verifyFinalState() {
+        // Wait to see if there are extra callbacks
+        std::this_thread::sleep_for(500ms);
+
+        std::lock_guard lock(mMutex);
+        EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received";
+        mCallbackDataQueue = {};
+    }
+
+    void* getContext() { return static_cast<void*>(this); }
+
+    std::mutex mMutex;
+    std::condition_variable mConditionVariable;
+    std::queue<CallbackData> mCallbackDataQueue;
+};
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/utils/ColorUtils.h b/services/surfaceflinger/tests/utils/ColorUtils.h
new file mode 100644
index 0000000..07916b6
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/ColorUtils.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <ui/ColorSpace.h>
+
+namespace android {
+
+namespace {
+
+struct Color {
+    uint8_t r;
+    uint8_t g;
+    uint8_t b;
+    uint8_t a;
+
+    static const Color RED;
+    static const Color GREEN;
+    static const Color BLUE;
+    static const Color WHITE;
+    static const Color BLACK;
+    static const Color TRANSPARENT;
+};
+
+const Color Color::RED{255, 0, 0, 255};
+const Color Color::GREEN{0, 255, 0, 255};
+const Color Color::BLUE{0, 0, 255, 255};
+const Color Color::WHITE{255, 255, 255, 255};
+const Color Color::BLACK{0, 0, 0, 255};
+const Color Color::TRANSPARENT{0, 0, 0, 0};
+
+class ColorTransformHelper {
+public:
+    static void DegammaColorSingle(half& s) {
+        if (s <= 0.03928f)
+            s = s / 12.92f;
+        else
+            s = pow((s + 0.055f) / 1.055f, 2.4f);
+    }
+
+    static void DegammaColor(half3& color) {
+        DegammaColorSingle(color.r);
+        DegammaColorSingle(color.g);
+        DegammaColorSingle(color.b);
+    }
+
+    static void GammaColorSingle(half& s) {
+        if (s <= 0.0031308f) {
+            s = s * 12.92f;
+        } else {
+            s = 1.055f * pow(s, (1.0f / 2.4f)) - 0.055f;
+        }
+    }
+
+    static void GammaColor(half3& color) {
+        GammaColorSingle(color.r);
+        GammaColorSingle(color.g);
+        GammaColorSingle(color.b);
+    }
+
+    static void applyMatrix(half3& color, const mat3& mat) {
+        half3 ret = half3(0);
+
+        for (int i = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++) {
+                ret[i] = ret[i] + color[j] * mat[j][i];
+            }
+        }
+        color = ret;
+    }
+};
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
new file mode 100644
index 0000000..5480b00
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <ui/Rect.h>
+#include <utils/String8.h>
+#include "TransactionUtils.h"
+
+namespace android {
+
+namespace {
+
+// A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check
+// individual pixel values for testing purposes.
+class ScreenCapture : public RefBase {
+public:
+    static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
+        captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
+    }
+
+    static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
+        const auto sf = ComposerService::getComposerService();
+        SurfaceComposerClient::Transaction().apply(true);
+
+        sp<GraphicBuffer> outBuffer;
+        ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false));
+        *sc = std::make_unique<ScreenCapture>(outBuffer);
+    }
+
+    static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
+                              Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
+        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+        SurfaceComposerClient::Transaction().apply(true);
+
+        sp<GraphicBuffer> outBuffer;
+        ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale));
+        *sc = std::make_unique<ScreenCapture>(outBuffer);
+    }
+
+    static void captureChildLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
+                                   Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
+        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+        SurfaceComposerClient::Transaction().apply(true);
+
+        sp<GraphicBuffer> outBuffer;
+        ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale, true));
+        *sc = std::make_unique<ScreenCapture>(outBuffer);
+    }
+
+    static void captureChildLayersExcluding(
+            std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
+            std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) {
+        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+        SurfaceComposerClient::Transaction().apply(true);
+
+        sp<GraphicBuffer> outBuffer;
+        ASSERT_EQ(NO_ERROR,
+                  sf->captureLayers(parentHandle, &outBuffer, ui::Dataspace::V0_SRGB,
+                                    ui::PixelFormat::RGBA_8888, Rect::EMPTY_RECT, excludeLayers,
+                                    1.0f, true));
+        *sc = std::make_unique<ScreenCapture>(outBuffer);
+    }
+
+    void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
+        ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
+        TransactionUtils::expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
+    }
+
+    void expectBorder(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
+        ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
+        const bool leftBorder = rect.left > 0;
+        const bool topBorder = rect.top > 0;
+        const bool rightBorder = rect.right < int32_t(mOutBuffer->getWidth());
+        const bool bottomBorder = rect.bottom < int32_t(mOutBuffer->getHeight());
+
+        if (topBorder) {
+            Rect top(rect.left, rect.top - 1, rect.right, rect.top);
+            if (leftBorder) {
+                top.left -= 1;
+            }
+            if (rightBorder) {
+                top.right += 1;
+            }
+            expectColor(top, color, tolerance);
+        }
+        if (leftBorder) {
+            Rect left(rect.left - 1, rect.top, rect.left, rect.bottom);
+            expectColor(left, color, tolerance);
+        }
+        if (rightBorder) {
+            Rect right(rect.right, rect.top, rect.right + 1, rect.bottom);
+            expectColor(right, color, tolerance);
+        }
+        if (bottomBorder) {
+            Rect bottom(rect.left, rect.bottom, rect.right, rect.bottom + 1);
+            if (leftBorder) {
+                bottom.left -= 1;
+            }
+            if (rightBorder) {
+                bottom.right += 1;
+            }
+            expectColor(bottom, color, tolerance);
+        }
+    }
+
+    void expectQuadrant(const Rect& rect, const Color& topLeft, const Color& topRight,
+                        const Color& bottomLeft, const Color& bottomRight, bool filtered = false,
+                        uint8_t tolerance = 0) {
+        ASSERT_TRUE((rect.right - rect.left) % 2 == 0 && (rect.bottom - rect.top) % 2 == 0);
+
+        const int32_t centerX = rect.left + (rect.right - rect.left) / 2;
+        const int32_t centerY = rect.top + (rect.bottom - rect.top) / 2;
+        // avoid checking borders due to unspecified filtering behavior
+        const int32_t offsetX = filtered ? 2 : 0;
+        const int32_t offsetY = filtered ? 2 : 0;
+        expectColor(Rect(rect.left, rect.top, centerX - offsetX, centerY - offsetY), topLeft,
+                    tolerance);
+        expectColor(Rect(centerX + offsetX, rect.top, rect.right, centerY - offsetY), topRight,
+                    tolerance);
+        expectColor(Rect(rect.left, centerY + offsetY, centerX - offsetX, rect.bottom), bottomLeft,
+                    tolerance);
+        expectColor(Rect(centerX + offsetX, centerY + offsetY, rect.right, rect.bottom),
+                    bottomRight, tolerance);
+    }
+
+    void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) {
+        ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
+        const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x));
+        if (r != pixel[0] || g != pixel[1] || b != pixel[2]) {
+            String8 err(String8::format("pixel @ (%3d, %3d): "
+                                        "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]",
+                                        x, y, r, g, b, pixel[0], pixel[1], pixel[2]));
+            EXPECT_EQ(String8(), err) << err.string();
+        }
+    }
+
+    void expectFGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 195, 63, 63); }
+
+    void expectBGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 63, 63, 195); }
+
+    void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); }
+
+    explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) {
+        mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
+    }
+
+    ~ScreenCapture() { mOutBuffer->unlock(); }
+
+private:
+    sp<GraphicBuffer> mOutBuffer;
+    uint8_t* mPixels = nullptr;
+};
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
new file mode 100644
index 0000000..8e1f943
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+
+#include <android/native_window.h>
+#include <binder/IPCThreadState.h>
+#include <gtest/gtest.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+
+#include "ColorUtils.h"
+
+namespace android {
+
+namespace {
+
+using namespace std::chrono_literals;
+using Transaction = SurfaceComposerClient::Transaction;
+
+std::ostream& operator<<(std::ostream& os, const Color& color) {
+    os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a);
+    return os;
+}
+
+class TransactionUtils {
+public:
+    // Fill a region with the specified color.
+    static void fillANativeWindowBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect,
+                                             const Color& color) {
+        Rect r(0, 0, buffer.width, buffer.height);
+        if (!r.intersect(rect, &r)) {
+            return;
+        }
+
+        int32_t width = r.right - r.left;
+        int32_t height = r.bottom - r.top;
+
+        for (int32_t row = 0; row < height; row++) {
+            uint8_t* dst = static_cast<uint8_t*>(buffer.bits) +
+                    (buffer.stride * (r.top + row) + r.left) * 4;
+            for (int32_t column = 0; column < width; column++) {
+                dst[0] = color.r;
+                dst[1] = color.g;
+                dst[2] = color.b;
+                dst[3] = color.a;
+                dst += 4;
+            }
+        }
+    }
+
+    // Fill a region with the specified color.
+    static void fillGraphicBufferColor(const sp<GraphicBuffer>& buffer, const Rect& rect,
+                                       const Color& color) {
+        Rect r(0, 0, buffer->width, buffer->height);
+        if (!r.intersect(rect, &r)) {
+            return;
+        }
+
+        int32_t width = r.right - r.left;
+        int32_t height = r.bottom - r.top;
+
+        uint8_t* pixels;
+        buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                     reinterpret_cast<void**>(&pixels));
+
+        for (int32_t row = 0; row < height; row++) {
+            uint8_t* dst = pixels + (buffer->getStride() * (r.top + row) + r.left) * 4;
+            for (int32_t column = 0; column < width; column++) {
+                dst[0] = color.r;
+                dst[1] = color.g;
+                dst[2] = color.b;
+                dst[3] = color.a;
+                dst += 4;
+            }
+        }
+        buffer->unlock();
+    }
+
+    // Check if a region has the specified color.
+    static void expectBufferColor(const sp<GraphicBuffer>& outBuffer, uint8_t* pixels,
+                                  const Rect& rect, const Color& color, uint8_t tolerance) {
+        int32_t x = rect.left;
+        int32_t y = rect.top;
+        int32_t width = rect.right - rect.left;
+        int32_t height = rect.bottom - rect.top;
+
+        int32_t bufferWidth = int32_t(outBuffer->getWidth());
+        int32_t bufferHeight = int32_t(outBuffer->getHeight());
+        if (x + width > bufferWidth) {
+            x = std::min(x, bufferWidth);
+            width = bufferWidth - x;
+        }
+        if (y + height > bufferHeight) {
+            y = std::min(y, bufferHeight);
+            height = bufferHeight - y;
+        }
+
+        auto colorCompare = [tolerance](uint8_t a, uint8_t b) {
+            uint8_t tmp = a >= b ? a - b : b - a;
+            return tmp <= tolerance;
+        };
+        for (int32_t j = 0; j < height; j++) {
+            const uint8_t* src = pixels + (outBuffer->getStride() * (y + j) + x) * 4;
+            for (int32_t i = 0; i < width; i++) {
+                const uint8_t expected[4] = {color.r, color.g, color.b, color.a};
+                EXPECT_TRUE(std::equal(src, src + 4, expected, colorCompare))
+                        << "pixel @ (" << x + i << ", " << y + j << "): "
+                        << "expected (" << color << "), "
+                        << "got (" << Color{src[0], src[1], src[2], src[3]} << ")";
+                src += 4;
+            }
+        }
+    }
+
+    static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const Color& color,
+                                 bool unlock = true) {
+        fillSurfaceRGBA8(sc, color.r, color.g, color.b, unlock);
+    }
+
+    // Fill an RGBA_8888 formatted surface with a single color.
+    static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b,
+                                 bool unlock = true) {
+        ANativeWindow_Buffer outBuffer;
+        sp<Surface> s = sc->getSurface();
+        ASSERT_TRUE(s != nullptr);
+        ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
+        uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+        for (int y = 0; y < outBuffer.height; y++) {
+            for (int x = 0; x < outBuffer.width; x++) {
+                uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
+                pixel[0] = r;
+                pixel[1] = g;
+                pixel[2] = b;
+                pixel[3] = 255;
+            }
+        }
+        if (unlock) {
+            ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+        }
+    }
+};
+
+enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY };
+
+// Environment for starting up binder threads. This is required for testing
+// virtual displays, as BufferQueue parameters may be queried over binder.
+class BinderEnvironment : public ::testing::Environment {
+public:
+    void SetUp() override { ProcessState::self()->startThreadPool(); }
+};
+
+/** RAII Wrapper around get/seteuid */
+class UIDFaker {
+    uid_t oldId;
+
+public:
+    UIDFaker(uid_t uid) {
+        oldId = geteuid();
+        seteuid(uid);
+    }
+    ~UIDFaker() { seteuid(oldId); }
+};
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/vsync/vsync.cpp b/services/surfaceflinger/tests/vsync/vsync.cpp
index a1b45e6..667dfb9 100644
--- a/services/surfaceflinger/tests/vsync/vsync.cpp
+++ b/services/surfaceflinger/tests/vsync/vsync.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <android/looper.h>
 #include <gui/DisplayEventReceiver.h>
 #include <utils/Looper.h>
@@ -82,3 +86,6 @@
 
     return 0;
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index a7fd912..b71964b 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -350,7 +350,7 @@
     while (!buffer_state_->compare_exchange_weak(
         current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
         std::memory_order_acquire)) {
-      ALOGI(
+      ALOGV(
           "%s: Failed to post to the new consumer. "
           "Current buffer state was changed to %" PRIx32
           " when trying to acquire the buffer and modify the buffer state to "
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index c202b5c..4df7b7c 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -12,10 +12,14 @@
     ],
 
     shared_libs: [
-        "android.frameworks.vr.composer@1.0",
+        "android.frameworks.vr.composer@2.0",
         "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.3",
+        "android.hardware.graphics.composer@2.1-resources",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@3.0",
+        "android.hardware.graphics.mapper@4.0",
         "libbase",
         "libbufferhubqueue",
         "libbinder",
@@ -32,11 +36,11 @@
 
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
-        "android.hardware.graphics.composer@2.1-hal",
+        "android.hardware.graphics.composer@2.3-hal",
     ],
 
     export_header_lib_headers: [
-        "android.hardware.graphics.composer@2.1-hal",
+        "android.hardware.graphics.composer@2.3-hal",
     ],
 
     export_static_lib_headers: [
@@ -44,8 +48,10 @@
     ],
 
     export_shared_lib_headers: [
-        "android.frameworks.vr.composer@1.0",
+        "android.frameworks.vr.composer@2.0",
         "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.3",
     ],
 
     export_include_dirs: ["."],
@@ -102,8 +108,8 @@
         "libvr_hwc-binder",
     ],
     shared_libs: [
-        "android.frameworks.vr.composer@1.0",
-        "android.hardware.graphics.composer@2.1",
+        "android.frameworks.vr.composer@2.0",
+        "android.hardware.graphics.composer@2.3",
         "libbase",
         "libbinder",
         "liblog",
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp
index 786d5fa..dd1603d 100644
--- a/services/vr/hardware_composer/impl/vr_composer_client.cpp
+++ b/services/vr/hardware_composer/impl/vr_composer_client.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
+#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
 #include <hardware/gralloc.h>
 #include <hardware/gralloc1.h>
 #include <log/log.h>
@@ -27,8 +27,7 @@
 namespace android {
 namespace dvr {
 
-using android::hardware::graphics::common::V1_0::PixelFormat;
-using android::frameworks::vr::composer::V1_0::IVrComposerClient;
+using android::frameworks::vr::composer::V2_0::IVrComposerClient;
 
 VrComposerClient::VrComposerClient(dvr::VrHwc& hal)
     : ComposerClient(&hal), mVrHal(hal) {
@@ -51,7 +50,8 @@
 VrComposerClient::VrCommandEngine::~VrCommandEngine() {}
 
 bool VrComposerClient::VrCommandEngine::executeCommand(
-    IComposerClient::Command command, uint16_t length) {
+    hardware::graphics::composer::V2_1::IComposerClient::Command command,
+    uint16_t length) {
   IVrComposerClient::VrCommand vrCommand =
       static_cast<IVrComposerClient::VrCommand>(command);
   switch (vrCommand) {
@@ -73,7 +73,7 @@
 
   auto err = mVrHal.setLayerInfo(mCurrentDisplay, mCurrentLayer, read(), read());
   if (err != Error::NONE) {
-    mWriter.setError(getCommandLoc(), err);
+    mWriter->setError(getCommandLoc(), err);
   }
 
   return true;
@@ -86,7 +86,7 @@
 
   auto err = mVrHal.setClientTargetMetadata(mCurrentDisplay, readBufferMetadata());
   if (err != Error::NONE)
-    mWriter.setError(getCommandLoc(), err);
+    mWriter->setError(getCommandLoc(), err);
 
   return true;
 }
@@ -99,7 +99,7 @@
   auto err = mVrHal.setLayerBufferMetadata(mCurrentDisplay, mCurrentLayer,
                                            readBufferMetadata());
   if (err != Error::NONE)
-    mWriter.setError(getCommandLoc(), err);
+    mWriter->setError(getCommandLoc(), err);
 
   return true;
 }
@@ -107,12 +107,14 @@
 IVrComposerClient::BufferMetadata
 VrComposerClient::VrCommandEngine::readBufferMetadata() {
   IVrComposerClient::BufferMetadata metadata = {
-    .width = read(),
-    .height = read(),
-    .stride = read(),
-    .layerCount = read(),
-    .format = static_cast<PixelFormat>(readSigned()),
-    .usage = read64(),
+      .width = read(),
+      .height = read(),
+      .stride = read(),
+      .layerCount = read(),
+      .format =
+          static_cast<android::hardware::graphics::common::V1_2::PixelFormat>(
+              readSigned()),
+      .usage = read64(),
   };
   return metadata;
 }
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h
index 0b7ce5e..1b2b5f4 100644
--- a/services/vr/hardware_composer/impl/vr_composer_client.h
+++ b/services/vr/hardware_composer/impl/vr_composer_client.h
@@ -17,10 +17,12 @@
 #ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
 #define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
 
-#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
-#include <composer-command-buffer/2.1/ComposerCommandBuffer.h>
+#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
+#include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
 #include <composer-hal/2.1/ComposerClient.h>
 #include <composer-hal/2.1/ComposerCommandEngine.h>
+#include <composer-hal/2.2/ComposerClient.h>
+#include <composer-hal/2.3/ComposerClient.h>
 
 namespace android {
 namespace dvr {
@@ -28,8 +30,8 @@
 class VrHwc;
 
 using hardware::graphics::composer::V2_1::hal::ComposerCommandEngine;
-using hardware::graphics::composer::V2_1::hal::ComposerHal;
-using hardware::graphics::composer::V2_1::hal::detail::ComposerClientImpl;
+using hardware::graphics::composer::V2_3::hal::ComposerHal;
+using hardware::graphics::composer::V2_3::hal::detail::ComposerClientImpl;
 
 using ComposerClient = ComposerClientImpl<IVrComposerClient, ComposerHal>;
 
@@ -44,8 +46,9 @@
     explicit VrCommandEngine(VrComposerClient& client);
     ~VrCommandEngine() override;
 
-    bool executeCommand(IComposerClient::Command command,
-                        uint16_t length) override;
+    bool executeCommand(
+        hardware::graphics::composer::V2_1::IComposerClient::Command command,
+        uint16_t length) override;
 
    private:
     bool executeSetLayerInfo(uint16_t length);
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index fb7932d..e530b16 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -27,7 +27,7 @@
 #include "vr_composer_client.h"
 
 using namespace android::hardware::graphics::common::V1_0;
-using namespace android::hardware::graphics::composer::V2_1;
+using namespace android::hardware::graphics::composer::V2_3;
 
 using android::base::StringPrintf;
 using android::hardware::hidl_handle;
@@ -36,12 +36,12 @@
 using android::hardware::Return;
 using android::hardware::Void;
 
+namespace types = android::hardware::graphics::common;
+
 namespace android {
 namespace dvr {
 namespace {
 
-using android::hardware::graphics::common::V1_0::PixelFormat;
-
 const Display kDefaultDisplayId = 1;
 const Config kDefaultConfigId = 1;
 
@@ -269,7 +269,8 @@
   // onHotplug() call, so it's important to release mutex_ here.
   lock.unlock();
   event_callback_->onHotplug(kDefaultDisplayId,
-                             IComposerCallback::Connection::CONNECTED);
+                             hardware::graphics::composer::V2_1::
+                                 IComposerCallback::Connection::CONNECTED);
   lock.lock();
   UpdateVsyncCallbackEnabledLocked();
 }
@@ -282,15 +283,6 @@
 
 uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; }
 
-Error VrHwc::createVirtualDisplay(uint32_t width, uint32_t height,
-                                  PixelFormat* format, Display* outDisplay) {
-  *format = PixelFormat::RGBA_8888;
-  *outDisplay = display_count_;
-  displays_[display_count_].reset(new HwcDisplay(width, height));
-  display_count_++;
-  return Error::NONE;
-}
-
 Error VrHwc::destroyVirtualDisplay(Display display) {
   std::lock_guard<std::mutex> guard(mutex_);
   if (display == kDefaultDisplayId || displays_.erase(display) == 0)
@@ -332,24 +324,6 @@
   return Error::NONE;
 }
 
-Error VrHwc::getClientTargetSupport(Display display, uint32_t /* width */,
-                                    uint32_t /* height */,
-                                    PixelFormat /* format */,
-                                    Dataspace /* dataspace */) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  if (!FindDisplay(display))
-    return Error::BAD_DISPLAY;
-
-  return Error::NONE;
-}
-
-Error VrHwc::getColorModes(Display /* display */,
-                           hidl_vec<ColorMode>* outModes) {
-  std::vector<ColorMode> color_modes(1, ColorMode::NATIVE);
-  *outModes = hidl_vec<ColorMode>(color_modes);
-  return Error::NONE;
-}
-
 Error VrHwc::getDisplayAttribute(Display display, Config config,
                                  IComposerClient::Attribute attribute,
                                  int32_t* outValue) {
@@ -441,17 +415,6 @@
   return Error::NONE;
 }
 
-Error VrHwc::getHdrCapabilities(Display /* display */,
-                                hidl_vec<Hdr>* /* outTypes */,
-                                float* outMaxLuminance,
-                                float* outMaxAverageLuminance,
-                                float* outMinLuminance) {
-  *outMaxLuminance = 0;
-  *outMaxAverageLuminance = 0;
-  *outMinLuminance = 0;
-  return Error::NONE;
-}
-
 Error VrHwc::setActiveConfig(Display display, Config config) {
   std::lock_guard<std::mutex> guard(mutex_);
   auto display_ptr = FindDisplay(display);
@@ -464,47 +427,6 @@
   return Error::NONE;
 }
 
-Error VrHwc::setColorMode(Display display, ColorMode mode) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  if (mode < ColorMode::NATIVE || mode > ColorMode::DISPLAY_P3)
-    return Error::BAD_PARAMETER;
-
-  display_ptr->set_color_mode(mode);
-  return Error::NONE;
-}
-
-Error VrHwc::setPowerMode(Display display, IComposerClient::PowerMode mode) {
-  bool dozeSupported = false;
-
-  Error dozeSupportError = getDozeSupport(display, &dozeSupported);
-
-  if (dozeSupportError != Error::NONE)
-    return dozeSupportError;
-
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  if (!display_ptr)
-    return Error::BAD_DISPLAY;
-
-  if (mode < IComposerClient::PowerMode::OFF ||
-      mode > IComposerClient::PowerMode::DOZE_SUSPEND) {
-    return Error::BAD_PARAMETER;
-  }
-
-  if (!dozeSupported &&
-      (mode == IComposerClient::PowerMode::DOZE ||
-       mode == IComposerClient::PowerMode::DOZE_SUSPEND)) {
-    return Error::UNSUPPORTED;
-  }
-
-  display_ptr->set_power_mode(mode);
-  return Error::NONE;
-}
-
 Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
   std::lock_guard<std::mutex> guard(mutex_);
   auto display_ptr = FindDisplay(display);
@@ -956,6 +878,23 @@
   return Void();
 }
 
+Return<void> VrHwc::createClient_2_3(IComposer::createClient_2_3_cb hidl_cb) {
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  Error status = Error::NONE;
+  sp<VrComposerClient> client;
+  if (!client_.promote().get()) {
+    client = new VrComposerClient(*this);
+  } else {
+    ALOGE("Already have a client");
+    status = Error::NO_RESOURCES;
+  }
+
+  client_ = client;
+  hidl_cb(status, client);
+  return Void();
+}
+
 void VrHwc::ForceDisplaysRefresh() {
   std::lock_guard<std::mutex> guard(mutex_);
   if (event_callback_ != nullptr) {
@@ -994,6 +933,26 @@
   vsync_callback_->SetEventCallback(send_vsync ? event_callback_ : nullptr);
 }
 
+Return<void> VrHwc::debug(const hidl_handle& fd,
+                          const hidl_vec<hidl_string>& args) {
+  std::string result;
+
+  {
+    std::lock_guard<std::mutex> guard(mutex_);
+    for (const auto& pair : displays_) {
+      result += StringPrintf("Display id: %d\n", static_cast<int>(pair.first));
+      pair.second->dumpDebugInfo(&result);
+    }
+    result += "\n";
+  }
+
+  FILE* out = fdopen(dup(fd->data[0]), "w");
+  fprintf(out, "%s", result.c_str());
+  fclose(out);
+
+  return Void();
+}
+
 void HwcLayer::dumpDebugInfo(std::string* result) const {
   if (!result) {
     return;
@@ -1024,5 +983,196 @@
   callback_ = callback;
 }
 
+// composer::V2_2::ComposerHal
+Error VrHwc::setReadbackBuffer(Display display,
+                               const native_handle_t* bufferHandle,
+                               android::base::unique_fd fenceFd) {
+  return Error::NONE;
+}
+
+Error VrHwc::getReadbackBufferFence(Display display,
+                                    android::base::unique_fd* outFenceFd) {
+  return Error::NONE;
+}
+
+Error VrHwc::createVirtualDisplay_2_2(uint32_t width, uint32_t height,
+                                      types::V1_1::PixelFormat* format,
+                                      Display* outDisplay) {
+  *format = types::V1_1::PixelFormat::RGBA_8888;
+  *outDisplay = display_count_;
+  displays_[display_count_].reset(new HwcDisplay(width, height));
+  display_count_++;
+  return Error::NONE;
+}
+
+Error VrHwc::setPowerMode_2_2(Display display,
+                              IComposerClient::PowerMode mode) {
+  bool dozeSupported = false;
+
+  Error dozeSupportError = getDozeSupport(display, &dozeSupported);
+
+  if (dozeSupportError != Error::NONE)
+    return dozeSupportError;
+
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  if (mode < IComposerClient::PowerMode::OFF ||
+      mode > IComposerClient::PowerMode::DOZE_SUSPEND) {
+    return Error::BAD_PARAMETER;
+  }
+
+  if (!dozeSupported && (mode == IComposerClient::PowerMode::DOZE ||
+                         mode == IComposerClient::PowerMode::DOZE_SUSPEND)) {
+    return Error::UNSUPPORTED;
+  }
+
+  display_ptr->set_power_mode(mode);
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerFloatColor(Display display, Layer layer,
+                                IComposerClient::FloatColor color) {
+  return Error::NONE;
+}
+
+Error VrHwc::getRenderIntents(Display display, types::V1_1::ColorMode mode,
+                              std::vector<RenderIntent>* outIntents) {
+  return Error::NONE;
+}
+
+std::array<float, 16> VrHwc::getDataspaceSaturationMatrix(
+    types::V1_1::Dataspace dataspace) {
+  return {};
+}
+
+// composer::V2_3::ComposerHal
+Error VrHwc::getHdrCapabilities_2_3(Display /*display*/,
+                                    hidl_vec<Hdr>* /*outTypes*/,
+                                    float* outMaxLuminance,
+                                    float* outMaxAverageLuminance,
+                                    float* outMinLuminance) {
+  *outMaxLuminance = 0;
+  *outMaxAverageLuminance = 0;
+  *outMinLuminance = 0;
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerPerFrameMetadata_2_3(
+    Display display, Layer layer,
+    const std::vector<IComposerClient::PerFrameMetadata>& metadata) {
+  return Error::NONE;
+}
+
+Error VrHwc::getPerFrameMetadataKeys_2_3(
+    Display display,
+    std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) {
+  return Error::NONE;
+}
+
+Error VrHwc::setColorMode_2_3(Display display, ColorMode mode,
+                              RenderIntent intent) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  if (!display_ptr)
+    return Error::BAD_DISPLAY;
+
+  if (mode < ColorMode::NATIVE || mode > ColorMode::DISPLAY_P3)
+    return Error::BAD_PARAMETER;
+
+  display_ptr->set_color_mode(mode);
+  return Error::NONE;
+}
+
+Error VrHwc::getRenderIntents_2_3(Display display, ColorMode mode,
+                                  std::vector<RenderIntent>* outIntents) {
+  return Error::NONE;
+}
+
+Error VrHwc::getColorModes_2_3(Display display, hidl_vec<ColorMode>* outModes) {
+  return Error::NONE;
+}
+
+Error VrHwc::getClientTargetSupport_2_3(Display display, uint32_t width,
+                                        uint32_t height, PixelFormat format,
+                                        Dataspace dataspace) {
+  return Error::NONE;
+}
+
+Error VrHwc::getReadbackBufferAttributes_2_3(Display display,
+                                             PixelFormat* outFormat,
+                                             Dataspace* outDataspace) {
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                          std::vector<uint8_t>* outData) {
+  int error = 0;
+  auto display_client = display::DisplayClient::Create(&error);
+  if (!display_client) {
+    ALOGE("Could not connect to display service : %s(%d)", strerror(error),
+          error);
+    return Error::BAD_CONFIG;
+  }
+  auto edid_data = display_client->GetConfigurationData(
+      display::ConfigFileType::kDeviceEdid);
+  auto display_identification_port =
+      display_client->GetDisplayIdentificationPort();
+  *outPort = display_identification_port.get();
+
+  std::copy(edid_data.get().begin(), edid_data.get().end(),
+            std::back_inserter(*outData));
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerColorTransform(Display display, Layer layer,
+                                    const float* matrix) {
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayedContentSamplingAttributes(
+    Display display, PixelFormat& format, Dataspace& dataspace,
+    hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) {
+  return Error::NONE;
+}
+
+Error VrHwc::setDisplayedContentSamplingEnabled(
+    Display display, IComposerClient::DisplayedContentSampling enable,
+    hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
+    uint64_t maxFrames) {
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayedContentSample(Display display, uint64_t maxFrames,
+                                       uint64_t timestamp, uint64_t& frameCount,
+                                       hidl_vec<uint64_t>& sampleComponent0,
+                                       hidl_vec<uint64_t>& sampleComponent1,
+                                       hidl_vec<uint64_t>& sampleComponent2,
+                                       hidl_vec<uint64_t>& sampleComponent3) {
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayCapabilities(
+    Display display,
+    std::vector<IComposerClient::DisplayCapability>* outCapabilities) {
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerPerFrameMetadataBlobs(
+    Display display, Layer layer,
+    std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) {
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayBrightnessSupport(Display display, bool* outSupport) {
+  return Error::NONE;
+}
+
+Error VrHwc::setDisplayBrightness(Display display, float brightness) {
+  return Error::NONE;
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h
index e8c0212..3e3a630 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.h
+++ b/services/vr/hardware_composer/impl/vr_hwc.h
@@ -17,9 +17,9 @@
 #define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
 
 #include <android-base/unique_fd.h>
-#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
-#include <android/hardware/graphics/composer/2.1/IComposer.h>
-#include <composer-hal/2.1/ComposerHal.h>
+#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
+#include <android/hardware/graphics/composer/2.3/IComposer.h>
+#include <composer-hal/2.3/ComposerHal.h>
 #include <private/dvr/vsync_service.h>
 #include <ui/Fence.h>
 #include <ui/GraphicBuffer.h>
@@ -28,15 +28,21 @@
 #include <mutex>
 #include <unordered_map>
 
-using namespace android::frameworks::vr::composer::V1_0;
+using namespace android::frameworks::vr::composer::V2_0;
 using namespace android::hardware::graphics::common::V1_0;
-using namespace android::hardware::graphics::composer::V2_1;
+using namespace android::hardware::graphics::composer::V2_3;
 
+using android::hardware::hidl_bitfield;
 using android::hardware::hidl_handle;
 using android::hardware::hidl_string;
 using android::hardware::hidl_vec;
 using android::hardware::Return;
 using android::hardware::Void;
+using android::hardware::graphics::composer::V2_1::Config;
+using android::hardware::graphics::composer::V2_1::Display;
+using android::hardware::graphics::composer::V2_1::Error;
+using android::hardware::graphics::composer::V2_1::Layer;
+using android::hardware::graphics::composer::V2_3::IComposerClient;
 
 namespace android {
 
@@ -46,16 +52,23 @@
 
 class VrComposerClient;
 
-using android::hardware::graphics::common::V1_0::PixelFormat;
-using android::hardware::graphics::composer::V2_1::hal::ComposerHal;
+using android::hardware::graphics::composer::V2_3::hal::ComposerHal;
+
+namespace types = android::hardware::graphics::common;
+
+using types::V1_1::RenderIntent;
+using types::V1_2::ColorMode;
+using types::V1_2::Dataspace;
+using types::V1_2::Hdr;
+using types::V1_2::PixelFormat;
 
 class ComposerView {
  public:
   struct ComposerLayer {
-    using Recti = hardware::graphics::composer::V2_1::IComposerClient::Rect;
-    using Rectf = hardware::graphics::composer::V2_1::IComposerClient::FRect;
+    using Recti = hardware::graphics::composer::V2_3::IComposerClient::Rect;
+    using Rectf = hardware::graphics::composer::V2_3::IComposerClient::FRect;
     using BlendMode =
-        hardware::graphics::composer::V2_1::IComposerClient::BlendMode;
+        hardware::graphics::composer::V2_3::IComposerClient::BlendMode;
 
     Layer id;
     sp<GraphicBuffer> buffer;
@@ -111,7 +124,7 @@
 
 struct HwcLayer {
   using Composition =
-      hardware::graphics::composer::V2_1::IComposerClient::Composition;
+      hardware::graphics::composer::V2_3::IComposerClient::Composition;
 
   explicit HwcLayer(Layer new_id) { info.id = new_id; }
 
@@ -205,96 +218,157 @@
       Display display, Layer layer,
       const IVrComposerClient::BufferMetadata& metadata);
 
-  // ComposerHal
+  // composer::V2_1::ComposerHal
   bool hasCapability(hwc2_capability_t capability) override;
 
   std::string dumpDebugInfo() override { return {}; }
-  void registerEventCallback(EventCallback* callback) override;
+
+  void registerEventCallback(ComposerHal::EventCallback* callback) override;
   void unregisterEventCallback() override;
 
   uint32_t getMaxVirtualDisplayCount() override;
-  Error createVirtualDisplay(uint32_t width, uint32_t height,
-      PixelFormat* format, Display* outDisplay) override;
   Error destroyVirtualDisplay(Display display) override;
 
   Error createLayer(Display display, Layer* outLayer) override;
   Error destroyLayer(Display display, Layer layer) override;
 
   Error getActiveConfig(Display display, Config* outConfig) override;
-  Error getClientTargetSupport(Display display,
-          uint32_t width, uint32_t height,
-          PixelFormat format, Dataspace dataspace) override;
-  Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override;
   Error getDisplayAttribute(Display display, Config config,
-          IComposerClient::Attribute attribute, int32_t* outValue) override;
+                            IComposerClient::Attribute attribute,
+                            int32_t* outValue) override;
   Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
   Error getDisplayName(Display display, hidl_string* outName) override;
   Error getDisplayType(Display display,
-          IComposerClient::DisplayType* outType) override;
+                       IComposerClient::DisplayType* outType) override;
   Error getDozeSupport(Display display, bool* outSupport) override;
-  Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes,
-          float* outMaxLuminance, float* outMaxAverageLuminance,
-          float* outMinLuminance) override;
 
   Error setActiveConfig(Display display, Config config) override;
-  Error setColorMode(Display display, ColorMode mode) override;
-  Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
   Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
 
   Error setColorTransform(Display display, const float* matrix,
-          int32_t hint) override;
+                          int32_t hint) override;
   Error setClientTarget(Display display, buffer_handle_t target,
-          int32_t acquireFence, int32_t dataspace,
-          const std::vector<hwc_rect_t>& damage) override;
+                        int32_t acquireFence, int32_t dataspace,
+                        const std::vector<hwc_rect_t>& damage) override;
   Error setOutputBuffer(Display display, buffer_handle_t buffer,
-          int32_t releaseFence) override;
-  Error validateDisplay(Display display,
-          std::vector<Layer>* outChangedLayers,
-          std::vector<IComposerClient::Composition>* outCompositionTypes,
-          uint32_t* outDisplayRequestMask,
-          std::vector<Layer>* outRequestedLayers,
-          std::vector<uint32_t>* outRequestMasks) override;
+                        int32_t releaseFence) override;
+  Error validateDisplay(
+      Display display, std::vector<Layer>* outChangedLayers,
+      std::vector<IComposerClient::Composition>* outCompositionTypes,
+      uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
+      std::vector<uint32_t>* outRequestMasks) override;
   Error acceptDisplayChanges(Display display) override;
   Error presentDisplay(Display display, int32_t* outPresentFence,
-          std::vector<Layer>* outLayers,
-          std::vector<int32_t>* outReleaseFences) override;
+                       std::vector<Layer>* outLayers,
+                       std::vector<int32_t>* outReleaseFences) override;
 
-  Error setLayerCursorPosition(Display display, Layer layer,
-          int32_t x, int32_t y) override;
-  Error setLayerBuffer(Display display, Layer layer,
-          buffer_handle_t buffer, int32_t acquireFence) override;
+  Error setLayerCursorPosition(Display display, Layer layer, int32_t x,
+                               int32_t y) override;
+  Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
+                       int32_t acquireFence) override;
   Error setLayerSurfaceDamage(Display display, Layer layer,
-          const std::vector<hwc_rect_t>& damage) override;
+                              const std::vector<hwc_rect_t>& damage) override;
   Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
   Error setLayerColor(Display display, Layer layer,
-          IComposerClient::Color color) override;
+                      IComposerClient::Color color) override;
   Error setLayerCompositionType(Display display, Layer layer,
-          int32_t type) override;
+                                int32_t type) override;
   Error setLayerDataspace(Display display, Layer layer,
-          int32_t dataspace) override;
+                          int32_t dataspace) override;
   Error setLayerDisplayFrame(Display display, Layer layer,
-          const hwc_rect_t& frame) override;
+                             const hwc_rect_t& frame) override;
   Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
   Error setLayerSidebandStream(Display display, Layer layer,
-          buffer_handle_t stream) override;
+                               buffer_handle_t stream) override;
   Error setLayerSourceCrop(Display display, Layer layer,
-          const hwc_frect_t& crop) override;
+                           const hwc_frect_t& crop) override;
   Error setLayerTransform(Display display, Layer layer,
-          int32_t transform) override;
+                          int32_t transform) override;
   Error setLayerVisibleRegion(Display display, Layer layer,
-          const std::vector<hwc_rect_t>& visible) override;
+                              const std::vector<hwc_rect_t>& visible) override;
   Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
 
+  // composer::V2_2::ComposerHal
+  Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle,
+                          android::base::unique_fd fenceFd) override;
+  Error getReadbackBufferFence(Display display,
+                               android::base::unique_fd* outFenceFd) override;
+  Error createVirtualDisplay_2_2(uint32_t width, uint32_t height,
+                                 types::V1_1::PixelFormat* format,
+                                 Display* outDisplay) override;
+  Error setPowerMode_2_2(Display display,
+                         IComposerClient::PowerMode mode) override;
+  Error setLayerFloatColor(Display display, Layer layer,
+                           IComposerClient::FloatColor color) override;
+  Error getRenderIntents(Display display, types::V1_1::ColorMode mode,
+                         std::vector<RenderIntent>* outIntents) override;
+  std::array<float, 16> getDataspaceSaturationMatrix(
+      types::V1_1::Dataspace dataspace) override;
+
+  // composer::V2_3::ComposerHal
+  Error getHdrCapabilities_2_3(Display display, hidl_vec<Hdr>* outTypes,
+                               float* outMaxLuminance,
+                               float* outMaxAverageLuminance,
+                               float* outMinLuminance) override;
+  Error setLayerPerFrameMetadata_2_3(
+      Display display, Layer layer,
+      const std::vector<IComposerClient::PerFrameMetadata>& metadata) override;
+  Error getPerFrameMetadataKeys_2_3(
+      Display display,
+      std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) override;
+  Error setColorMode_2_3(Display display, ColorMode mode,
+                         RenderIntent intent) override;
+  Error getRenderIntents_2_3(Display display, ColorMode mode,
+                             std::vector<RenderIntent>* outIntents) override;
+  Error getColorModes_2_3(Display display,
+                          hidl_vec<ColorMode>* outModes) override;
+  Error getClientTargetSupport_2_3(Display display, uint32_t width,
+                                   uint32_t height, PixelFormat format,
+                                   Dataspace dataspace) override;
+  Error getReadbackBufferAttributes_2_3(Display display, PixelFormat* outFormat,
+                                        Dataspace* outDataspace) override;
+  Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                     std::vector<uint8_t>* outData) override;
+  Error setLayerColorTransform(Display display, Layer layer,
+                               const float* matrix) override;
+  Error getDisplayedContentSamplingAttributes(
+      Display display, PixelFormat& format, Dataspace& dataspace,
+      hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask)
+      override;
+  Error setDisplayedContentSamplingEnabled(
+      Display display, IComposerClient::DisplayedContentSampling enable,
+      hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
+      uint64_t maxFrames) override;
+  Error getDisplayedContentSample(
+      Display display, uint64_t maxFrames, uint64_t timestamp,
+      uint64_t& frameCount, hidl_vec<uint64_t>& sampleComponent0,
+      hidl_vec<uint64_t>& sampleComponent1,
+      hidl_vec<uint64_t>& sampleComponent2,
+      hidl_vec<uint64_t>& sampleComponent3) override;
+  Error getDisplayCapabilities(Display display,
+                               std::vector<IComposerClient::DisplayCapability>*
+                                   outCapabilities) override;
+  Error setLayerPerFrameMetadataBlobs(
+      Display display, Layer layer,
+      std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) override;
+  Error getDisplayBrightnessSupport(Display display, bool* outSupport) override;
+  Error setDisplayBrightness(Display display, float brightness) override;
+
   // IComposer:
   Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
   Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
   Return<void> createClient(createClient_cb hidl_cb) override;
+  Return<void> createClient_2_3(
+      IComposer::createClient_2_3_cb hidl_cb) override;
 
   // ComposerView:
   void ForceDisplaysRefresh() override;
   void RegisterObserver(Observer* observer) override;
   void UnregisterObserver(Observer* observer) override;
 
+  Return<void> debug(const hidl_handle& fd,
+                     const hidl_vec<hidl_string>& args) override;
+
  private:
   class VsyncCallback : public BnVsyncCallback {
    public:
diff --git a/services/vr/virtual_touchpad/virtual_touchpad.rc b/services/vr/virtual_touchpad/virtual_touchpad.rc
index 99315ef..0de0f9e 100644
--- a/services/vr/virtual_touchpad/virtual_touchpad.rc
+++ b/services/vr/virtual_touchpad/virtual_touchpad.rc
@@ -1,5 +1,5 @@
 service virtual_touchpad /system/bin/virtual_touchpad
   class core
   user system
-  group system input
+  group system input uhid
   writepid /dev/cpuset/system/tasks