Merge "Revert "Add AndroidBitmap_getDataSpace""
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 20bfe65..4a84884 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -14,10 +14,12 @@
                libs/renderengine/
                libs/ui/
                libs/vr/
+               opengl/libs/
                services/bufferhub/
                services/inputflinger/
                services/surfaceflinger/
                services/vr/
+               vulkan/
 
 [Hook Scripts]
 owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl
new file mode 100644
index 0000000..7026ca8
--- /dev/null
+++ b/aidl/gui/android/view/LayerMetadataKey.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+/** @hide */
+@Backing(type="int")
+enum LayerMetadataKey {
+    METADATA_OWNER_UID = 1,
+    METADATA_WINDOW_TYPE = 2,
+    METADATA_TASK_ID = 3,
+    METADATA_MOUSE_CURSOR = 4,
+}
diff --git a/build/phone-xhdpi-2048-dalvik-heap.mk b/build/phone-xhdpi-2048-dalvik-heap.mk
index 042a6e6..7ccfc13 100644
--- a/build/phone-xhdpi-2048-dalvik-heap.mk
+++ b/build/phone-xhdpi-2048-dalvik-heap.mk
@@ -14,7 +14,7 @@
 # limitations under the License.
 #
 
-# Provides overrides to configure the Dalvik heap for a 2G phone
+# Provides overrides to configure the Dalvik heap for a 2GB phone
 # 192m of RAM gives enough space for 5 8 megapixel camera bitmaps in RAM.
 
 PRODUCT_PROPERTY_OVERRIDES += \
diff --git a/build/phone-xhdpi-4096-dalvik-heap.mk b/build/phone-xhdpi-4096-dalvik-heap.mk
new file mode 100644
index 0000000..2b84841
--- /dev/null
+++ b/build/phone-xhdpi-4096-dalvik-heap.mk
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+# Provides overrides to configure the Dalvik heap for a 4GB phone
+
+PRODUCT_PROPERTY_OVERRIDES += \
+    dalvik.vm.heapstartsize=8m \
+    dalvik.vm.heapgrowthlimit=192m \
+    dalvik.vm.heapsize=512m \
+    dalvik.vm.heaptargetutilization=0.6 \
+    dalvik.vm.heapminfree=8m \
+    dalvik.vm.heapmaxfree=16m
diff --git a/build/phone-xhdpi-6144-dalvik-heap.mk b/build/phone-xhdpi-6144-dalvik-heap.mk
new file mode 100644
index 0000000..2bacc4a
--- /dev/null
+++ b/build/phone-xhdpi-6144-dalvik-heap.mk
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+# Provides overrides to configure the Dalvik heap for a 6GB phone
+
+PRODUCT_PROPERTY_OVERRIDES += \
+    dalvik.vm.heapstartsize=16m \
+    dalvik.vm.heapgrowthlimit=256m \
+    dalvik.vm.heapsize=512m \
+    dalvik.vm.heaptargetutilization=0.5 \
+    dalvik.vm.heapminfree=8m \
+    dalvik.vm.heapmaxfree=32m
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index ccd74db..87ea520 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -81,8 +81,8 @@
 
 binder::Status DumpstateService::startBugreport(int32_t calling_uid,
                                                 const std::string& calling_package,
-                                                const android::base::unique_fd& bugreport_fd,
-                                                const android::base::unique_fd& screenshot_fd,
+                                                android::base::unique_fd bugreport_fd,
+                                                android::base::unique_fd screenshot_fd,
                                                 int bugreport_mode,
                                                 const sp<IDumpstateListener>& listener) {
     MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 27954ad..6dc0225 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -39,8 +39,8 @@
     status_t dump(int fd, const Vector<String16>& args) override;
 
     binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package,
-                                  const android::base::unique_fd& bugreport_fd,
-                                  const android::base::unique_fd& screenshot_fd, int bugreport_mode,
+                                  android::base::unique_fd bugreport_fd,
+                                  android::base::unique_fd screenshot_fd, int bugreport_mode,
                                   const sp<IDumpstateListener>& listener) override;
 
     // No-op
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 9d6a7ff..fc94f23 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2729,15 +2729,13 @@
 DurationReporter::~DurationReporter() {
     if (!title_.empty()) {
         float elapsed = (float)(Nanotime() - started_) / NANOS_PER_SEC;
-        if (elapsed < .5f && !verbose_) {
-            return;
+        if (elapsed >= .5f || verbose_) {
+            MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed);
         }
-        MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed);
-        if (logcat_only_) {
-            return;
+        if (!logcat_only_) {
+            // Use "Yoda grammar" to make it easier to grep|sort sections.
+            printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str());
         }
-        // Use "Yoda grammar" to make it easier to grep|sort sections.
-        printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str());
     }
 }
 
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index f1884f8..dac90d9 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -444,7 +444,7 @@
 
     sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
     android::binder::Status status =
-        ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+        ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
                                   Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener);
     // startBugreport is an async call. Verify binder call succeeded first, then wait till listener
     // gets expected callbacks.
@@ -480,7 +480,7 @@
     // Call startBugreport with bad arguments.
     sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
     android::binder::Status status =
-        ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+        ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
                                   2000,  // invalid bugreport mode
                                   listener);
     EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
@@ -501,20 +501,24 @@
 
     // Prepare arguments
     unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
+    unique_fd bugreport_fd2(dup(bugreport_fd.get()));
     unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
+    unique_fd screenshot_fd2(dup(screenshot_fd.get()));
 
     EXPECT_NE(bugreport_fd.get(), -1);
+    EXPECT_NE(bugreport_fd2.get(), -1);
     EXPECT_NE(screenshot_fd.get(), -1);
+    EXPECT_NE(screenshot_fd2.get(), -1);
 
     sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
     android::binder::Status status =
-        ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+        ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
                                   Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1);
     EXPECT_TRUE(status.isOk());
 
     // try to make another call to startBugreport. This should fail.
     sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
-    status = ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+    status = ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd2), std::move(screenshot_fd2),
                                        Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2);
     EXPECT_FALSE(status.isOk());
     WaitTillExecutionComplete(listener2.get());
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 5476319..402767a 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -15,20 +15,21 @@
 cc_defaults {
     name: "idlcli-defaults",
     shared_libs: [
+        "android.hardware.vibrator-ndk_platform",
         "android.hardware.vibrator@1.0",
         "android.hardware.vibrator@1.1",
         "android.hardware.vibrator@1.2",
         "android.hardware.vibrator@1.3",
         "libbase",
-        "libbinder",
+        "libbinder_ndk",
         "libhidlbase",
         "liblog",
         "libutils",
-        "vintf-vibrator-cpp",
     ],
     cflags: [
         "-DLOG_TAG=\"idlcli\"",
     ],
+    vendor_available: true,
 }
 
 cc_library {
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index 2f11923..ca5142d 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -13,13 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
 #define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
 
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <android/binder_manager.h>
 #include <android/hardware/vibrator/1.3/IVibrator.h>
-#include <android/hardware/vibrator/IVibrator.h>
-#include <binder/IServiceManager.h>
 
 #include "utils.h"
 
@@ -39,22 +38,27 @@
 }
 
 template <>
-inline binder::Status NullptrStatus() {
-    using binder::Status;
-    return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+inline ndk::ScopedAStatus NullptrStatus() {
+    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_NULL_POINTER));
 }
 
 template <typename I>
-inline sp<I> getService() {
+inline auto getService() {
     return I::getService();
 }
 
 template <>
-inline sp<hardware::vibrator::IVibrator> getService() {
-    return waitForVintfService<hardware::vibrator::IVibrator>();
+inline auto getService<aidl::android::hardware::vibrator::IVibrator>() {
+    const auto instance =
+            std::string() + aidl::android::hardware::vibrator::IVibrator::descriptor + "/default";
+    auto vibBinder = ndk::SpAIBinder(AServiceManager_getService(instance.c_str()));
+    return aidl::android::hardware::vibrator::IVibrator::fromBinder(vibBinder);
 }
 
 template <typename I>
+using shared_ptr = std::result_of_t<decltype(getService<I>)&()>;
+
+template <typename I>
 class HalWrapper {
 public:
     static std::unique_ptr<HalWrapper> Create() {
@@ -70,10 +74,10 @@
     }
 
 private:
-    HalWrapper(sp<I>&& hal) : mHal(std::move(hal)) {}
+    HalWrapper(shared_ptr<I>&& hal) : mHal(std::move(hal)) {}
 
 private:
-    sp<I> mHal;
+    shared_ptr<I> mHal;
 };
 
 template <typename I>
@@ -95,7 +99,7 @@
 namespace V1_1 = ::android::hardware::vibrator::V1_1;
 namespace V1_2 = ::android::hardware::vibrator::V1_2;
 namespace V1_3 = ::android::hardware::vibrator::V1_3;
-namespace aidl = ::android::hardware::vibrator;
+namespace aidl = ::aidl::android::hardware::vibrator;
 
 } // namespace vibrator
 } // namespace idlcli
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
index 705e40b..4721a5f 100644
--- a/cmds/idlcli/vibrator/CommandCompose.cpp
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -80,7 +80,7 @@
         Status ret;
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else {
             return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
index 30d8587..303a989 100644
--- a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
@@ -48,7 +48,7 @@
 
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::getCapabilities, &cap);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else {
             return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
index b414307..10508bd 100644
--- a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
@@ -50,7 +50,7 @@
 
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::getCompositionDelayMax, &maxDelayMs);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else {
             return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
index 360fc9d..900cb18 100644
--- a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
@@ -50,7 +50,7 @@
 
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::getCompositionSizeMax, &maxSize);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else {
             return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandOff.cpp b/cmds/idlcli/vibrator/CommandOff.cpp
index 53fada0..cedb9fe 100644
--- a/cmds/idlcli/vibrator/CommandOff.cpp
+++ b/cmds/idlcli/vibrator/CommandOff.cpp
@@ -47,7 +47,7 @@
 
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::off);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else if (auto hal = getHal<V1_0::IVibrator>()) {
             auto status = hal->call(&V1_0::IVibrator::off);
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index ccb3c19..4e7e493 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -55,7 +55,7 @@
 
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else if (auto hal = getHal<V1_0::IVibrator>()) {
             auto status = hal->call(&V1_0::IVibrator::on, mDuration);
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index 58d4e0a..69c7e37 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -99,7 +99,7 @@
             auto status =
                     hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect),
                               static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             lengthMs = static_cast<uint32_t>(aidlLengthMs);
             ret = status.isOk() ? OK : ERROR;
         } else {
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
index 33d7eed..8b8058c 100644
--- a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -56,7 +56,7 @@
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::setAmplitude,
                                     static_cast<float>(mAmplitude) / UINT8_MAX);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else if (auto hal = getHal<V1_0::IVibrator>()) {
             auto status = hal->call(&V1_0::IVibrator::setAmplitude, mAmplitude);
diff --git a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
index 5bc827e..1795793 100644
--- a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
+++ b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
@@ -53,7 +53,7 @@
 
         if (auto hal = getHal<aidl::IVibrator>()) {
             auto status = hal->call(&aidl::IVibrator::setExternalControl, mEnable);
-            statusStr = status.toString8();
+            statusStr = status.getDescription();
             ret = status.isOk() ? OK : ERROR;
         } else if (auto hal = getHal<V1_3::IVibrator>()) {
             auto status = hal->call(&V1_3::IVibrator::setExternalControl, mEnable);
diff --git a/cmds/installd/.gitignore b/cmds/installd/.gitignore
new file mode 100644
index 0000000..abc921c
--- /dev/null
+++ b/cmds/installd/.gitignore
@@ -0,0 +1,2 @@
+# ignore the files generated by intellij
+.idea
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 75dec37..8ff4dd8 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -13,6 +13,7 @@
     srcs: [
         "CacheItem.cpp",
         "CacheTracker.cpp",
+        "CrateManager.cpp",
         "InstalldNativeService.cpp",
         "QuotaUtils.cpp",
         "dexopt.cpp",
@@ -163,6 +164,7 @@
     name: "installd_aidl",
     srcs: [
         "binder/android/os/IInstalld.aidl",
+        "binder/android/os/storage/CrateMetadata.aidl",
     ],
     path: "binder",
 }
diff --git a/cmds/installd/CrateManager.cpp b/cmds/installd/CrateManager.cpp
new file mode 100644
index 0000000..344aefb
--- /dev/null
+++ b/cmds/installd/CrateManager.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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 "CrateManager.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android/log.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <string>
+#include <utils.h>
+
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+CrateManager::CrateManager(const char* uuid, userid_t userId, const std::string& packageName) {
+    mPackageName = packageName;
+    mRoot = create_data_user_ce_package_path(uuid, userId, (const char*)packageName.c_str());
+    mCratedFoldersRoot = StringPrintf("%s/crates", mRoot.c_str());
+}
+
+CrateManager::~CrateManager() {}
+
+static std::string getValidatedCratedPath(std::string path) {
+    size_t pos = path.rfind("/");
+    if (pos == std::string::npos) {
+        return path;
+    }
+
+    return path.substr(pos + 1, path.length());
+}
+
+void CrateManager::traverseChildDir(const std::string& targetDir,
+    std::function<void(FTSENT*)>& onVisitChildDir) {
+    char* argv[] = {(char*)targetDir.c_str(), nullptr};
+    FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr);
+    if (fts == nullptr) {
+        PLOG(WARNING) << "Failed to fts_open " << targetDir;
+        return;
+    }
+
+    FTSENT* p;
+    while ((p = fts_read(fts)) != nullptr) {
+        switch (p->fts_info) {
+            case FTS_D:
+                if (p->fts_level == 1) {
+                    onVisitChildDir(p);
+                }
+                break;
+            default:
+                break;
+        }
+
+        if (p->fts_level == 1) {
+            fts_set(fts, p, FTS_SKIP);
+        }
+    }
+    fts_close(fts);
+}
+
+void CrateManager::traverseAllPackagesForUser(
+        const std::unique_ptr<std::string>& uuid, userid_t userId,
+        std::function<void(FTSENT*)>& onHandlingPackage) {
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    auto ce_path = create_data_user_ce_path(uuid_, userId);
+    traverseChildDir(ce_path, onHandlingPackage);
+}
+
+void CrateManager::createCrate(
+        CratedFolder cratedFolder,
+        std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+    const char* path = cratedFolder->fts_path;
+    if (path == nullptr || *path == '\0') {
+        return;
+    }
+
+    std::unique_ptr<CrateMetadata> crateMetadata = std::make_unique<CrateMetadata>();
+    crateMetadata->uid = cratedFolder->fts_statp->st_uid;
+    crateMetadata->packageName = mPackageName;
+    crateMetadata->id = getValidatedCratedPath(path);
+
+    onCreateCrate(cratedFolder, crateMetadata);
+}
+
+void CrateManager::traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+    std::function<void(FTSENT*)> onVisitCrateDir = [&](FTSENT* cratedFolder) -> void {
+        createCrate(cratedFolder, onCreateCrate);
+    };
+    traverseChildDir(mCratedFoldersRoot, onVisitCrateDir);
+}
+
+#if CRATE_DEBUG
+void CrateManager::dump(std::unique_ptr<CrateMetadata>& CrateMetadata) {
+    LOG(DEBUG) << "CrateMetadata = {"
+            << "uid : \"" << CrateMetadata->uid
+            << "\", packageName : \"" << CrateMetadata->packageName
+            << "\", id : \"" << CrateMetadata->id
+            << "\"}";
+}
+#endif
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/CrateManager.h b/cmds/installd/CrateManager.h
new file mode 100644
index 0000000..1776622
--- /dev/null
+++ b/cmds/installd/CrateManager.h
@@ -0,0 +1,82 @@
+/*
+ * 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_INSTALLD_CRATE_INFO_MANAGER_H
+#define ANDROID_INSTALLD_CRATE_INFO_MANAGER_H
+
+#include <android/os/storage/CrateMetadata.h>
+#include <cutils/multiuser.h>
+#include <fts.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#ifndef CRATE_DEBUG
+#define CRATE_DEBUG 1
+#endif
+
+namespace android {
+namespace installd {
+
+using android::os::storage::CrateMetadata;
+
+/**
+ * The crated folder actually is a folder that is the first level child director. In order to
+ * distingish between the crated folder and the other FTSENT*, to define the type "CratedFolder"
+ * make the code easy to identify the difference.
+ */
+typedef FTSENT* CratedFolder;
+
+/**
+ * In order to give the users more fine-grained files controlling, the crate information can help
+ * applications' developers to show the more detail information to the users. The crate information
+ * include the Label, Expiration etc..
+ */
+class CrateManager {
+public:
+    CrateManager(const char* uuid, userid_t userId, const std::string& packageName);
+    ~CrateManager();
+
+    void traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+
+    static void traverseChildDir(const std::string& targetDir,
+            std::function<void(FTSENT*)>& onVisitChildDir);
+
+    static void traverseAllPackagesForUser(
+        const std::unique_ptr<std::string>& uuid,
+        userid_t userId,
+        std::function<void(FTSENT*)>& onHandlingPackage);
+
+#if CRATE_DEBUG
+    static void dump(std::unique_ptr<CrateMetadata>& CrateMetadata);
+#endif
+private:
+    std::string mRoot;
+    std::string mCratedFoldersRoot;
+    std::string mPackageName;
+
+    void createCrate(
+        CratedFolder cratedFolder,
+        std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_CRATE_INFO_MANAGER_H
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 95957a0..6b14bee 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -31,6 +31,7 @@
 #include <sys/file.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
+#include <sys/mount.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/statvfs.h>
@@ -65,6 +66,7 @@
 #include "view_compiler.h"
 
 #include "CacheTracker.h"
+#include "CrateManager.h"
 #include "MatchExtensionGen.h"
 #include "QuotaUtils.h"
 
@@ -86,6 +88,9 @@
 static constexpr const char* kCpPath = "/system/bin/cp";
 static constexpr const char* kXattrDefault = "user.default";
 
+static constexpr const char* kDataMirrorCePath = "/data_mirror/data_ce";
+static constexpr const char* kDataMirrorDePath = "/data_mirror/data_de";
+
 static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
 
 static constexpr const char* PKG_LIB_POSTFIX = "/lib";
@@ -99,6 +104,13 @@
 static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
 static constexpr const char* kFuseProp = "persist.sys.fuse";
 
+/**
+ * Property to control if app data isolation is enabled.
+ */
+static constexpr const char* kAppDataIsolationEnabledProperty = "persist.zygote.app_data_isolation";
+
+static std::atomic<bool> sAppDataIsolationEnabled(false);
+
 namespace {
 
 constexpr const char* kDump = "android.permission.DUMP";
@@ -258,6 +270,8 @@
     sp<ProcessState> ps(ProcessState::self());
     ps->startThreadPool();
     ps->giveThreadPoolName();
+    sAppDataIsolationEnabled = android::base::GetBoolProperty(
+            kAppDataIsolationEnabledProperty, false);
     return android::OK;
 }
 
@@ -450,9 +464,12 @@
 
         // And return the CE inode of the top-level data directory so we can
         // clear contents while CE storage is locked
-        if ((_aidl_return != nullptr)
-                && get_path_inode(path, reinterpret_cast<ino_t*>(_aidl_return)) != 0) {
-            return error("Failed to get_path_inode for " + path);
+        if (_aidl_return != nullptr) {
+            ino_t result;
+            if (get_path_inode(path, &result) != 0) {
+                return error("Failed to get_path_inode for " + path);
+            }
+            *_aidl_return = static_cast<uint64_t>(result);
         }
     }
     if (flags & FLAG_STORAGE_DE) {
@@ -622,10 +639,8 @@
                 if (delete_dir_contents(path, true) != 0) {
                     res = error("Failed to delete contents of " + path);
                 }
-                path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname);
-                if (delete_dir_contents(path, true) != 0) {
-                    res = error("Failed to delete contents of " + path);
-                }
+                // Note that we explicitly don't delete OBBs - those are only removed on
+                // app uninstall.
             }
         }
     }
@@ -2029,6 +2044,82 @@
     return ok();
 }
 
+binder::Status InstalldNativeService::getAppCrates(
+        const std::unique_ptr<std::string>& uuid,
+        const std::vector<std::string>& packageNames, int32_t userId,
+        std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    for (const auto& packageName : packageNames) {
+        CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    }
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
+            [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+        if (cratedFolder == nullptr) {
+            return;
+        }
+        retVector->push_back(std::move(crateMetadata));
+    };
+
+    for (const auto& packageName : packageNames) {
+#if CRATE_DEBUG
+        LOG(DEBUG) << "packageName = " << packageName;
+#endif
+        auto crateManager = std::make_unique<CrateManager>(uuid_, userId, packageName);
+        crateManager->traverseAllCrates(onCreateCrate);
+    }
+
+#if CRATE_DEBUG
+    LOG(WARNING) << "retVector->size() =" << retVector->size();
+    for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
+        CrateManager::dump(*iter);
+    }
+#endif
+
+    *_aidl_return = std::move(retVector);
+    return ok();
+}
+
+binder::Status InstalldNativeService::getUserCrates(
+        const std::unique_ptr<std::string>& uuid, int32_t userId,
+        std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+
+    std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
+            [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+        if (cratedFolder == nullptr) {
+            return;
+        }
+        retVector->push_back(std::move(crateMetadata));
+    };
+
+    std::function<void(FTSENT*)> onHandingPackage = [&](FTSENT* packageDir) -> void {
+        auto crateManager = std::make_unique<CrateManager>(uuid_, userId, packageDir->fts_name);
+        crateManager->traverseAllCrates(onCreateCrate);
+    };
+    CrateManager::traverseAllPackagesForUser(uuid, userId, onHandingPackage);
+
+#if CRATE_DEBUG
+    LOG(DEBUG) << "retVector->size() =" << retVector->size();
+    for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
+        CrateManager::dump(*iter);
+    }
+#endif
+
+    *_aidl_return = std::move(retVector);
+    return ok();
+}
+
 binder::Status InstalldNativeService::setAppQuota(const std::unique_ptr<std::string>& uuid,
         int32_t userId, int32_t appId, int64_t cacheQuota) {
     ENFORCE_UID(AID_SYSTEM);
@@ -2421,7 +2512,7 @@
 #endif
 
 binder::Status InstalldNativeService::installApkVerity(const std::string& filePath,
-        const ::android::base::unique_fd& verityInputAshmem, int32_t contentSize) {
+        android::base::unique_fd verityInputAshmem, int32_t contentSize) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PATH(filePath);
     std::lock_guard<std::recursive_mutex> lock(mLock);
@@ -2595,6 +2686,8 @@
         std::getline(in, ignored);
 
         if (android::base::GetBoolProperty(kFuseProp, false)) {
+            // TODO(b/146139106): Use sdcardfs mounts on devices running sdcardfs so we don't bypass
+            // it's VFS cache
             if (target.compare(0, 17, "/mnt/pass_through") == 0) {
                 LOG(DEBUG) << "Found storage mount " << source << " at " << target;
                 mStorageMounts[source] = target;
@@ -2611,11 +2704,105 @@
     return ok();
 }
 
+// Mount volume's CE and DE storage to mirror
+binder::Status InstalldNativeService::onPrivateVolumeMounted(
+        const std::unique_ptr<std::string>& uuid) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    if (!sAppDataIsolationEnabled) {
+        return ok();
+    }
+    if (!uuid) {
+        return error("Should not happen, mounting uuid == null");
+    }
+
+    const char* uuid_ = uuid->c_str();
+    // Mount CE mirror
+    std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+    if (fs_prepare_dir(mirrorVolCePath.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
+        return error("Failed to create CE mirror");
+    }
+    auto cePath = StringPrintf("%s/user_ce", create_data_path(uuid_).c_str());
+    if (TEMP_FAILURE_RETRY(mount(cePath.c_str(), mirrorVolCePath.c_str(), NULL,
+            MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
+        return error("Failed to mount " + mirrorVolCePath);
+    }
+
+    // Mount DE mirror
+    std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+    if (fs_prepare_dir(mirrorVolDePath.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
+        return error("Failed to create DE mirror");
+    }
+    auto dePath = StringPrintf("%s/user_de", create_data_path(uuid_).c_str());
+    if (TEMP_FAILURE_RETRY(mount(dePath.c_str(), mirrorVolDePath.c_str(), NULL,
+            MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
+        return error("Failed to mount " + mirrorVolDePath);
+    }
+    return ok();
+}
+
+// Unmount volume's CE and DE storage from mirror
+binder::Status InstalldNativeService::onPrivateVolumeRemoved(
+        const std::unique_ptr<std::string>& uuid) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    if (!sAppDataIsolationEnabled) {
+        return ok();
+    }
+    if (!uuid) {
+        // It happens when private volume failed to mount.
+        LOG(INFO) << "Ignore unmount uuid=null";
+        return ok();
+    }
+    const char* uuid_ = uuid->c_str();
+
+    binder::Status res = ok();
+
+    std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
+    std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+
+    // Unmount CE storage
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+    if (TEMP_FAILURE_RETRY(umount(mirrorCeVolPath.c_str())) != 0) {
+        if (errno != ENOENT) {
+            res = error(StringPrintf("Failed to umount %s %s", mirrorCeVolPath.c_str(),
+                                strerror(errno)));
+        }
+    }
+    if (delete_dir_contents_and_dir(mirrorCeVolPath, true) != 0) {
+        res = error("Failed to delete " + mirrorCeVolPath);
+    }
+
+    // Unmount DE storage
+    if (TEMP_FAILURE_RETRY(umount(mirrorDeVolPath.c_str())) != 0) {
+        if (errno != ENOENT) {
+            res = error(StringPrintf("Failed to umount %s %s", mirrorDeVolPath.c_str(),
+                                strerror(errno)));
+        }
+    }
+    if (delete_dir_contents_and_dir(mirrorDeVolPath, true) != 0) {
+        res = error("Failed to delete " + mirrorDeVolPath);
+    }
+    return res;
+}
+
 std::string InstalldNativeService::findDataMediaPath(
         const std::unique_ptr<std::string>& uuid, userid_t userid) {
     std::lock_guard<std::recursive_mutex> lock(mMountsLock);
     const char* uuid_ = uuid ? uuid->c_str() : nullptr;
     auto path = StringPrintf("%s/media", create_data_path(uuid_).c_str());
+    if (android::base::GetBoolProperty(kFuseProp, false)) {
+        // TODO(b/146139106): This is only safe on devices not running sdcardfs where there is no
+        // risk of bypassing the sdcardfs VFS cache
+
+        // Always use the lower filesystem path on FUSE enabled devices not running sdcardfs
+        // The upper filesystem path, /mnt/pass_through/<userid>/<vol>/ which was a bind mount
+        // to the lower filesytem may have been unmounted already when a user is
+        // removed and the path will now be pointing to a tmpfs without content
+        return StringPrintf("%s/%u", path.c_str(), userid);
+    }
+
     auto resolved = mStorageMounts[path];
     if (resolved.empty()) {
         LOG(WARNING) << "Failed to find storage mount for " << path;
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index ef91bf8..bf11002 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -81,6 +81,16 @@
             int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
             std::vector<int64_t>* _aidl_return);
 
+    binder::Status getAppCrates(const std::unique_ptr<std::string>& uuid,
+            const std::vector<std::string>& packageNames,
+            int32_t userId,
+            std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+                    _aidl_return);
+    binder::Status getUserCrates(
+            const std::unique_ptr<std::string>& uuid, int32_t userId,
+            std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+                    _aidl_return);
+
     binder::Status setAppQuota(const std::unique_ptr<std::string>& uuid,
             int32_t userId, int32_t appId, int64_t cacheQuota);
 
@@ -133,7 +143,7 @@
     binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
             const std::unique_ptr<std::string>& outputPath);
     binder::Status installApkVerity(const std::string& filePath,
-            const ::android::base::unique_fd& verityInput, int32_t contentSize);
+            android::base::unique_fd verityInput, int32_t contentSize);
     binder::Status assertFsverityRootHashMatches(const std::string& filePath,
             const std::vector<uint8_t>& expectedHash);
     binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
@@ -146,6 +156,8 @@
     binder::Status invalidateMounts();
     binder::Status isQuotaSupported(const std::unique_ptr<std::string>& volumeUuid,
             bool* _aidl_return);
+    binder::Status onPrivateVolumeMounted(const std::unique_ptr<std::string>& volumeUuid);
+    binder::Status onPrivateVolumeRemoved(const std::unique_ptr<std::string>& volumeUuid);
 
     binder::Status prepareAppProfile(const std::string& packageName,
             int32_t userId, int32_t appId, const std::string& profileName,
diff --git a/cmds/installd/QuotaUtils.cpp b/cmds/installd/QuotaUtils.cpp
index b238dd3..e080291 100644
--- a/cmds/installd/QuotaUtils.cpp
+++ b/cmds/installd/QuotaUtils.cpp
@@ -61,6 +61,10 @@
         std::getline(in, target, ' ');
         std::getline(in, ignored);
 
+        if (target.compare(0, 13, "/data_mirror/") == 0) {
+            continue;
+        }
+
         if (source.compare(0, 11, "/dev/block/") == 0) {
             struct dqblk dq;
             if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), source.c_str(), 0,
@@ -97,6 +101,26 @@
     }
 }
 
+int64_t GetOccupiedSpaceForProjectId(const std::string& uuid, int projectId) {
+    const std::string device = FindQuotaDeviceForUuid(uuid);
+    if (device == "") {
+        return -1;
+    }
+    struct dqblk dq;
+    if (quotactl(QCMD(Q_GETQUOTA, PRJQUOTA), device.c_str(), projectId,
+            reinterpret_cast<char*>(&dq)) != 0) {
+        if (errno != ESRCH) {
+            PLOG(ERROR) << "Failed to quotactl " << device << " for Project ID " << projectId;
+        }
+        return -1;
+    } else {
+#if MEASURE_DEBUG
+        LOG(DEBUG) << "quotactl() for Project ID " << projectId << " " << dq.dqb_curspace;
+#endif
+        return dq.dqb_curspace;
+    }
+}
+
 int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid) {
     const std::string device = FindQuotaDeviceForUuid(uuid);
     if (device == "") {
diff --git a/cmds/installd/QuotaUtils.h b/cmds/installd/QuotaUtils.h
index 9ad170f..96aca04 100644
--- a/cmds/installd/QuotaUtils.h
+++ b/cmds/installd/QuotaUtils.h
@@ -35,6 +35,8 @@
 /* Get the current occupied space in bytes for a gid or -1 if fails */
 int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid);
 
+/* Get the current occupied space in bytes for a project id or -1 if fails */
+int64_t GetOccupiedSpaceForProjectId(const std::string& uuid, int projectId);
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 6cc4bde..891b26d 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -40,6 +40,14 @@
     long[] getUserSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
     long[] getExternalSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
 
+    @nullable
+    android.os.storage.CrateMetadata[] getAppCrates(
+            @nullable @utf8InCpp String uuid, in @utf8InCpp String[] packageNames,
+             int userId);
+    @nullable
+    android.os.storage.CrateMetadata[] getUserCrates(
+            @nullable @utf8InCpp String uuid, int userId);
+
     void setAppQuota(@nullable @utf8InCpp String uuid, int userId, int appId, long cacheQuota);
 
     void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid,
@@ -109,6 +117,8 @@
             int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags);
     void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
             int userId, long ceSnapshotInode, int snapshotId, int storageFlags);
+    void onPrivateVolumeMounted(@nullable @utf8InCpp String volumeUuid);
+    void onPrivateVolumeRemoved(@nullable @utf8InCpp String volumeUuid);
 
     void migrateLegacyObbData();
 
diff --git a/cmds/installd/binder/android/os/storage/CrateMetadata.aidl b/cmds/installd/binder/android/os/storage/CrateMetadata.aidl
new file mode 100644
index 0000000..bd6d12d
--- /dev/null
+++ b/cmds/installd/binder/android/os/storage/CrateMetadata.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+package android.os.storage;
+
+/** {@hide} */
+parcelable CrateMetadata {
+    /**
+     * To tell which uid the crate belong to.
+     * <p>Because installd query all of crates in specified userId, the install may return the list
+     * whose elements have the same crate id but different uid and package name.
+     * It needs to tell the caller the difference between these elements.
+     */
+    int uid;
+
+    /**
+     * To tell which the package the crate belong to.
+     * <p>Because installd query all of crates in specified uid, the install may return the list
+     * whose elements have the same uid and crate id but different package name.
+     * It needs to tell the caller the difference between these elements.
+     */
+    @utf8InCpp String packageName;
+
+    /**
+     * To tell the crate id that is the child directory/folder name in crates
+     * root.
+     */
+    @utf8InCpp String id;
+}
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 4eb1df0..6012822 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -278,6 +278,15 @@
     return "/data/dalvik-cache";
 }
 
+std::string create_system_user_ce_path(userid_t userId) {
+    return StringPrintf("%s/system_ce/%u", create_data_path(nullptr).c_str(), userId);
+}
+
+std::string create_system_user_ce_package_path(userid_t userId, const char* package_name) {
+    check_package_name(package_name);
+    return StringPrintf("%s/%s", create_system_user_ce_path(userId).c_str(), package_name);
+}
+
 // Keep profile paths in sync with ActivityThread and LoadedApk.
 const std::string PROFILE_EXT = ".prof";
 const std::string CURRENT_PROFILE_EXT = ".cur";
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 6a42026..6a39adc 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -82,6 +82,10 @@
 
 std::string create_data_dalvik_cache_path();
 
+std::string create_system_user_ce_path(userid_t userId);
+
+std::string create_system_user_ce_package_path(userid_t userId, const char* package_name);
+
 std::string create_primary_cur_profile_dir_path(userid_t userid);
 std::string create_primary_current_profile_package_dir_path(
         userid_t user, const std::string& package_name);
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 141171b..ae74ac3 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -18,6 +18,9 @@
 
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
 #include <binder/Stability.h>
 #include <cutils/android_filesystem_config.h>
 #include <cutils/multiuser.h>
@@ -80,7 +83,7 @@
 ServiceManager::~ServiceManager() {
     // this should only happen in tests
 
-    for (const auto& [name, callbacks] : mNameToCallback) {
+    for (const auto& [name, callbacks] : mNameToRegistrationCallback) {
         CHECK(!callbacks.empty()) << name;
         for (const auto& callback : callbacks) {
             CHECK(callback != nullptr) << name;
@@ -108,10 +111,11 @@
     auto ctx = mAccess->getCallingContext();
 
     sp<IBinder> out;
+    Service* service = nullptr;
     if (auto it = mNameToService.find(name); it != mNameToService.end()) {
-        const Service& service = it->second;
+        service = &(it->second);
 
-        if (!service.allowIsolated) {
+        if (!service->allowIsolated) {
             uid_t appid = multiuser_get_app_id(ctx.uid);
             bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;
 
@@ -119,7 +123,7 @@
                 return nullptr;
             }
         }
-        out = service.binder;
+        out = service->binder;
     }
 
     if (!mAccess->canFind(ctx, name)) {
@@ -130,6 +134,12 @@
         tryStartService(name);
     }
 
+    if (out) {
+        // Setting this guarantee each time we hand out a binder ensures that the client-checking
+        // loop knows about the event even if the client immediately drops the service
+        service->guaranteeClient = true;
+    }
+
     return out;
 }
 
@@ -182,15 +192,17 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
-    mNameToService[name] = Service {
+    auto entry = mNameToService.emplace(name, Service {
         .binder = binder,
         .allowIsolated = allowIsolated,
         .dumpPriority = dumpPriority,
-    };
+        .debugPid = ctx.debugPid,
+    });
 
-    auto it = mNameToCallback.find(name);
-    if (it != mNameToCallback.end()) {
+    auto it = mNameToRegistrationCallback.find(name);
+    if (it != mNameToRegistrationCallback.end()) {
         for (const sp<IServiceCallback>& cb : it->second) {
+            entry.first->second.guaranteeClient = true;
             // permission checked in registerForNotifications
             cb->onRegistration(name, binder);
         }
@@ -247,7 +259,7 @@
         return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
-    mNameToCallback[name].push_back(callback);
+    mNameToRegistrationCallback[name].push_back(callback);
 
     if (auto it = mNameToService.find(name); it != mNameToService.end()) {
         const sp<IBinder>& binder = it->second.binder;
@@ -269,9 +281,9 @@
 
     bool found = false;
 
-    auto it = mNameToCallback.find(name);
-    if (it != mNameToCallback.end()) {
-        removeCallback(IInterface::asBinder(callback), &it, &found);
+    auto it = mNameToRegistrationCallback.find(name);
+    if (it != mNameToRegistrationCallback.end()) {
+        removeRegistrationCallback(IInterface::asBinder(callback), &it, &found);
     }
 
     if (!found) {
@@ -297,8 +309,8 @@
     return Status::ok();
 }
 
-void ServiceManager::removeCallback(const wp<IBinder>& who,
-                                    CallbackMap::iterator* it,
+void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who,
+                                    ServiceCallbackMap::iterator* it,
                                     bool* found) {
     std::vector<sp<IServiceCallback>>& listeners = (*it)->second;
 
@@ -312,7 +324,7 @@
     }
 
     if (listeners.empty()) {
-        *it = mNameToCallback.erase(*it);
+        *it = mNameToRegistrationCallback.erase(*it);
     } else {
         (*it)++;
     }
@@ -327,8 +339,12 @@
         }
     }
 
-    for (auto it = mNameToCallback.begin(); it != mNameToCallback.end();) {
-        removeCallback(who, &it, nullptr /*found*/);
+    for (auto it = mNameToRegistrationCallback.begin(); it != mNameToRegistrationCallback.end();) {
+        removeRegistrationCallback(who, &it, nullptr /*found*/);
+    }
+
+    for (auto it = mNameToClientCallback.begin(); it != mNameToClientCallback.end();) {
+        removeClientCallback(who, &it);
     }
 }
 
@@ -341,4 +357,183 @@
     }).detach();
 }
 
-}  // namespace android
+Status ServiceManager::registerClientCallback(const std::string& name, const sp<IBinder>& service,
+                                              const sp<IClientCallback>& cb) {
+    if (cb == nullptr) {
+        return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+    }
+
+    auto ctx = mAccess->getCallingContext();
+    if (!mAccess->canAdd(ctx, name)) {
+        return Status::fromExceptionCode(Status::EX_SECURITY);
+    }
+
+    auto serviceIt = mNameToService.find(name);
+    if (serviceIt == mNameToService.end()) {
+        LOG(ERROR) << "Could not add callback for nonexistent service: " << name;
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+    }
+
+    if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
+        LOG(WARNING) << "Only a server can register for client callbacks (for " << name << ")";
+        return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+    }
+
+    if (serviceIt->second.binder != service) {
+        LOG(WARNING) << "Tried to register client callback for " << name
+            << " but a different service is registered under this name.";
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+    }
+
+    if (OK != IInterface::asBinder(cb)->linkToDeath(this)) {
+        LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name;
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+    }
+
+    mNameToClientCallback[name].push_back(cb);
+
+    return Status::ok();
+}
+
+void ServiceManager::removeClientCallback(const wp<IBinder>& who,
+                                          ClientCallbackMap::iterator* it) {
+    std::vector<sp<IClientCallback>>& listeners = (*it)->second;
+
+    for (auto lit = listeners.begin(); lit != listeners.end();) {
+        if (IInterface::asBinder(*lit) == who) {
+            lit = listeners.erase(lit);
+        } else {
+            ++lit;
+        }
+    }
+
+    if (listeners.empty()) {
+        *it = mNameToClientCallback.erase(*it);
+    } else {
+        (*it)++;
+    }
+}
+
+ssize_t ServiceManager::Service::getNodeStrongRefCount() {
+    sp<BpBinder> bpBinder = binder->remoteBinder();
+    if (bpBinder == nullptr) return -1;
+
+    return ProcessState::self()->getStrongRefCountForNodeByHandle(bpBinder->handle());
+}
+
+void ServiceManager::handleClientCallbacks() {
+    for (const auto& [name, service] : mNameToService) {
+        handleServiceClientCallback(name);
+    }
+}
+
+ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName) {
+    auto serviceIt = mNameToService.find(serviceName);
+    if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) {
+        return -1;
+    }
+
+    Service& service = serviceIt->second;
+    ssize_t count = service.getNodeStrongRefCount();
+
+    // binder driver doesn't support this feature
+    if (count == -1) return count;
+
+    bool hasClients = count > 1; // this process holds a strong count
+
+    if (service.guaranteeClient) {
+        // we have no record of this client
+        if (!service.hasClients && !hasClients) {
+            sendClientCallbackNotifications(serviceName, true);
+        }
+
+        // guarantee is temporary
+        service.guaranteeClient = false;
+    }
+
+    if (hasClients && !service.hasClients) {
+        // client was retrieved in some other way
+        sendClientCallbackNotifications(serviceName, true);
+    }
+
+    // there are no more clients, but the callback has not been called yet
+    if (!hasClients && service.hasClients) {
+        sendClientCallbackNotifications(serviceName, false);
+    }
+
+    return count;
+}
+
+void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, bool hasClients) {
+    auto serviceIt = mNameToService.find(serviceName);
+    if (serviceIt == mNameToService.end()) {
+        LOG(WARNING) << "sendClientCallbackNotifications could not find service " << serviceName;
+        return;
+    }
+    Service& service = serviceIt->second;
+
+    CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients
+        << " so we can't tell clients again that we have client: " << hasClients;
+
+    LOG(INFO) << "Notifying " << serviceName << " they have clients: " << hasClients;
+
+    auto ccIt = mNameToClientCallback.find(serviceName);
+    CHECK(ccIt != mNameToClientCallback.end())
+        << "sendClientCallbackNotifications could not find callbacks for service ";
+
+    for (const auto& callback : ccIt->second) {
+        callback->onClients(service.binder, hasClients);
+    }
+
+    service.hasClients = hasClients;
+}
+
+Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IBinder>& binder) {
+    if (binder == nullptr) {
+        return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+    }
+
+    auto ctx = mAccess->getCallingContext();
+    if (!mAccess->canAdd(ctx, name)) {
+        return Status::fromExceptionCode(Status::EX_SECURITY);
+    }
+
+    auto serviceIt = mNameToService.find(name);
+    if (serviceIt == mNameToService.end()) {
+        LOG(WARNING) << "Tried to unregister " << name
+            << ", but that service wasn't registered to begin with.";
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+    }
+
+    if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
+        LOG(WARNING) << "Only a server can unregister itself (for " << name << ")";
+        return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+    }
+
+    sp<IBinder> storedBinder = serviceIt->second.binder;
+
+    if (binder != storedBinder) {
+        LOG(WARNING) << "Tried to unregister " << name
+            << ", but a different service is registered under this name.";
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+    }
+
+    int clients = handleServiceClientCallback(name);
+
+    // clients < 0: feature not implemented or other error. Assume clients.
+    // Otherwise:
+    // - kernel driver will hold onto one refcount (during this transaction)
+    // - servicemanager has a refcount (guaranteed by this transaction)
+    // So, if clients > 2, then at least one other service on the system must hold a refcount.
+    if (clients < 0 || clients > 2) {
+        // client callbacks are either disabled or there are other clients
+        LOG(INFO) << "Tried to unregister " << name << " but there are clients: " << clients;
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+    }
+
+    mNameToService.erase(name);
+
+    return Status::ok();
+}
+
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 7dcdaa4..77f5250 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -17,12 +17,14 @@
 #pragma once
 
 #include <android/os/BnServiceManager.h>
+#include <android/os/IClientCallback.h>
 #include <android/os/IServiceCallback.h>
 
 #include "Access.h"
 
 namespace android {
 
+using os::IClientCallback;
 using os::IServiceCallback;
 
 class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient {
@@ -40,9 +42,13 @@
                                             const sp<IServiceCallback>& callback) override;
     binder::Status unregisterForNotifications(const std::string& name,
                                               const sp<IServiceCallback>& callback) override;
-    binder::Status isDeclared(const std::string& name, bool* outReturn) override;
 
+    binder::Status isDeclared(const std::string& name, bool* outReturn) override;
+    binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service,
+                                          const sp<IClientCallback>& cb) override;
+    binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override;
     void binderDied(const wp<IBinder>& who) override;
+    void handleClientCallbacks();
 
 protected:
     virtual void tryStartService(const std::string& name);
@@ -52,20 +58,35 @@
         sp<IBinder> binder; // not null
         bool allowIsolated;
         int32_t dumpPriority;
+        bool hasClients = false; // notifications sent on true -> false.
+        bool guaranteeClient = false; // forces the client check to true
+        pid_t debugPid = 0; // the process in which this service runs
+
+        // the number of clients of the service, including servicemanager itself
+        ssize_t getNodeStrongRefCount();
     };
 
-    using CallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>;
+    using ServiceCallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>;
+    using ClientCallbackMap = std::map<std::string, std::vector<sp<IClientCallback>>>;
     using ServiceMap = std::map<std::string, Service>;
 
-    // removes a callback from mNameToCallback, removing it if the vector is empty
+    // removes a callback from mNameToRegistrationCallback, removing it if the vector is empty
     // this updates iterator to the next location
-    void removeCallback(const wp<IBinder>& who,
-                        CallbackMap::iterator* it,
+    void removeRegistrationCallback(const wp<IBinder>& who,
+                        ServiceCallbackMap::iterator* it,
                         bool* found);
+    ssize_t handleServiceClientCallback(const std::string& serviceName);
+     // Also updates mHasClients (of what the last callback was)
+    void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients);
+    // removes a callback from mNameToClientCallback, deleting the entry if the vector is empty
+    // this updates the iterator to the next location
+    void removeClientCallback(const wp<IBinder>& who, ClientCallbackMap::iterator* it);
+
     sp<IBinder> tryGetService(const std::string& name, bool startIfNotFound);
 
-    CallbackMap mNameToCallback;
     ServiceMap mNameToService;
+    ServiceCallbackMap mNameToRegistrationCallback;
+    ClientCallbackMap mNameToClientCallback;
 
     std::unique_ptr<Access> mAccess;
 };
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 4b12fc6..2618906 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -18,18 +18,101 @@
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <binder/Status.h>
+#include <sys/timerfd.h>
+#include <utils/Looper.h>
 #include <utils/StrongPointer.h>
 
 #include "Access.h"
 #include "ServiceManager.h"
 
 using ::android::Access;
+using ::android::sp;
+using ::android::Looper;
+using ::android::LooperCallback;
+using ::android::ProcessState;
 using ::android::IPCThreadState;
 using ::android::ProcessState;
 using ::android::ServiceManager;
 using ::android::os::IServiceManager;
 using ::android::sp;
 
+class BinderCallback : public LooperCallback {
+public:
+    static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
+        sp<BinderCallback> cb = new BinderCallback;
+
+        int binder_fd = -1;
+        IPCThreadState::self()->setupPolling(&binder_fd);
+        LOG_ALWAYS_FATAL_IF(binder_fd < 0, "Failed to setupPolling: %d", binder_fd);
+
+        // Flush after setupPolling(), to make sure the binder driver
+        // knows about this thread handling commands.
+        IPCThreadState::self()->flushCommands();
+
+        int ret = looper->addFd(binder_fd,
+                                Looper::POLL_CALLBACK,
+                                Looper::EVENT_INPUT,
+                                cb,
+                                nullptr /*data*/);
+        LOG_ALWAYS_FATAL_IF(ret != 1, "Failed to add binder FD to Looper");
+
+        return cb;
+    }
+
+    int handleEvent(int /* fd */, int /* events */, void* /* data */) override {
+        IPCThreadState::self()->handlePolledCommands();
+        return 1;  // Continue receiving callbacks.
+    }
+};
+
+// LooperCallback for IClientCallback
+class ClientCallbackCallback : public LooperCallback {
+public:
+    static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
+        sp<ClientCallbackCallback> cb = new ClientCallbackCallback(manager);
+
+        int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
+        LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);
+
+        itimerspec timespec {
+            .it_interval = {
+                .tv_sec = 5,
+                .tv_nsec = 0,
+            },
+            .it_value = {
+                .tv_sec = 5,
+                .tv_nsec = 0,
+            },
+        };
+
+        int timeRes = timerfd_settime(fdTimer, 0 /*flags*/, &timespec, nullptr);
+        LOG_ALWAYS_FATAL_IF(timeRes < 0, "Failed to timerfd_settime: res: %d err: %d", timeRes, errno);
+
+        int addRes = looper->addFd(fdTimer,
+                                   Looper::POLL_CALLBACK,
+                                   Looper::EVENT_INPUT,
+                                   cb,
+                                   nullptr);
+        LOG_ALWAYS_FATAL_IF(addRes != 1, "Failed to add client callback FD to Looper");
+
+        return cb;
+    }
+
+    int handleEvent(int fd, int /*events*/, void* /*data*/) override {
+        uint64_t expirations;
+        int ret = read(fd, &expirations, sizeof(expirations));
+        if (ret != sizeof(expirations)) {
+            ALOGE("Read failed to callback FD: ret: %d err: %d", ret, errno);
+        }
+
+        mManager->handleClientCallbacks();
+        return 1;  // Continue receiving callbacks.
+    }
+private:
+    ClientCallbackCallback(const sp<ServiceManager>& manager) : mManager(manager) {}
+    sp<ServiceManager> mManager;
+};
+
 int main(int argc, char** argv) {
     if (argc > 2) {
         LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
@@ -49,7 +132,14 @@
     IPCThreadState::self()->setTheContextObject(manager);
     ps->becomeContextManager(nullptr, nullptr);
 
-    IPCThreadState::self()->joinThreadPool();
+    sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
+
+    BinderCallback::setupTo(looper);
+    ClientCallbackCallback::setupTo(looper, manager);
+
+    while(true) {
+        looper->pollAll(-1);
+    }
 
     // should not be reached
     return EXIT_FAILURE;
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 792ff91..7f2f949 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -51,6 +51,7 @@
         RelativeParentChange        relative_parent         = 18;
         DetachChildrenChange        detach_children         = 19;
         ReparentChildrenChange      reparent_children       = 20;
+        ShadowRadiusChange          shadow_radius           = 22;
     }
 }
 
@@ -199,3 +200,7 @@
 message DetachChildrenChange {
     required bool detach_children = 1;
 }
+
+message ShadowRadiusChange {
+    required float radius = 1;
+}
\ No newline at end of file
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index a4a9b6a..0d6c31e 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -424,6 +424,9 @@
             case SurfaceChange::SurfaceChangeCase::kDetachChildren:
                 setDetachChildrenChange(transaction, change.id(), change.detach_children());
                 break;
+            case SurfaceChange::SurfaceChangeCase::kShadowRadius:
+                setShadowRadiusChange(transaction, change.id(), change.shadow_radius());
+                break;
             default:
                 status = 1;
                 break;
@@ -724,3 +727,8 @@
     }
     t.reparentChildren(mLayers[id], mLayers[c.parent_id()]->getHandle());
 }
+
+void Replayer::setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
+        layer_id id, const ShadowRadiusChange& c) {
+    t.setShadowRadius(mLayers[id], c.radius());
+}
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index 3b94618..b547834 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -118,6 +118,8 @@
             layer_id id, const DetachChildrenChange& c);
     void setReparentChildrenChange(SurfaceComposerClient::Transaction& t,
             layer_id id, const ReparentChildrenChange& c);
+    void setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
+            layer_id id, const ShadowRadiusChange& c);
 
     void setDisplaySurface(SurfaceComposerClient::Transaction& t,
             display_id id, const DispSurfaceChange& dsc);
diff --git a/data/etc/android.hardware.reboot_escrow.xml b/data/etc/android.hardware.reboot_escrow.xml
new file mode 100644
index 0000000..5db9f4c
--- /dev/null
+++ b/data/etc/android.hardware.reboot_escrow.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!--
+     This is the feature indicating that the device has support for the
+     RebootEscrow HAL.
+-->
+
+<permissions>
+    <feature name="android.hardware.reboot_escrow" />
+</permissions>
diff --git a/data/etc/android.hardware.telephony.cdma.xml b/data/etc/android.hardware.telephony.cdma.xml
index 082378d..b598f68 100644
--- a/data/etc/android.hardware.telephony.cdma.xml
+++ b/data/etc/android.hardware.telephony.cdma.xml
@@ -18,4 +18,5 @@
 <permissions>
     <feature name="android.hardware.telephony" />
     <feature name="android.hardware.telephony.cdma" />
+    <feature name="android.hardware.telephony.data" />
 </permissions>
diff --git a/data/etc/android.hardware.telephony.gsm.xml b/data/etc/android.hardware.telephony.gsm.xml
index 7927fa8..fe8a5cf 100644
--- a/data/etc/android.hardware.telephony.gsm.xml
+++ b/data/etc/android.hardware.telephony.gsm.xml
@@ -18,4 +18,5 @@
 <permissions>
     <feature name="android.hardware.telephony" />
     <feature name="android.hardware.telephony.gsm" />
+    <feature name="android.hardware.telephony.data" />
 </permissions>
diff --git a/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png
new file mode 100644
index 0000000..b6c0765
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png
new file mode 100644
index 0000000..4e975e5
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png
new file mode 100644
index 0000000..a331095
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png
new file mode 100644
index 0000000..41e6668
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png
Binary files differ
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 3310722..05f4340 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -645,10 +645,14 @@
  */
 void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong);
 
+#if __ANDROID_API__ >= 30
 /**
  * Return the current ACONFIGURATION_SCREENROUND_* set in the configuration.
+ *
+ * Available since API level 30.
  */
-int32_t AConfiguration_getScreenRound(AConfiguration* config);
+int32_t AConfiguration_getScreenRound(AConfiguration* config) __INTRODUCED_IN(30);
+#endif
 
 /**
  * Set the current screen round in the configuration.
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index eaa562b..b327d76 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -405,13 +405,12 @@
     { nullptr, 0 }
 };
 
-static const InputEventLabel FLAGS[] = {
-    DEFINE_FLAG(VIRTUAL),
-    DEFINE_FLAG(FUNCTION),
-    DEFINE_FLAG(GESTURE),
+static const InputEventLabel FLAGS[] = {DEFINE_FLAG(VIRTUAL),
+                                        DEFINE_FLAG(FUNCTION),
+                                        DEFINE_FLAG(GESTURE),
+                                        DEFINE_FLAG(WAKE),
 
-    { nullptr, 0 }
-};
+                                        {nullptr, 0}};
 
 static int lookupValueByLabel(const char* literal, const InputEventLabel *list) {
     while (list->literal) {
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 916af69..c44db51 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -63,7 +63,6 @@
         FLAG_DISMISS_KEYGUARD = 0x00400000,
         FLAG_SPLIT_TOUCH = 0x00800000,
         FLAG_SLIPPERY = 0x20000000,
-        FLAG_NEEDS_MENU_KEY = 0x40000000,
     };
 
     // Window types from WindowManager.LayoutParams
@@ -120,7 +119,11 @@
     /* These values are filled in by the WM and passed through SurfaceFlinger
      * unless specified otherwise.
      */
+    // This value should NOT be used to uniquely identify the window. There may be different
+    // input windows that have the same token.
     sp<IBinder> token;
+    // This uniquely identifies the input window.
+    int32_t id = 0;
     std::string name;
     int32_t layoutParamsFlags;
     int32_t layoutParamsType;
@@ -158,7 +161,6 @@
     bool hasFocus;
     bool hasWallpaper;
     bool paused;
-    int32_t layer;
     int32_t ownerPid;
     int32_t ownerUid;
     int32_t inputFeatures;
@@ -204,6 +206,8 @@
 
     sp<IBinder> getToken() const;
 
+    int32_t getId() const { return mInfo.id; }
+
     sp<IBinder> getApplicationToken() {
         return mInfo.applicationInfo.token;
     }
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
index 6479109..a9c2311 100644
--- a/libs/adbd_auth/adbd_auth.cpp
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -178,6 +178,10 @@
 
           this->callbacks_.key_authorized(arg, id);
           this->dispatched_prompt_ = std::nullopt;
+
+          // We need to dispatch pending prompts here upon success as well,
+          // since we might have multiple queued prompts.
+          DispatchPendingPrompt();
         } else if (packet[0] == 'N' && packet[1] == 'O') {
           CHECK_EQ(2UL, packet.length());
           // TODO: Do we want a callback if the key is denied?
diff --git a/libs/android_runtime_lazy/android_runtime_lazy.cpp b/libs/android_runtime_lazy/android_runtime_lazy.cpp
index 98d8e8a..8062be6 100644
--- a/libs/android_runtime_lazy/android_runtime_lazy.cpp
+++ b/libs/android_runtime_lazy/android_runtime_lazy.cpp
@@ -15,6 +15,7 @@
  */
 #define LOG_TAG "ANDROID_RUNTIME_LAZY"
 #include "android_runtime/AndroidRuntime.h"
+#include "android_os_Parcel.h"
 #include "android_util_Binder.h"
 
 #include <dlfcn.h>
@@ -28,12 +29,18 @@
 std::once_flag loadFlag;
 
 typedef JNIEnv* (*getJNIEnv_t)();
+
+// android_util_Binder.h
 typedef sp<IBinder> (*ibinderForJavaObject_t)(JNIEnv* env, jobject obj);
 typedef jobject (*javaObjectForIBinder_t)(JNIEnv* env, const sp<IBinder>& val);
 
+// android_os_Parcel.h
+typedef Parcel* (*parcelForJavaObject_t)(JNIEnv* env, jobject obj);
+
 getJNIEnv_t _getJNIEnv;
 ibinderForJavaObject_t _ibinderForJavaObject;
 javaObjectForIBinder_t _javaObjectForIBinder;
+parcelForJavaObject_t _parcelForJavaObject;
 
 void load() {
     std::call_once(loadFlag, []() {
@@ -64,6 +71,13 @@
             ALOGW("Could not find javaObjectForIBinder.");
             // no return
         }
+
+        _parcelForJavaObject = reinterpret_cast<parcelForJavaObject_t>(
+            dlsym(handle, "_ZN7android19parcelForJavaObjectEP7_JNIEnvP8_jobject"));
+        if (_parcelForJavaObject == nullptr) {
+            ALOGW("Could not find parcelForJavaObject.");
+            // no return
+        }
     });
 }
 
@@ -95,4 +109,12 @@
     return _javaObjectForIBinder(env, val);
 }
 
+Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) {
+    load();
+    if (_parcelForJavaObject == nullptr) {
+        return nullptr;
+    }
+    return _parcelForJavaObject(env, obj);
+}
+
 } // namespace android
diff --git a/libs/android_runtime_lazy/include/android_os_Parcel.h b/libs/android_runtime_lazy/include/android_os_Parcel.h
new file mode 100644
index 0000000..19b094d
--- /dev/null
+++ b/libs/android_runtime_lazy/include/android_os_Parcel.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <binder/Parcel.h>
+#include "jni.h"
+
+namespace android {
+
+// The name of this file is same with the file in frameworks/base/core/jni/
+// This is intentional to make the client use these exported functions
+// in the same way with the original.
+
+Parcel* parcelForJavaObject(JNIEnv* env, jobject obj);
+
+} // namespace android
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 7ee4882..079dd82 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -90,6 +90,7 @@
         "IResultReceiver.cpp",
         "IServiceManager.cpp",
         "IShellCallback.cpp",
+        "LazyServiceRegistrar.cpp",
         "MemoryBase.cpp",
         "MemoryDealer.cpp",
         "MemoryHeapBase.cpp",
@@ -160,6 +161,7 @@
     name: "libbinder_aidl",
     srcs: [
         "aidl/android/content/pm/IPackageManagerNative.aidl",
+        "aidl/android/os/IClientCallback.aidl",
         "aidl/android/os/IServiceCallback.aidl",
         "aidl/android/os/IServiceManager.aidl",
     ],
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 4f0b7d3..aeca12b 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -44,16 +44,16 @@
 }  // namespace
 
 static String16 _appops("appops");
-static pthread_mutex_t gTokenMutex = PTHREAD_MUTEX_INITIALIZER;
-static sp<IBinder> gToken;
+static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER;
+static sp<IBinder> gClientId;
 
-static const sp<IBinder>& getToken(const sp<IAppOpsService>& service) {
-    pthread_mutex_lock(&gTokenMutex);
-    if (gToken == nullptr || gToken->pingBinder() != NO_ERROR) {
-        gToken = service->getToken(new BBinder());
+static const sp<IBinder>& getClientId() {
+    pthread_mutex_lock(&gClientIdMutex);
+    if (gClientId == nullptr) {
+        gClientId = new BBinder();
     }
-    pthread_mutex_unlock(&gTokenMutex);
-    return gToken;
+    pthread_mutex_unlock(&gClientIdMutex);
+    return gClientId;
 }
 
 thread_local uint64_t notedAppOpsInThisBinderTransaction[2];
@@ -123,13 +123,10 @@
         const std::unique_ptr<String16>& featureId, const String16& message) {
     sp<IAppOpsService> service = getService();
     int32_t mode = service != nullptr
-            ? service->noteOperation(op, uid, callingPackage, featureId)
+            ? service->noteOperation(op, uid, callingPackage, featureId, shouldCollectNotes(op),
+                    message)
             : APP_OPS_MANAGER_UNAVAILABLE_MODE;
 
-    if (mode == AppOpsManager::MODE_ALLOWED) {
-        markAppOpNoted(uid, callingPackage, op, featureId, message);
-    }
-
     return mode;
 }
 
@@ -144,12 +141,9 @@
         const String16& message) {
     sp<IAppOpsService> service = getService();
     int32_t mode = service != nullptr
-            ? service->startOperation(getToken(service), op, uid, callingPackage,
-                    featureId, startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE;
-
-    if (mode == AppOpsManager::MODE_ALLOWED) {
-        markAppOpNoted(uid, callingPackage, op, featureId, message);
-    }
+            ? service->startOperation(getClientId(), op, uid, callingPackage,
+                    featureId, startIfModeDefault, shouldCollectNotes(op), message)
+            : APP_OPS_MANAGER_UNAVAILABLE_MODE;
 
     return mode;
 }
@@ -162,7 +156,7 @@
         const std::unique_ptr<String16>& callingFeatureId) {
     sp<IAppOpsService> service = getService();
     if (service != nullptr) {
-        service->finishOperation(getToken(service), op, uid, callingPackage, callingFeatureId);
+        service->finishOperation(getClientId(), op, uid, callingPackage, callingFeatureId);
     }
 }
 
@@ -196,40 +190,17 @@
     }
 }
 
+// check it the appops needs to be collected and cache result
 bool AppOpsManager::shouldCollectNotes(int32_t opcode) {
-    sp<IAppOpsService> service = getService();
-    if (service != nullptr) {
-        return service->shouldCollectNotes(opcode);
-    }
-    return false;
-}
-
-void AppOpsManager::markAppOpNoted(int32_t uid, const String16& packageName, int32_t opCode,
-         const std::unique_ptr<String16>& featureId, const String16& message) {
-    // check it the appops needs to be collected and cache result
-    if (appOpsToNote[opCode] == 0) {
-        if (shouldCollectNotes(opCode)) {
-            appOpsToNote[opCode] = 2;
+    if (appOpsToNote[opcode] == 0) {
+        if (getService()->shouldCollectNotes(opcode)) {
+            appOpsToNote[opcode] = 2;
         } else {
-            appOpsToNote[opCode] = 1;
+            appOpsToNote[opcode] = 1;
         }
     }
 
-    if (appOpsToNote[opCode] != 2) {
-        return;
-    }
-
-    noteAsyncOp(std::unique_ptr<String16>(), uid, packageName, opCode, featureId, message);
-}
-
-void AppOpsManager::noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
-         const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
-         const String16& message) {
-    sp<IAppOpsService> service = getService();
-    if (service != nullptr) {
-        return service->noteAsyncOp(callingPackageName, uid, packageName, opCode, featureId,
-                message);
-    }
+    return appOpsToNote[opcode] == 2;
 }
 
 } // namespace android
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index b85a5f2..a5555a3 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -47,13 +47,16 @@
     }
 
     virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
-                const std::unique_ptr<String16>& featureId) {
+                const std::unique_ptr<String16>& featureId, bool shouldCollectAsyncNotedOp,
+                const String16& message) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeInt32(code);
         data.writeInt32(uid);
         data.writeString16(packageName);
         data.writeString16(featureId);
+        data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+        data.writeString16(message);
         remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -62,7 +65,7 @@
 
     virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
                 const String16& packageName, const std::unique_ptr<String16>& featureId,
-                bool startIfModeDefault) {
+                bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeStrongBinder(token);
@@ -71,6 +74,8 @@
         data.writeString16(packageName);
         data.writeString16(featureId);
         data.writeInt32(startIfModeDefault ? 1 : 0);
+        data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+        data.writeString16(message);
         remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -106,16 +111,6 @@
         remote()->transact(STOP_WATCHING_MODE_TRANSACTION, data, &reply);
     }
 
-    virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
-        data.writeStrongBinder(clientToken);
-        remote()->transact(GET_TOKEN_TRANSACTION, data, &reply);
-        // fail on exception
-        if (reply.readExceptionCode() != 0) return nullptr;
-        return reply.readStrongBinder();
-    }
-
     virtual int32_t permissionToOpCode(const String16& permission) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -149,20 +144,6 @@
         remote()->transact(SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION, data, &reply);
     }
 
-    virtual void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
-            const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
-            const String16& message) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
-        data.writeString16(callingPackageName);
-        data.writeInt32(uid);
-        data.writeString16(packageName);
-        data.writeInt32(opCode);
-        data.writeString16(featureId);
-        data.writeString16(message);
-        remote()->transact(NOTE_ASYNC_OP_TRANSACTION, data, &reply);
-    }
-
     virtual bool shouldCollectNotes(int32_t opCode) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -203,7 +184,10 @@
             String16 packageName = data.readString16();
             std::unique_ptr<String16> featureId;
             data.readString16(&featureId);
-            int32_t res = noteOperation(code, uid, packageName, featureId);
+            bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+            String16 message = data.readString16();
+            int32_t res = noteOperation(code, uid, packageName, featureId,
+                    shouldCollectAsyncNotedOp, message);
             reply->writeNoException();
             reply->writeInt32(res);
             return NO_ERROR;
@@ -217,8 +201,10 @@
             std::unique_ptr<String16> featureId;
             data.readString16(&featureId);
             bool startIfModeDefault = data.readInt32() == 1;
+            bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+            String16 message = data.readString16();
             int32_t res = startOperation(token, code, uid, packageName, featureId,
-                    startIfModeDefault);
+                    startIfModeDefault, shouldCollectAsyncNotedOp, message);
             reply->writeNoException();
             reply->writeInt32(res);
             return NO_ERROR;
@@ -251,14 +237,6 @@
             reply->writeNoException();
             return NO_ERROR;
         } break;
-        case GET_TOKEN_TRANSACTION: {
-            CHECK_INTERFACE(IAppOpsService, data, reply);
-            sp<IBinder> clientToken = data.readStrongBinder();
-            sp<IBinder> token = getToken(clientToken);
-            reply->writeNoException();
-            reply->writeStrongBinder(token);
-            return NO_ERROR;
-        } break;
         case PERMISSION_TO_OP_CODE_TRANSACTION: {
             CHECK_INTERFACE(IAppOpsService, data, reply);
             String16 permission = data.readString16();
@@ -285,20 +263,6 @@
             reply->writeNoException();
             return NO_ERROR;
         } break;
-        case NOTE_ASYNC_OP_TRANSACTION: {
-            CHECK_INTERFACE(IAppOpsService, data, reply);
-            std::unique_ptr<String16> callingPackageName;
-            data.readString16(&callingPackageName);
-            int32_t uid = data.readInt32();
-            String16 packageName = data.readString16();
-            int32_t opCode = data.readInt32();
-            std::unique_ptr<String16> featureId;
-            data.readString16(&featureId);
-            String16 message = data.readString16();
-            noteAsyncOp(callingPackageName, uid, packageName, opCode, featureId, message);
-            reply->writeNoException();
-            return NO_ERROR;
-        } break;
         case SHOULD_COLLECT_NOTES_TRANSACTION: {
             CHECK_INTERFACE(IAppOpsService, data, reply);
             int32_t opCode = data.readInt32();
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index bac8b66..5ca9156 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -271,6 +271,8 @@
             std::unique_lock<std::mutex> lock(mMutex);
             mBinder = binder;
             lock.unlock();
+            // Flushing here helps ensure the service's ref count remains accurate
+            IPCThreadState::self()->flushCommands();
             mCv.notify_one();
             return Status::ok();
         }
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
new file mode 100644
index 0000000..dc9482c
--- /dev/null
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -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.
+ */
+
+#define LOG_TAG "AidlLazyServiceRegistrar"
+
+#include <binder/LazyServiceRegistrar.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <android/os/BnClientCallback.h>
+#include <android/os/IServiceManager.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace binder {
+namespace internal {
+
+using AidlServiceManager = android::os::IServiceManager;
+
+class ClientCounterCallback : public ::android::os::BnClientCallback {
+public:
+    ClientCounterCallback() : mNumConnectedServices(0) {}
+
+    bool registerService(const sp<IBinder>& service, const std::string& name,
+                         bool allowIsolated, int dumpFlags);
+
+protected:
+    Status onClients(const sp<IBinder>& service, bool clients) override;
+
+private:
+    /**
+     * Unregisters all services that we can. If we can't unregister all, re-register other
+     * services.
+     */
+    void tryShutdown();
+
+    /**
+     * Counter of the number of services that currently have at least one client.
+     */
+    size_t mNumConnectedServices;
+
+    struct Service {
+        sp<IBinder> service;
+        std::string name;
+        bool allowIsolated;
+        int dumpFlags;
+    };
+    /**
+     * Number of services that have been registered.
+     */
+    std::vector<Service> mRegisteredServices;
+};
+
+bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
+                                            bool allowIsolated, int dumpFlags) {
+    auto manager = interface_cast<AidlServiceManager>(
+                    ProcessState::self()->getContextObject(nullptr));
+
+    ALOGI("Registering service %s", name.c_str());
+
+    if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) {
+        ALOGE("Failed to register service %s", name.c_str());
+        return false;
+    }
+
+    if (!manager->registerClientCallback(name, service, this).isOk())
+    {
+      ALOGE("Failed to add client callback for service %s", name.c_str());
+      return false;
+    }
+
+    mRegisteredServices.push_back({service, name, allowIsolated, dumpFlags});
+
+    return true;
+}
+
+/**
+ * onClients is oneway, so no need to worry about multi-threading. Note that this means multiple
+ * invocations could occur on different threads however.
+ */
+Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) {
+    if (clients) {
+        mNumConnectedServices++;
+    } else {
+        mNumConnectedServices--;
+    }
+
+    ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d",
+          mNumConnectedServices, mRegisteredServices.size(),
+          String8(service->getInterfaceDescriptor()).string(), clients);
+
+    if (mNumConnectedServices == 0) {
+        tryShutdown();
+    }
+
+    return Status::ok();
+}
+
+void ClientCounterCallback::tryShutdown() {
+    ALOGI("Trying to shut down the service. No clients in use for any service in process.");
+
+    // This makes the same assumption as IServiceManager.cpp. Could dedupe if used in more places.
+    auto manager = interface_cast<AidlServiceManager>(
+            ProcessState::self()->getContextObject(nullptr));
+
+    auto unRegisterIt = mRegisteredServices.begin();
+    for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) {
+        auto& entry = (*unRegisterIt);
+
+        bool success = manager->tryUnregisterService(entry.name, entry.service).isOk();
+
+        if (!success) {
+            ALOGI("Failed to unregister service %s", entry.name.c_str());
+            break;
+        }
+    }
+
+    if (unRegisterIt == mRegisteredServices.end()) {
+        ALOGI("Unregistered all clients and exiting");
+        exit(EXIT_SUCCESS);
+    }
+
+    for (auto reRegisterIt = mRegisteredServices.begin(); reRegisterIt != unRegisterIt;
+         reRegisterIt++) {
+        auto& entry = (*reRegisterIt);
+
+        // re-register entry
+        if (!registerService(entry.service, entry.name, entry.allowIsolated, entry.dumpFlags)) {
+            // Must restart. Otherwise, clients will never be able to get a hold of this service.
+            ALOGE("Bad state: could not re-register services");
+        }
+    }
+}
+
+}  // namespace internal
+
+LazyServiceRegistrar::LazyServiceRegistrar() {
+    mClientCC = std::make_shared<internal::ClientCounterCallback>();
+}
+
+LazyServiceRegistrar& LazyServiceRegistrar::getInstance() {
+    static auto registrarInstance = new LazyServiceRegistrar();
+    return *registrarInstance;
+}
+
+status_t LazyServiceRegistrar::registerService(const sp<IBinder>& service, const std::string& name,
+                                               bool allowIsolated, int dumpFlags) {
+    if (!mClientCC->registerService(service, name, allowIsolated, dumpFlags)) {
+        return UNKNOWN_ERROR;
+    }
+    return OK;
+}
+
+}  // namespace hardware
+}  // namespace android
\ No newline at end of file
diff --git a/libs/binder/aidl/android/os/IClientCallback.aidl b/libs/binder/aidl/android/os/IClientCallback.aidl
new file mode 100644
index 0000000..36d7ee6
--- /dev/null
+++ b/libs/binder/aidl/android/os/IClientCallback.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+/**
+ * @hide
+ */
+oneway interface IClientCallback {
+    /**
+     * This is called when there is a transition between having >= 1 clients and having 0 clients
+     * (or vice versa).
+     *
+     * Upon receiving hasClients false, if the process decides to exit, it is recommended to try to
+     * unregister using IServiceManager's tryUnregister before quitting in case another client
+     * associates.
+     *
+     * @param registered binder 'server' registered with IServiceManager's registerClientCallback
+     * @param hasClients whether there are currently clients
+     *     true - when there are >= 1 clients. This must be called as soon as IServiceManager::get
+     *         is called (no race).
+     *     false - when there are 0 clients. This may be delayed if it is thought that another
+     *         may be used again soon.
+     */
+    void onClients(IBinder registered, boolean hasClients);
+}
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index b965881..ff15460 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.os.IClientCallback;
 import android.os.IServiceCallback;
 
 /**
@@ -96,4 +97,15 @@
      * manifest.
      */
     boolean isDeclared(@utf8InCpp String name);
+
+    /**
+     * Request a callback when the number of clients of the service changes.
+     * Used by LazyServiceRegistrar to dynamically stop services that have no clients.
+     */
+    void registerClientCallback(@utf8InCpp String name, IBinder service, IClientCallback callback);
+
+    /**
+     * Attempt to unregister and remove a service. Will fail if the service is still in use.
+     */
+    void tryUnregisterService(@utf8InCpp String name, IBinder service);
 }
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 22a0179..5b6eb68 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -151,17 +151,12 @@
     void stopWatchingMode(const sp<IAppOpsCallback>& callback);
     int32_t permissionToOpCode(const String16& permission);
     void setCameraAudioRestriction(int32_t mode);
-    void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
-            const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
-            const String16& message);
 
 private:
     Mutex mLock;
     sp<IAppOpsService> mService;
 
     sp<IAppOpsService> getService();
-    void markAppOpNoted(int32_t uid, const String16& packageName, int32_t opCode,
-            const std::unique_ptr<String16>& featureId, const String16& message);
     bool shouldCollectNotes(int32_t opCode);
 };
 
diff --git a/libs/binder/include/binder/Enums.h b/libs/binder/include/binder/Enums.h
new file mode 100644
index 0000000..aec6f70
--- /dev/null
+++ b/libs/binder/include/binder/Enums.h
@@ -0,0 +1,41 @@
+/*
+ * 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 <iterator>
+#include <type_traits>
+
+namespace android {
+
+namespace internal {
+
+// Never instantiated. Used as a placeholder for template variables.
+template <typename T>
+struct invalid_type;
+
+// AIDL generates specializations of this for enums.
+template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
+constexpr invalid_type<EnumType> enum_values;
+} // namespace internal
+
+// Usage: for (const auto v : enum_range<EnumType>() ) { ... }
+template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
+struct enum_range {
+    constexpr auto begin() const { return std::begin(internal::enum_values<EnumType>); }
+    constexpr auto end() const { return std::end(internal::enum_values<EnumType>); }
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 15ba005..1b4bcce 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -36,23 +36,20 @@
 
     virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
     virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
-            const std::unique_ptr<String16>& featureId) = 0;
+            const std::unique_ptr<String16>& featureId, bool shouldCollectAsyncNotedOp,
+            const String16& message) = 0;
     virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
             const String16& packageName, const std::unique_ptr<String16>& featureId,
-            bool startIfModeDefault) = 0;
+            bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0;
     virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
             const String16& packageName, const std::unique_ptr<String16>& featureId) = 0;
     virtual void startWatchingMode(int32_t op, const String16& packageName,
             const sp<IAppOpsCallback>& callback) = 0;
     virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
-    virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0;
     virtual int32_t permissionToOpCode(const String16& permission) = 0;
     virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid,
             const String16& packageName) = 0;
     virtual void setCameraAudioRestriction(int32_t mode) = 0;
-    virtual void noteAsyncOp(const std::unique_ptr<String16>& callingPackageName, int32_t uid,
-            const String16& packageName, int32_t opCode, const std::unique_ptr<String16>& featureId,
-            const String16& message) = 0;
     virtual bool shouldCollectNotes(int32_t opCode) = 0;
 
     enum {
@@ -62,12 +59,10 @@
         FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3,
         START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4,
         STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
-        GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
-        PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
-        CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
-        NOTE_ASYNC_OP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
-        SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+10,
-        SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+11,
+        PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
+        CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
+        SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
+        SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
     };
 
     enum {
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 79d9b79..3a401ad 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -253,7 +253,6 @@
   "android.media.IDrmClient",
   "android.media.IEffect",
   "android.media.IEffectClient",
-  "android.media.IMediaAnalyticsService",
   "android.media.IMediaCodecList",
   "android.media.IMediaDrmService",
   "android.media.IMediaExtractor",
@@ -262,6 +261,7 @@
   "android.media.IMediaHTTPService",
   "android.media.IMediaLogService",
   "android.media.IMediaMetadataRetriever",
+  "android.media.IMediaMetricsService",
   "android.media.IMediaPlayer",
   "android.media.IMediaPlayerClient",
   "android.media.IMediaPlayerService",
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
new file mode 100644
index 0000000..efdecc4
--- /dev/null
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -0,0 +1,44 @@
+/*
+ * 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 <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace binder {
+namespace internal {
+class ClientCounterCallback;
+}  // namespace internal
+
+/** Exits when all services registered through this object have 0 clients */
+class LazyServiceRegistrar {
+   public:
+     static LazyServiceRegistrar& getInstance();
+     status_t registerService(const sp<IBinder>& service,
+                              const std::string& name = "default",
+                              bool allowIsolated = false,
+                              int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
+
+   private:
+     std::shared_ptr<internal::ClientCounterCallback> mClientCC;
+     LazyServiceRegistrar();
+};
+
+}  // namespace binder
+}  // namespace android
\ No newline at end of file
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index fa07d04..b37db43 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -50,6 +50,7 @@
         "ibinder.cpp",
         "ibinder_jni.cpp",
         "parcel.cpp",
+        "parcel_jni.cpp",
         "process.cpp",
         "stability.cpp",
         "status.cpp",
diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
index 946ccb7..2b61cf1 100644
--- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
@@ -34,6 +34,7 @@
 
 #include <unistd.h>
 #include <cstddef>
+#include <string>
 
 namespace ndk {
 
@@ -228,6 +229,13 @@
      */
     const char* getMessage() const { return AStatus_getMessage(get()); }
 
+    std::string getDescription() const {
+        const char* cStr = AStatus_getDescription(get());
+        std::string ret = cStr;
+        AStatus_deleteDescription(cStr);
+        return ret;
+    }
+
     /**
      * Convenience methods for creating scoped statuses.
      */
diff --git a/libs/binder/ndk/include_ndk/android/binder_enums.h b/libs/binder/ndk/include_ndk/android/binder_enums.h
new file mode 100644
index 0000000..ee819c0
--- /dev/null
+++ b/libs/binder/ndk/include_ndk/android/binder_enums.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_enums.h
+ * @brief Helpers for AIDL enum types.
+ */
+
+#pragma once
+
+#include <iterator>
+#include <type_traits>
+
+namespace ndk {
+
+namespace internal {
+/**
+ * Never instantiated. Used as a placeholder for template variables.
+ */
+template <typename T>
+struct invalid_type;
+
+/**
+ * AIDL generates specializations of this for enums.
+ */
+template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
+constexpr invalid_type<EnumType> enum_values;
+}  // namespace internal
+
+/**
+ * Iterable interface to enumerate all values of AIDL enum types.
+ */
+template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
+struct enum_range {
+    /**
+     * Return an iterator pointing to the first enum value.
+     */
+    constexpr auto begin() const { return std::begin(internal::enum_values<EnumType>); }
+    /**
+     * Return an iterator pointing to one past the last enum value.
+     */
+    constexpr auto end() const { return std::end(internal::enum_values<EnumType>); }
+};
+
+}  // namespace ndk
+
+/** @} */
\ No newline at end of file
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
index be3029c..cd1ff1f 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
@@ -36,13 +36,13 @@
 /**
  * Converts an android.os.IBinder object into an AIBinder* object.
  *
- * If either env or the binder is null, null is returned. If this binder object was originally an
+ * If the binder is null, null is returned. If this binder object was originally an
  * AIBinder object, the original object is returned. The returned object has one refcount
  * associated with it, and so this should be accompanied with an AIBinder_decStrong call.
  *
  * Available since API level 29.
  *
- * \param env Java environment.
+ * \param env Java environment. Must not be null.
  * \param binder android.os.IBinder java object.
  *
  * \return an AIBinder object representing the Java binder object. If either parameter is null, or
@@ -54,12 +54,12 @@
 /**
  * Converts an AIBinder* object into an android.os.IBinder object.
  *
- * If either env or the binder is null, null is returned. If this binder object was originally an
- * IBinder object, the original java object will be returned.
+ * If the binder is null, null is returned. If this binder object was originally an IBinder object,
+ * the original java object will be returned.
  *
  * Available since API level 29.
  *
- * \param env Java environment.
+ * \param env Java environment. Must not be null.
  * \param binder the object to convert.
  *
  * \return an android.os.IBinder object or null if the parameters were null.
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h b/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h
new file mode 100644
index 0000000..65e1704
--- /dev/null
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_parcel_jni.h
+ * @brief Conversions between AParcel and android.os.Parcel
+ */
+
+#pragma once
+
+#include <android/binder_parcel.h>
+
+#include <jni.h>
+
+__BEGIN_DECLS
+#if __ANDROID_API__ >= 30
+
+/**
+ * Converts an android.os.Parcel object into an AParcel* object.
+ *
+ * If the parcel is null, null is returned.
+ *
+ * Available since API level 30.
+ *
+ * \param env Java environment. Must not be null.
+ * \param parcel android.os.Parcel java object.
+ *
+ * \return an AParcel object representing the Java parcel object. If either parameter is null, this
+ * will return null. This must be deleted with AParcel_delete. This does not take ownership of the
+ * jobject and is only good for as long as the jobject is alive.
+ */
+__attribute__((warn_unused_result)) AParcel* AParcel_fromJavaParcel(JNIEnv* env, jobject parcel)
+        __INTRODUCED_IN(30);
+
+#endif  //__ANDROID_API__ >= 30
+__END_DECLS
+
+/** @} */
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
index 7871667..df5df13 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
@@ -421,13 +421,76 @@
 }
 
 /**
+ * Convenience API for writing a non-null parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& p) {
+    binder_status_t status = AParcel_writeInt32(parcel, 1);  // non-null
+    if (status != STATUS_OK) {
+        return status;
+    }
+    return p.writeToParcel(parcel);
+}
+
+/**
+ * Convenience API for reading a non-null parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_readParcelable(const AParcel* parcel, P* p) {
+    int32_t null;
+    binder_status_t status = AParcel_readInt32(parcel, &null);
+    if (status != STATUS_OK) {
+        return status;
+    }
+    if (null == 0) {
+        return STATUS_UNEXPECTED_NULL;
+    }
+    return p->readFromParcel(parcel);
+}
+
+/**
+ * Convenience API for writing a nullable parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel,
+                                                              const std::optional<P>& p) {
+    if (p == std::nullopt) {
+        return AParcel_writeInt32(parcel, 0);  // null
+    }
+    binder_status_t status = AParcel_writeInt32(parcel, 1);  // non-null
+    if (status != STATUS_OK) {
+        return status;
+    }
+    return p->writeToParcel(parcel);
+}
+
+/**
+ * Convenience API for reading a nullable parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel,
+                                                             std::optional<P>* p) {
+    int32_t null;
+    binder_status_t status = AParcel_readInt32(parcel, &null);
+    if (status != STATUS_OK) {
+        return status;
+    }
+    if (null == 0) {
+        *p = std::nullopt;
+        return STATUS_OK;
+    }
+    *p = std::optional<P>(P{});
+    return (*p)->readFromParcel(parcel);
+}
+
+/**
  * Writes a parcelable object of type P inside a std::vector<P> at index 'index' to 'parcel'.
  */
 template <typename P>
 binder_status_t AParcel_writeStdVectorParcelableElement(AParcel* parcel, const void* vectorData,
                                                         size_t index) {
     const std::vector<P>* vector = static_cast<const std::vector<P>*>(vectorData);
-    return vector->at(index).writeToParcel(parcel);
+    return AParcel_writeParcelable(parcel, vector->at(index));
 }
 
 /**
@@ -437,7 +500,7 @@
 binder_status_t AParcel_readStdVectorParcelableElement(const AParcel* parcel, void* vectorData,
                                                        size_t index) {
     std::vector<P>* vector = static_cast<std::vector<P>*>(vectorData);
-    return vector->at(index).readFromParcel(parcel);
+    return AParcel_readParcelable(parcel, &vector->at(index));
 }
 
 /**
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 78d70f8..ab9a144 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -247,6 +247,25 @@
 const char* AStatus_getMessage(const AStatus* status) __INTRODUCED_IN(29);
 
 /**
+ * Get human-readable description for debugging.
+ *
+ * Available since API level 30.
+ *
+ * \param status the status being queried.
+ *
+ * \return a description, must be deleted with AStatus_deleteDescription.
+ */
+__attribute__((warn_unused_result)) const char* AStatus_getDescription(const AStatus* status)
+        __INTRODUCED_IN(30);
+
+/**
+ * Delete description.
+ *
+ * \param description value from AStatus_getDescription
+ */
+void AStatus_deleteDescription(const char* description) __INTRODUCED_IN(30);
+
+/**
  * Deletes memory associated with the status instance.
  *
  * Available since API level 29.
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index d59d6e4..f3158d7 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -103,6 +103,9 @@
   global:
     AIBinder_getExtension;
     AIBinder_setExtension;
+    AStatus_getDescription;
+    AStatus_deleteDescription;
+    AParcel_fromJavaParcel;
 
     AIBinder_markSystemStability; # apex
     AIBinder_markVendorStability; # llndk
diff --git a/libs/binder/ndk/parcel_jni.cpp b/libs/binder/ndk/parcel_jni.cpp
new file mode 100644
index 0000000..53b2d7c
--- /dev/null
+++ b/libs/binder/ndk/parcel_jni.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 <android/binder_parcel_jni.h>
+#include "parcel_internal.h"
+
+#include <android_os_Parcel.h>
+
+using ::android::Parcel;
+using ::android::parcelForJavaObject;
+
+AParcel* AParcel_fromJavaParcel(JNIEnv* env, jobject jbinder) {
+    if (jbinder == nullptr) {
+        return nullptr;
+    }
+
+    Parcel* parcel = parcelForJavaObject(env, jbinder);
+
+    if (parcel == nullptr) {
+        return nullptr;
+    }
+
+    return new AParcel(nullptr /*binder*/, parcel, false /*shouldOwn*/);
+}
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp
index 1f75b0b..87e1341 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -66,6 +66,17 @@
     return status->get()->exceptionMessage().c_str();
 }
 
+const char* AStatus_getDescription(const AStatus* status) {
+    android::String8 description = status->get()->toString8();
+    char* cStr = new char[description.size() + 1];
+    memcpy(cStr, description.c_str(), description.size() + 1);
+    return cStr;
+}
+
+void AStatus_deleteDescription(const char* description) {
+    delete[] const_cast<char*>(description);
+}
+
 void AStatus_delete(AStatus* status) {
     delete status;
 }
diff --git a/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp
index d3ccdc2..ad78e31 100644
--- a/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp
+++ b/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp
@@ -105,7 +105,8 @@
 
         std::string outString;
         ScopedAStatus status = server->RepeatString("foo", &outString);
-        EXPECT_EQ(STATUS_OK, AStatus_getExceptionCode(status.get())) << serviceName;
+        EXPECT_EQ(STATUS_OK, AStatus_getExceptionCode(status.get()))
+                << serviceName << " " << status.getDescription();
         EXPECT_EQ("foo", outString) << serviceName;
     }
 }
diff --git a/libs/gralloc/OWNERS b/libs/gralloc/OWNERS
new file mode 100644
index 0000000..67743cd
--- /dev/null
+++ b/libs/gralloc/OWNERS
@@ -0,0 +1,2 @@
+marissaw@google.com
+vhau@google.com
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index cb5305f..00f7484 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -33,15 +33,16 @@
     ],
 
     shared_libs: [
+        "android.hardware.graphics.common-ndk_platform",
         "android.hardware.graphics.mapper@4.0",
         "libhidlbase",
         "liblog",
-        "vintf-graphics-common-ndk_platform",
     ],
 
     export_shared_lib_headers: [
+        "android.hardware.graphics.common-ndk_platform",
         "android.hardware.graphics.mapper@4.0",
-        "vintf-graphics-common-ndk_platform",
+        "libhidlbase",
     ],
 
     export_include_dirs: [
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 0330dac..4851b44 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "libgralloctypes"
+
 #include <cstring>
 #include <cinttypes>
 #include <limits>
@@ -25,15 +27,22 @@
 
 using android::hardware::hidl_vec;
 
-using ::aidl::android::hardware::graphics::common::BlendMode;
-using ::aidl::android::hardware::graphics::common::Dataspace;
-using ::aidl::android::hardware::graphics::common::PlaneLayout;
-using ::aidl::android::hardware::graphics::common::PlaneLayoutComponent;
-using ::aidl::android::hardware::graphics::common::ExtendableType;
-using ::aidl::android::hardware::graphics::common::Rect;
-using ::aidl::android::hardware::graphics::common::StandardMetadataType;
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::ChromaSiting;
+using aidl::android::hardware::graphics::common::Compression;
+using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::Interlaced;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::Rect;
+using aidl::android::hardware::graphics::common::Smpte2086;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using aidl::android::hardware::graphics::common::XyColor;
 
-using MetadataType = ::android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
 
 namespace android {
 
@@ -61,6 +70,7 @@
             return BAD_VALUE;
         }
         mVec->resize(mNeededResize);
+        mResized = true;
         return NO_ERROR;
     }
 
@@ -68,11 +78,17 @@
         if (!mVec) {
             return BAD_VALUE;
         }
-        if (mVec->size() == 0) {
+        if (!mResized) {
             if (hasAdditionOverflow(mNeededResize, size)) {
                 clear();
                 return BAD_VALUE;
             }
+            /**
+             * Update mNeededResize and return NO_ERROR here because if (!mResized), the
+             * caller hasn't called resize(). No data will be written into the mVec until
+             * the caller resizes. We can't resize here for the caller because hidl_vec::resize()
+             * allocates a new backing array every time.
+             */
             mNeededResize += size;
             return NO_ERROR;
         }
@@ -93,12 +109,14 @@
             mVec->resize(0);
         }
         mNeededResize = 0;
+        mResized = false;
         mOffset = 0;
     }
 
 private:
     hidl_vec<uint8_t>* mVec;
     size_t mNeededResize = 0;
+    size_t mResized = false;
     size_t mOffset = 0;
 };
 
@@ -138,6 +156,13 @@
         if (!mVec) {
             return false;
         }
+        return mVec->size() > mOffset;
+    }
+
+    size_t getRemainingSize() {
+        if (!mVec) {
+            return 0;
+        }
         return mVec->size() - mOffset;
     }
 
@@ -166,6 +191,9 @@
 template<class T>
 using ErrorHandler = void(*)(T*);
 
+status_t encodeMetadataType(const MetadataType& input, OutputHidlVec* output);
+status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType);
+
 /**
  * encode is the main encoding function. It takes in T and uses the encodeHelper function to turn T
  * into the hidl_vec byte stream.
@@ -175,9 +203,16 @@
  * encodes T into the hidl_vec byte stream.
  */
 template <class T>
-status_t encode(const T& input, hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
+status_t encode(const MetadataType& metadataType, const T& input, hidl_vec<uint8_t>* output,
+                EncodeHelper<T> encodeHelper) {
     OutputHidlVec outputHidlVec{output};
-    status_t err = encodeHelper(input, &outputHidlVec);
+
+    status_t err = encodeMetadataType(metadataType, &outputHidlVec);
+    if (err) {
+        return err;
+    }
+
+    err = encodeHelper(input, &outputHidlVec);
     if (err) {
         return err;
     }
@@ -187,19 +222,39 @@
         return err;
     }
 
+    err = encodeMetadataType(metadataType, &outputHidlVec);
+    if (err) {
+        return err;
+    }
+
     return encodeHelper(input, &outputHidlVec);
 }
 
+template <class T>
+status_t encodeOptional(const MetadataType& metadataType, const std::optional<T>& input,
+                        hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
+    if (!input) {
+        return NO_ERROR;
+    }
+    return encode(metadataType, *input, output, encodeHelper);
+}
+
 /**
  * decode is the main decode function. It takes in a hidl_vec and uses the decodeHelper function to
  * turn the hidl_vec byte stream into T. If an error occurs, the errorHandler function cleans up
  * T.
  */
 template <class T>
-status_t decode(const hidl_vec<uint8_t>& input, T* output, DecodeHelper<T> decodeHelper,
-                ErrorHandler<T> errorHandler = nullptr) {
+status_t decode(const MetadataType& metadataType, const hidl_vec<uint8_t>& input, T* output,
+                DecodeHelper<T> decodeHelper, ErrorHandler<T> errorHandler = nullptr) {
     InputHidlVec inputHidlVec{&input};
-    status_t err = decodeHelper(&inputHidlVec, output);
+
+    status_t err = validateMetadataType(&inputHidlVec, metadataType);
+    if (err) {
+        return err;
+    }
+
+    err = decodeHelper(&inputHidlVec, output);
     if (err) {
         return err;
     }
@@ -215,13 +270,32 @@
     return NO_ERROR;
 }
 
+template <class T>
+status_t decodeOptional(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                        std::optional<T>* output, DecodeHelper<T> decodeHelper) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+    if (input.size() <= 0) {
+        output->reset();
+        return NO_ERROR;
+    }
+    T tmp;
+    status_t err = decode(metadataType, input, &tmp, decodeHelper);
+    if (!err) {
+        *output = tmp;
+    }
+    return err;
+}
+
 /**
  * Private helper functions
  */
 template <class T>
 status_t encodeInteger(const T& input, OutputHidlVec* output) {
     static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
-                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value);
+                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+                  std::is_same<T, float>::value || std::is_same<T, double>::value);
     if (!output) {
         return BAD_VALUE;
     }
@@ -233,7 +307,8 @@
 template <class T>
 status_t decodeInteger(InputHidlVec* input, T* output) {
     static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
-                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value);
+                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+                  std::is_same<T, float>::value || std::is_same<T, double>::value);
     if (!output) {
         return BAD_VALUE;
     }
@@ -252,7 +327,7 @@
         return err;
     }
 
-    return output->encode(reinterpret_cast<const uint8_t*>(input.c_str()), input.size());
+    return output->encode(reinterpret_cast<const uint8_t*>(input.data()), input.size());
 }
 
 status_t decodeString(InputHidlVec* input, std::string* output) {
@@ -262,11 +337,46 @@
 
     int64_t size = 0;
     status_t err = decodeInteger<int64_t>(input, &size);
+    if (err) {
+        return err;
+    }
+    if (size < 0) {
+        return BAD_VALUE;
+    }
+
+    return input->decode(output, size);
+}
+
+status_t encodeByteVector(const std::vector<uint8_t>& input, OutputHidlVec* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    status_t err = encodeInteger<int64_t>(input.size(), output);
+    if (err) {
+        return err;
+    }
+
+    return output->encode(input.data(), input.size());
+}
+
+status_t decodeByteVector(InputHidlVec* input, std::vector<uint8_t>* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    int64_t size = 0;
+    status_t err = decodeInteger<int64_t>(input, &size);
     if (err || size < 0) {
         return err;
     }
 
-    return input->decode(output, size);
+    if (size > input->getRemainingSize()) {
+        return BAD_VALUE;
+    }
+    output->resize(size);
+
+    return input->decode(output->data(), size);
 }
 
 status_t encodeExtendableType(const ExtendableType& input, OutputHidlVec* output) {
@@ -305,6 +415,79 @@
     output->value = 0;
 }
 
+status_t encodeMetadataType(const MetadataType& input, OutputHidlVec* output) {
+    status_t err = encodeString(input.name, output);
+    if (err) {
+        return err;
+    }
+
+    err = encodeInteger<int64_t>(input.value, output);
+    if (err) {
+        return err;
+    }
+
+    return NO_ERROR;
+}
+
+status_t decodeMetadataType(InputHidlVec* input, MetadataType* output) {
+    std::string name;
+    status_t err = decodeString(input, &name);
+    if (err) {
+        return err;
+    }
+    output->name = name;
+
+    err = decodeInteger<int64_t>(input, &output->value);
+    if (err) {
+        return err;
+    }
+
+    return NO_ERROR;
+}
+
+status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType) {
+    MetadataType receivedMetadataType;
+
+    status_t err = decodeMetadataType(input, &receivedMetadataType);
+    if (err) {
+        return err;
+    }
+
+    if (expectedMetadataType.name != receivedMetadataType.name) {
+        return BAD_VALUE;
+    }
+
+    if (receivedMetadataType.value != expectedMetadataType.value) {
+        return BAD_VALUE;
+    }
+
+    return NO_ERROR;
+}
+
+status_t encodeXyColor(const XyColor& input, OutputHidlVec* output) {
+    status_t err = encodeInteger<float>(input.x, output);
+    if (err) {
+        return err;
+    }
+    return encodeInteger<float>(input.y, output);
+}
+
+status_t decodeXyColor(InputHidlVec* input, XyColor* output) {
+    status_t err = decodeInteger<float>(input, &output->x);
+    if (err) {
+        return err;
+    }
+    return decodeInteger<float>(input, &output->y);
+}
+
+void clearXyColor(XyColor* output) {
+    if (!output) {
+        return;
+    }
+    output->x = 0;
+    output->y = 0;
+}
+
 status_t encodeRect(const Rect& input, OutputHidlVec* output) {
     status_t err = encodeInteger<int32_t>(static_cast<int32_t>(input.left), output);
     if (err) {
@@ -396,13 +579,17 @@
 
     int64_t size = 0;
     status_t err = decodeInteger<int64_t>(input, &size);
-    if (err || size < 0) {
+    if (err) {
         return err;
     }
+    if (size < 0 || size > 10000) {
+        return BAD_VALUE;
+    }
 
-    for (int i = 0; i < size; i++) {
-        output->emplace_back();
-        err = decodePlaneLayoutComponent(input, &output->back());
+    output->resize(size);
+
+    for (auto& planeLayoutComponent : *output) {
+        err = decodePlaneLayoutComponent(input, &planeLayoutComponent);
         if (err) {
             return err;
         }
@@ -521,9 +708,12 @@
 status_t decodePlaneLayoutsHelper(InputHidlVec* inputHidlVec, std::vector<PlaneLayout>* outPlaneLayouts) {
     int64_t size = 0;
     status_t err = decodeInteger<int64_t>(inputHidlVec, &size);
-    if (err || size < 0) {
+    if (err) {
         return err;
     }
+    if (size < 0) {
+        return BAD_VALUE;
+    }
 
     for (size_t i = 0; i < size; i++) {
         outPlaneLayouts->emplace_back();
@@ -542,153 +732,445 @@
     output->clear();
 }
 
+status_t encodeSmpte2086Helper(const Smpte2086& smpte2086, OutputHidlVec* outOutputHidlVec) {
+    status_t err = encodeXyColor(smpte2086.primaryRed, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    err = encodeXyColor(smpte2086.primaryGreen, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    err = encodeXyColor(smpte2086.primaryBlue, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    err = encodeXyColor(smpte2086.whitePoint, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<float>(smpte2086.maxLuminance, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    return encodeInteger<float>(smpte2086.minLuminance, outOutputHidlVec);
+}
+
+status_t decodeSmpte2086Helper(InputHidlVec* inputHidlVec, Smpte2086* outSmpte2086) {
+    status_t err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryRed);
+    if (err) {
+        return err;
+    }
+    err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryGreen);
+    if (err) {
+        return err;
+    }
+    err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryBlue);
+    if (err) {
+        return err;
+    }
+    err = decodeXyColor(inputHidlVec, &outSmpte2086->whitePoint);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<float>(inputHidlVec, &outSmpte2086->maxLuminance);
+    if (err) {
+        return err;
+    }
+    return decodeInteger<float>(inputHidlVec, &outSmpte2086->minLuminance);
+}
+
+status_t encodeCta861_3Helper(const Cta861_3& cta861_3, OutputHidlVec* outOutputHidlVec) {
+    status_t err = encodeInteger<float>(cta861_3.maxContentLightLevel, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    return encodeInteger<float>(cta861_3.maxFrameAverageLightLevel, outOutputHidlVec);
+}
+
+status_t decodeCta861_3Helper(InputHidlVec* inputHidlVec, Cta861_3* outCta861_3) {
+    status_t err = decodeInteger<float>(inputHidlVec, &outCta861_3->maxContentLightLevel);
+    if (err) {
+        return err;
+    }
+    return decodeInteger<float>(inputHidlVec, &outCta861_3->maxFrameAverageLightLevel);
+}
+
 /**
  * Public API functions
  */
+
+status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
+    return encode(MetadataType_BufferId, bufferId, outBufferId, encodeInteger);
+}
+
+status_t decodeBufferId(const hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId) {
+    return decode(MetadataType_BufferId, bufferId, outBufferId, decodeInteger);
+}
+
+status_t encodeName(const std::string& name, hidl_vec<uint8_t>* outName) {
+    return encode(MetadataType_Name, name, outName, encodeString);
+}
+
+status_t decodeName(const hidl_vec<uint8_t>& name, std::string* outName) {
+    return decode(MetadataType_Name, name, outName, decodeString);
+}
+
+status_t encodeWidth(uint64_t width, hidl_vec<uint8_t>* outWidth) {
+    return encode(MetadataType_Width, width, outWidth, encodeInteger);
+}
+
+status_t decodeWidth(const hidl_vec<uint8_t>& width, uint64_t* outWidth) {
+    return decode(MetadataType_Width, width, outWidth, decodeInteger);
+}
+
+status_t encodeHeight(uint64_t height, hidl_vec<uint8_t>* outHeight) {
+    return encode(MetadataType_Height, height, outHeight, encodeInteger);
+}
+
+status_t decodeHeight(const hidl_vec<uint8_t>& height, uint64_t* outHeight) {
+    return decode(MetadataType_Height, height, outHeight, decodeInteger);
+}
+
+status_t encodeLayerCount(uint64_t layerCount, hidl_vec<uint8_t>* outLayerCount) {
+    return encode(MetadataType_LayerCount, layerCount, outLayerCount, encodeInteger);
+}
+
+status_t decodeLayerCount(const hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount) {
+    return decode(MetadataType_LayerCount, layerCount, outLayerCount, decodeInteger);
+}
+
+status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested,
+        hidl_vec<uint8_t>* outPixelFormatRequested) {
+    return encode(MetadataType_PixelFormatRequested, static_cast<int32_t>(pixelFormatRequested),
+                  outPixelFormatRequested, encodeInteger);
+}
+
+status_t decodePixelFormatRequested(const hidl_vec<uint8_t>& pixelFormatRequested,
+        hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested) {
+    return decode(MetadataType_PixelFormatRequested, pixelFormatRequested,
+                  reinterpret_cast<int32_t*>(outPixelFormatRequested), decodeInteger);
+}
+
+status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, hidl_vec<uint8_t>* outPixelFormatFourCC) {
+    return encode(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
+                  encodeInteger);
+}
+
+status_t decodePixelFormatFourCC(const hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC) {
+    return decode(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
+                  decodeInteger);
+}
+
+status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, hidl_vec<uint8_t>* outPixelFormatModifier) {
+    return encode(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
+                  encodeInteger);
+}
+
+status_t decodePixelFormatModifier(const hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier) {
+    return decode(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
+                  decodeInteger);
+}
+
+status_t encodeUsage(uint64_t usage, hidl_vec<uint8_t>* outUsage) {
+    return encode(MetadataType_Usage, usage, outUsage, encodeInteger);
+}
+
+status_t decodeUsage(const hidl_vec<uint8_t>& usage, uint64_t* outUsage) {
+    return decode(MetadataType_Usage, usage, outUsage, decodeInteger);
+}
+
+status_t encodeAllocationSize(uint64_t allocationSize, hidl_vec<uint8_t>* outAllocationSize) {
+    return encode(MetadataType_AllocationSize, allocationSize, outAllocationSize, encodeInteger);
+}
+
+status_t decodeAllocationSize(const hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize) {
+    return decode(MetadataType_AllocationSize, allocationSize, outAllocationSize, decodeInteger);
+}
+
+status_t encodeProtectedContent(uint64_t protectedContent, hidl_vec<uint8_t>* outProtectedContent) {
+    return encode(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
+                  encodeInteger);
+}
+
+status_t decodeProtectedContent(const hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent) {
+    return decode(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
+                  decodeInteger);
+}
+
+status_t encodeCompression(const ExtendableType& compression, hidl_vec<uint8_t>* outCompression) {
+    return encode(MetadataType_Compression, compression, outCompression, encodeExtendableType);
+}
+
+status_t decodeCompression(const hidl_vec<uint8_t>& compression, ExtendableType* outCompression) {
+    return decode(MetadataType_Compression, compression, outCompression, decodeExtendableType,
+                  clearExtendableType);
+}
+
+status_t encodeInterlaced(const ExtendableType& interlaced, hidl_vec<uint8_t>* outInterlaced) {
+    return encode(MetadataType_Interlaced, interlaced, outInterlaced, encodeExtendableType);
+}
+
+status_t decodeInterlaced(const hidl_vec<uint8_t>& interlaced, ExtendableType* outInterlaced) {
+    return decode(MetadataType_Interlaced, interlaced, outInterlaced, decodeExtendableType,
+                  clearExtendableType);
+}
+
+status_t encodeChromaSiting(const ExtendableType& chromaSiting, hidl_vec<uint8_t>* outChromaSiting) {
+    return encode(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, encodeExtendableType);
+}
+
+status_t decodeChromaSiting(const hidl_vec<uint8_t>& chromaSiting, ExtendableType* outChromaSiting) {
+    return decode(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, decodeExtendableType,
+                  clearExtendableType);
+}
+
+status_t encodePlaneLayouts(const std::vector<PlaneLayout>& planeLayouts, hidl_vec<uint8_t>* outPlaneLayouts) {
+    return encode(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
+                  encodePlaneLayoutsHelper);
+}
+
+status_t decodePlaneLayouts(const hidl_vec<uint8_t>& planeLayouts, std::vector<PlaneLayout>* outPlaneLayouts) {
+    return decode(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
+                  decodePlaneLayoutsHelper, clearPlaneLayouts);
+}
+
+status_t encodeDataspace(const Dataspace& dataspace, hidl_vec<uint8_t>* outDataspace) {
+    return encode(MetadataType_Dataspace, static_cast<int32_t>(dataspace), outDataspace,
+                  encodeInteger);
+}
+
+status_t decodeDataspace(const hidl_vec<uint8_t>& dataspace, Dataspace* outDataspace) {
+    return decode(MetadataType_Dataspace, dataspace, reinterpret_cast<int32_t*>(outDataspace),
+                  decodeInteger);
+}
+
+status_t encodeBlendMode(const BlendMode& blendMode, hidl_vec<uint8_t>* outBlendMode) {
+    return encode(MetadataType_BlendMode, static_cast<int32_t>(blendMode), outBlendMode,
+                  encodeInteger);
+}
+
+status_t decodeBlendMode(const hidl_vec<uint8_t>& blendMode, BlendMode* outBlendMode) {
+    return decode(MetadataType_BlendMode, blendMode, reinterpret_cast<int32_t*>(outBlendMode),
+                  decodeInteger);
+}
+
+status_t encodeSmpte2086(const std::optional<Smpte2086>& smpte2086,
+                         hidl_vec<uint8_t>* outSmpte2086) {
+    return encodeOptional(MetadataType_Smpte2086, smpte2086, outSmpte2086, encodeSmpte2086Helper);
+}
+
+status_t decodeSmpte2086(const hidl_vec<uint8_t>& smpte2086,
+                         std::optional<Smpte2086>* outSmpte2086) {
+    return decodeOptional(MetadataType_Smpte2086, smpte2086, outSmpte2086, decodeSmpte2086Helper);
+}
+
+status_t encodeCta861_3(const std::optional<Cta861_3>& cta861_3, hidl_vec<uint8_t>* outCta861_3) {
+    return encodeOptional(MetadataType_Cta861_3, cta861_3, outCta861_3, encodeCta861_3Helper);
+}
+
+status_t decodeCta861_3(const hidl_vec<uint8_t>& cta861_3, std::optional<Cta861_3>* outCta861_3) {
+    return decodeOptional(MetadataType_Cta861_3, cta861_3, outCta861_3, decodeCta861_3Helper);
+}
+
+status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094_40,
+                            hidl_vec<uint8_t>* outSmpte2094_40) {
+    return encodeOptional(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+                          encodeByteVector);
+}
+
+status_t decodeSmpte2094_40(const hidl_vec<uint8_t>& smpte2094_40,
+                            std::optional<std::vector<uint8_t>>* outSmpte2094_40) {
+    return decodeOptional(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+                          decodeByteVector);
+}
+
+status_t encodeUint32(const MetadataType& metadataType, uint32_t input,
+                      hidl_vec<uint8_t>* output) {
+    return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeUint32(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                      uint32_t* output) {
+    return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeInt32(const MetadataType& metadataType, int32_t input,
+                     hidl_vec<uint8_t>* output) {
+    return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeInt32(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                     int32_t* output) {
+    return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeUint64(const MetadataType& metadataType, uint64_t input,
+                      hidl_vec<uint8_t>* output) {
+    return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeUint64(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                      uint64_t* output) {
+    return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeInt64(const MetadataType& metadataType, int64_t input,
+                     hidl_vec<uint8_t>* output) {
+    return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeInt64(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                     int64_t* output) {
+    return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeFloat(const MetadataType& metadataType, float input,
+                     hidl_vec<uint8_t>* output) {
+    return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeFloat(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                     float* output) {
+    return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeDouble(const MetadataType& metadataType, double input,
+                      hidl_vec<uint8_t>* output) {
+    return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeDouble(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                      double* output) {
+    return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeString(const MetadataType& metadataType, const std::string& input,
+                      hidl_vec<uint8_t>* output) {
+    return encode(metadataType, input, output, encodeString);
+}
+
+status_t decodeString(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                      std::string* output) {
+    return decode(metadataType, input, output, decodeString);
+}
+
 bool isStandardMetadataType(const MetadataType& metadataType) {
-    return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE, metadataType.name.size());
+    return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE,
+                         metadataType.name.size());
+}
+
+bool isStandardCompression(const ExtendableType& compression) {
+    return !std::strncmp(compression.name.c_str(), GRALLOC4_STANDARD_COMPRESSION,
+                         compression.name.size());
+}
+
+bool isStandardInterlaced(const ExtendableType& interlaced) {
+    return !std::strncmp(interlaced.name.c_str(), GRALLOC4_STANDARD_INTERLACED,
+                         interlaced.name.size());
+}
+
+bool isStandardChromaSiting(const ExtendableType& chromaSiting) {
+    return !std::strncmp(chromaSiting.name.c_str(), GRALLOC4_STANDARD_CHROMA_SITING,
+                         chromaSiting.name.size());
+}
+
+bool isStandardPlaneLayoutComponentType(const ExtendableType& planeLayoutComponentType) {
+    return !std::strncmp(planeLayoutComponentType.name.c_str(), GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+                         planeLayoutComponentType.name.size());
 }
 
 StandardMetadataType getStandardMetadataTypeValue(const MetadataType& metadataType) {
     return static_cast<StandardMetadataType>(metadataType.value);
 }
 
-status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
-    return encode(bufferId, outBufferId, encodeInteger);
+Compression getStandardCompressionValue(const ExtendableType& compression) {
+    return static_cast<Compression>(compression.value);
 }
 
-status_t decodeBufferId(const hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId) {
-    return decode(bufferId, outBufferId, decodeInteger);
+Interlaced getStandardInterlacedValue(const ExtendableType& interlaced) {
+    return static_cast<Interlaced>(interlaced.value);
 }
 
-status_t encodeName(const std::string& name, hidl_vec<uint8_t>* outName) {
-    return encode(name, outName, encodeString);
+ChromaSiting getStandardChromaSitingValue(const ExtendableType& chromaSiting) {
+    return static_cast<ChromaSiting>(chromaSiting.value);
 }
 
-status_t decodeName(const hidl_vec<uint8_t>& name, std::string* outName) {
-    return decode(name, outName, decodeString);
+PlaneLayoutComponentType getStandardPlaneLayoutComponentTypeValue(
+        const ExtendableType& planeLayoutComponentType) {
+    return static_cast<PlaneLayoutComponentType>(planeLayoutComponentType.value);
 }
 
-status_t encodeWidth(uint64_t width, hidl_vec<uint8_t>* outWidth) {
-    return encode(width, outWidth, encodeInteger);
+std::string getCompressionName(const ExtendableType& compression) {
+    if (!isStandardCompression(compression)) {
+        std::ostringstream stream;
+        stream << compression.name << "#" << compression.value;
+        return stream.str();
+    }
+    switch (getStandardCompressionValue(compression)) {
+        case Compression::NONE:
+            return "None";
+        case Compression::DISPLAY_STREAM_COMPRESSION:
+            return "DisplayStreamCompression";
+    }
 }
 
-status_t decodeWidth(const hidl_vec<uint8_t>& width, uint64_t* outWidth) {
-    return decode(width, outWidth, decodeInteger);
+std::string getInterlacedName(const ExtendableType& interlaced) {
+    if (!isStandardInterlaced(interlaced)) {
+        std::ostringstream stream;
+        stream << interlaced.name << "#" << interlaced.value;
+        return stream.str();
+    }
+    switch (getStandardInterlacedValue(interlaced)) {
+        case Interlaced::NONE:
+            return "None";
+        case Interlaced::TOP_BOTTOM:
+            return "TopBottom";
+        case Interlaced::RIGHT_LEFT:
+            return "RightLeft";
+    }
 }
 
-status_t encodeHeight(uint64_t height, hidl_vec<uint8_t>* outHeight) {
-    return encode(height, outHeight, encodeInteger);
+std::string getChromaSitingName(const ExtendableType& chromaSiting) {
+    if (!isStandardChromaSiting(chromaSiting)) {
+        std::ostringstream stream;
+        stream << chromaSiting.name << "#" << chromaSiting.value;
+        return stream.str();
+    }
+    switch (getStandardChromaSitingValue(chromaSiting)) {
+        case ChromaSiting::NONE:
+            return "None";
+        case ChromaSiting::UNKNOWN:
+            return "Unknown";
+        case ChromaSiting::SITED_INTERSTITIAL:
+            return "SitedInterstitial";
+        case ChromaSiting::COSITED_HORIZONTAL:
+            return "CositedHorizontal";
+    }
 }
 
-status_t decodeHeight(const hidl_vec<uint8_t>& height, uint64_t* outHeight) {
-    return decode(height, outHeight, decodeInteger);
-}
-
-status_t encodeLayerCount(uint64_t layerCount, hidl_vec<uint8_t>* outLayerCount) {
-    return encode(layerCount, outLayerCount, encodeInteger);
-}
-
-status_t decodeLayerCount(const hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount) {
-    return decode(layerCount, outLayerCount, decodeInteger);
-}
-
-status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested,
-        hidl_vec<uint8_t>* outPixelFormatRequested) {
-    return encode(static_cast<int32_t>(pixelFormatRequested), outPixelFormatRequested, encodeInteger);
-}
-
-status_t decodePixelFormatRequested(const hidl_vec<uint8_t>& pixelFormatRequested,
-        hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested) {
-    return decode(pixelFormatRequested, reinterpret_cast<int32_t*>(outPixelFormatRequested), decodeInteger);
-}
-
-status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, hidl_vec<uint8_t>* outPixelFormatFourCC) {
-    return encode(pixelFormatFourCC, outPixelFormatFourCC, encodeInteger);
-}
-
-status_t decodePixelFormatFourCC(const hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC) {
-    return decode(pixelFormatFourCC, outPixelFormatFourCC, decodeInteger);
-}
-
-status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, hidl_vec<uint8_t>* outPixelFormatModifier) {
-    return encode(pixelFormatModifier, outPixelFormatModifier, encodeInteger);
-}
-
-status_t decodePixelFormatModifier(const hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier) {
-    return decode(pixelFormatModifier, outPixelFormatModifier, decodeInteger);
-}
-
-status_t encodeUsage(uint64_t usage, hidl_vec<uint8_t>* outUsage) {
-    return encode(usage, outUsage, encodeInteger);
-}
-
-status_t decodeUsage(const hidl_vec<uint8_t>& usage, uint64_t* outUsage) {
-    return decode(usage, outUsage, decodeInteger);
-}
-
-status_t encodeAllocationSize(uint64_t allocationSize, hidl_vec<uint8_t>* outAllocationSize) {
-    return encode(allocationSize, outAllocationSize, encodeInteger);
-}
-
-status_t decodeAllocationSize(const hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize) {
-    return decode(allocationSize, outAllocationSize, decodeInteger);
-}
-
-status_t encodeProtectedContent(uint64_t protectedContent, hidl_vec<uint8_t>* outProtectedContent) {
-    return encode(protectedContent, outProtectedContent, encodeInteger);
-}
-
-status_t decodeProtectedContent(const hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent) {
-    return decode(protectedContent, outProtectedContent, decodeInteger);
-}
-
-status_t encodeCompression(const ExtendableType& compression, hidl_vec<uint8_t>* outCompression) {
-    return encode(compression, outCompression, encodeExtendableType);
-}
-
-status_t decodeCompression(const hidl_vec<uint8_t>& compression, ExtendableType* outCompression) {
-    return decode(compression, outCompression, decodeExtendableType, clearExtendableType);
-}
-
-status_t encodeInterlaced(const ExtendableType& interlaced, hidl_vec<uint8_t>* outInterlaced) {
-    return encode(interlaced, outInterlaced, encodeExtendableType);
-}
-
-status_t decodeInterlaced(const hidl_vec<uint8_t>& interlaced, ExtendableType* outInterlaced) {
-    return decode(interlaced, outInterlaced, decodeExtendableType, clearExtendableType);
-}
-
-status_t encodeChromaSiting(const ExtendableType& chromaSiting, hidl_vec<uint8_t>* outChromaSiting) {
-    return encode(chromaSiting, outChromaSiting, encodeExtendableType);
-}
-
-status_t decodeChromaSiting(const hidl_vec<uint8_t>& chromaSiting, ExtendableType* outChromaSiting) {
-    return decode(chromaSiting, outChromaSiting, decodeExtendableType, clearExtendableType);
-}
-
-status_t encodePlaneLayouts(const std::vector<PlaneLayout>& planeLayouts, hidl_vec<uint8_t>* outPlaneLayouts) {
-    return encode(planeLayouts, outPlaneLayouts, encodePlaneLayoutsHelper);
-}
-
-status_t decodePlaneLayouts(const hidl_vec<uint8_t>& planeLayouts, std::vector<PlaneLayout>* outPlaneLayouts) {
-    return decode(planeLayouts, outPlaneLayouts, decodePlaneLayoutsHelper, clearPlaneLayouts);
-}
-
-status_t encodeDataspace(const Dataspace& dataspace, hidl_vec<uint8_t>* outDataspace) {
-    return encode(static_cast<int32_t>(dataspace), outDataspace, encodeInteger);
-}
-
-status_t decodeDataspace(const hidl_vec<uint8_t>& dataspace, Dataspace* outDataspace) {
-    return decode(dataspace, reinterpret_cast<int32_t*>(outDataspace), decodeInteger);
-}
-
-status_t encodeBlendMode(const BlendMode& blendMode, hidl_vec<uint8_t>* outBlendMode) {
-    return encode(static_cast<int32_t>(blendMode), outBlendMode, encodeInteger);
-}
-
-status_t decodeBlendMode(const hidl_vec<uint8_t>& blendMode, BlendMode* outBlendMode) {
-    return decode(blendMode, reinterpret_cast<int32_t*>(outBlendMode), decodeInteger);
+std::string getPlaneLayoutComponentTypeName(const ExtendableType& planeLayoutComponentType) {
+    if (!isStandardPlaneLayoutComponentType(planeLayoutComponentType)) {
+        std::ostringstream stream;
+        stream << planeLayoutComponentType.name << "#" << planeLayoutComponentType.value;
+        return stream.str();
+    }
+    switch (getStandardPlaneLayoutComponentTypeValue(planeLayoutComponentType)) {
+        case PlaneLayoutComponentType::Y:
+            return "Y";
+        case PlaneLayoutComponentType::CB:
+            return "Cb";
+        case PlaneLayoutComponentType::CR:
+            return "Cr";
+        case PlaneLayoutComponentType::R:
+            return "R";
+        case PlaneLayoutComponentType::G:
+            return "G";
+        case PlaneLayoutComponentType::B:
+            return "B";
+        case PlaneLayoutComponentType::A:
+            return "A";
+    }
 }
 
 } // namespace gralloc4
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.cpp b/libs/gralloc/types/fuzzer/gralloctypes.cpp
index 23c90b8..b18f407 100644
--- a/libs/gralloc/types/fuzzer/gralloctypes.cpp
+++ b/libs/gralloc/types/fuzzer/gralloctypes.cpp
@@ -30,9 +30,10 @@
 #include "util.h"
 
 using ::android::status_t;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
 
 #define GRALLOCTYPES_DECODE(T, FUNC) \
-    [] (const ::android::hardware::hidl_vec<uint8_t>& vec, uint8_t /*data*/) {\
+    [] (const ::android::hardware::hidl_vec<uint8_t>& vec) {\
         FUZZ_LOG() << "about to read " #T " using " #FUNC;\
         T t;\
         status_t err = FUNC(vec, &t);\
@@ -40,6 +41,15 @@
         FUZZ_LOG() << #T " done " /* << "err: " << err*/;\
     }
 
+#define GRALLOCTYPES_DECODE_VENDOR_HELPER(T, FUNC) \
+    [] (const MetadataType& metadataType, const ::android::hardware::hidl_vec<uint8_t>& vec) {\
+        FUZZ_LOG() << "about to read " #T " using " #FUNC;\
+        T t;\
+        status_t err = FUNC(metadataType, vec, &t);\
+        (void) err;\
+        FUZZ_LOG() << #T " done " /* << "err: " << err*/;\
+    }
+
 
 // clang-format off
 std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS {
@@ -60,5 +70,18 @@
     GRALLOCTYPES_DECODE(std::vector<aidl::android::hardware::graphics::common::PlaneLayout>, ::android::gralloc4::decodePlaneLayouts),
     GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::Dataspace, ::android::gralloc4::decodeDataspace),
     GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::BlendMode, ::android::gralloc4::decodeBlendMode),
+    GRALLOCTYPES_DECODE(std::optional<aidl::android::hardware::graphics::common::Smpte2086>, ::android::gralloc4::decodeSmpte2086),
+    GRALLOCTYPES_DECODE(std::optional<aidl::android::hardware::graphics::common::Cta861_3>, ::android::gralloc4::decodeCta861_3),
+    GRALLOCTYPES_DECODE(std::optional<std::vector<uint8_t>>, ::android::gralloc4::decodeSmpte2094_40),
+};
+
+std::vector<GrallocTypesVendorHelperDecode> GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS {
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(uint32_t, ::android::gralloc4::decodeUint32),
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(int32_t, ::android::gralloc4::decodeInt32),
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(uint64_t, ::android::gralloc4::decodeUint64),
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(int64_t, ::android::gralloc4::decodeInt64),
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(float, ::android::gralloc4::decodeFloat),
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(double, ::android::gralloc4::decodeDouble),
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(std::string, ::android::gralloc4::decodeString),
 };
 // clang-format on
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.h b/libs/gralloc/types/fuzzer/gralloctypes.h
index b995fce..a3fe2d9 100644
--- a/libs/gralloc/types/fuzzer/gralloctypes.h
+++ b/libs/gralloc/types/fuzzer/gralloctypes.h
@@ -20,6 +20,8 @@
 
 #include <vector>
 
-using GrallocTypesDecode = std::function<void(const ::android::hardware::hidl_vec<uint8_t>& vec, uint8_t data)>;
+using GrallocTypesDecode = std::function<void(const ::android::hardware::hidl_vec<uint8_t>& vec)>;
+using GrallocTypesVendorHelperDecode = std::function<void(const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType&, const ::android::hardware::hidl_vec<uint8_t>& vec)>;
 
 extern std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS;
+extern std::vector<GrallocTypesVendorHelperDecode> GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS;
diff --git a/libs/gralloc/types/fuzzer/main.cpp b/libs/gralloc/types/fuzzer/main.cpp
index 2807878..8779fa2 100644
--- a/libs/gralloc/types/fuzzer/main.cpp
+++ b/libs/gralloc/types/fuzzer/main.cpp
@@ -24,52 +24,99 @@
 #include <cstdlib>
 #include <ctime>
 
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
 void doFuzz(
-        const std::vector<GrallocTypesDecode>& decodes,
-        const std::vector<uint8_t>& input,
-        const std::vector<uint8_t>& instructions) {
+        const std::vector<GrallocTypesDecode>& decodes, uint8_t instruction,
+        const std::vector<uint8_t>& input) {
 
     ::android::hardware::hidl_vec<uint8_t> vec;
     vec.setToExternal(const_cast<uint8_t*>(input.data()), input.size(), false /*shouldOwn*/);
 
     // since we are only using a byte to index
     CHECK(decodes.size() <= 255) << decodes.size();
+    uint8_t decodeIdx = instruction % decodes.size();
 
-    for (size_t i = 0; i < instructions.size() - 1; i += 2) {
-        uint8_t a = instructions[i];
-        uint8_t decodeIdx = a % decodes.size();
+    FUZZ_LOG() << "Instruction: " << instruction << " idx: " << static_cast<size_t>(decodeIdx)
+               << " size: " << vec.size();
 
-        uint8_t b = instructions[i + 1];
+    decodes[decodeIdx](vec);
+}
 
-        FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
-                   << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(decodeIdx)
-                   << ") arg: " << static_cast<size_t>(b) << " size: " << vec.size();
+size_t fillInMetadataType(const std::vector<uint8_t>& input, MetadataType* outMetadataType) {
+    if (input.size() < sizeof(outMetadataType->value) + 1) {
+        return 0;
+    }
+    size_t size = 0;
 
-        decodes[decodeIdx](vec, b);
+    outMetadataType->value = *(reinterpret_cast<const int64_t*>(input.data()));
+    size += sizeof(outMetadataType->value);
+
+    uint8_t nameLen = *(input.data() + size);
+    size += 1;
+
+    if (input.size() < size + nameLen) {
+        return 0;
+    }
+    std::string name(reinterpret_cast<const char*>(input.data()) + size, nameLen);
+    outMetadataType->name = name;
+    return size + nameLen;
+}
+
+void doFuzzVendorHelper(
+        const std::vector<GrallocTypesVendorHelperDecode>& decodes, uint8_t instruction,
+        const std::vector<uint8_t>& input) {
+
+    MetadataType metadataType;
+    size_t sizeUsed  = fillInMetadataType(input, &metadataType);
+    if (sizeUsed <= 0) {
+        return;
+    }
+
+    ::android::hardware::hidl_vec<uint8_t> vec;
+    vec.setToExternal(const_cast<uint8_t*>(input.data() + sizeUsed), input.size() - sizeUsed,
+                      false /*shouldOwn*/);
+
+    // since we are only using a byte to index
+    CHECK(decodes.size() <= 255) << decodes.size();
+    uint8_t decodeIdx = instruction % decodes.size();
+
+    FUZZ_LOG() << "Vendor Helper instruction: " << instruction << " idx: "
+               << static_cast<size_t>(decodeIdx) << " size: " << vec.size();
+
+    decodes[decodeIdx](metadataType, vec);
+}
+
+void fuzz(uint8_t options, uint8_t instruction, const std::vector<uint8_t>& input) {
+    uint8_t option = options & 0x1;
+
+    switch (option) {
+        case 0x0:
+            doFuzz(GRALLOCTYPES_DECODE_FUNCTIONS, instruction, input);
+            break;
+        case 0x1:
+            doFuzzVendorHelper(GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS, instruction, input);
+            break;
+        default:
+            LOG_ALWAYS_FATAL("unknown gralloc types %d", static_cast<int>(option));
     }
 }
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     if (size <= 1) return 0;  // no use
 
-    // data to fill out parcel
-    size_t inputLen = size / 2;
-    std::vector<uint8_t> input(data, data + inputLen);
-    data += inputLen;
-    size -= inputLen;
+    uint8_t options = *data;
+    data++;
+    size--;
 
-    // data to use to determine what to do
-    size_t instructionLen = size;
-    std::vector<uint8_t> instructions(data, data + instructionLen);
-    data += instructionLen;
-    size -= instructionLen;
+    uint8_t instruction = *data;
+    data++;
+    size--;
 
-    CHECK(size == 0) << "size: " << size;
+    std::vector<uint8_t> input(data, data + size);
 
-    FUZZ_LOG() << "inputLen: " << inputLen << " instructionLen: " << instructionLen;
     FUZZ_LOG() << "input: " << hexString(input);
-    FUZZ_LOG() << "instructions: " << hexString(instructions);
 
-    doFuzz(GRALLOCTYPES_DECODE_FUNCTIONS, input, instructions);
+    fuzz(options, instruction, input);
     return 0;
 }
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index 80588cd..ca0d4b5 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -17,27 +17,209 @@
 #pragma once
 
 #include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Cta861_3.h>
 #include <aidl/android/hardware/graphics/common/Dataspace.h>
 #include <aidl/android/hardware/graphics/common/ExtendableType.h>
 #include <aidl/android/hardware/graphics/common/Interlaced.h>
 #include <aidl/android/hardware/graphics/common/PlaneLayout.h>
-#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
-#include <aidl/android/hardware/graphics/common/Compression.h>
-#include <aidl/android/hardware/graphics/common/Interlaced.h>
-#include <aidl/android/hardware/graphics/common/StandardMetadataType.h>
 #include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
-
+#include <aidl/android/hardware/graphics/common/Smpte2086.h>
+#include <aidl/android/hardware/graphics/common/StandardMetadataType.h>
+#include <aidl/android/hardware/graphics/common/XyColor.h>
 #include <android/hardware/graphics/mapper/4.0/IMapper.h>
 
+/**
+ * Define equality operators for Stable AIDL types.
+ */
+inline bool operator==(const aidl::android::hardware::graphics::common::ExtendableType& lhs,
+                const aidl::android::hardware::graphics::common::ExtendableType& rhs) {
+    return !std::strcmp(lhs.name.c_str(), rhs.name.c_str()) && lhs.value == rhs.value;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::ExtendableType& lhs,
+                const aidl::android::hardware::graphics::common::ExtendableType& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::PlaneLayoutComponent& lhs,
+                const aidl::android::hardware::graphics::common::PlaneLayoutComponent& rhs) {
+    if (lhs.type.name != rhs.type.name) {
+        return false;
+    }
+    if (lhs.type.value != rhs.type.value) {
+        return false;
+    }
+    if (lhs.sizeInBits != rhs.sizeInBits) {
+        return false;
+    }
+    if (lhs.offsetInBits != rhs.offsetInBits) {
+        return false;
+    }
+    return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::PlaneLayoutComponent& lhs,
+                const aidl::android::hardware::graphics::common::PlaneLayoutComponent& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Rect& lhs,
+                const aidl::android::hardware::graphics::common::Rect& rhs) {
+    if (lhs.left != rhs.left) {
+        return false;
+    }
+    if (lhs.top != rhs.top) {
+        return false;
+    }
+    if (lhs.right != rhs.right) {
+        return false;
+    }
+    if (lhs.bottom != rhs.bottom) {
+        return false;
+    }
+    return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Rect& lhs,
+                const aidl::android::hardware::graphics::common::Rect& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::PlaneLayout& lhs,
+                const aidl::android::hardware::graphics::common::PlaneLayout& rhs) {
+    if (lhs.offsetInBytes != rhs.offsetInBytes) {
+        return false;
+    }
+    if (lhs.sampleIncrementInBits != rhs.sampleIncrementInBits) {
+        return false;
+    }
+    if (lhs.strideInBytes != rhs.strideInBytes) {
+        return false;
+    }
+    if (lhs.widthInSamples != rhs.widthInSamples) {
+        return false;
+    }
+    if (lhs.heightInSamples != rhs.heightInSamples) {
+        return false;
+    }
+    if (lhs.totalSizeInBytes != rhs.totalSizeInBytes) {
+        return false;
+    }
+    if (lhs.horizontalSubsampling != rhs.horizontalSubsampling) {
+        return false;
+    }
+    if (lhs.verticalSubsampling != rhs.verticalSubsampling) {
+        return false;
+    }
+    if (lhs.crop != rhs.crop) {
+        return false;
+    }
+    if (lhs.components.size() != rhs.components.size()) {
+        return false;
+    }
+    for (size_t i = 0; i < lhs.components.size(); i++) {
+        if (lhs.components[i] != rhs.components[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::PlaneLayout& lhs,
+                const aidl::android::hardware::graphics::common::PlaneLayout& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& lhs,
+                const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& rhs) {
+    if (lhs.size() != rhs.size()) {
+        return false;
+    }
+    for (size_t i = 0; i < lhs.size(); i++) {
+        if (lhs[i] != rhs[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+inline bool operator!=(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& lhs,
+                const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::XyColor& lhs,
+                const aidl::android::hardware::graphics::common::XyColor& rhs) {
+    if (lhs.x != rhs.x) {
+        return false;
+    }
+    if (lhs.y != rhs.y) {
+        return false;
+    }
+    return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::XyColor& lhs,
+                const aidl::android::hardware::graphics::common::XyColor& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Smpte2086& lhs,
+                const aidl::android::hardware::graphics::common::Smpte2086& rhs) {
+    if (lhs.primaryRed != rhs.primaryRed) {
+        return false;
+    }
+    if (lhs.primaryGreen != rhs.primaryGreen) {
+        return false;
+    }
+    if (lhs.primaryBlue != rhs.primaryBlue) {
+        return false;
+    }
+    if (lhs.whitePoint != rhs.whitePoint) {
+        return false;
+    }
+    if (lhs.maxLuminance != rhs.maxLuminance) {
+        return false;
+    }
+    if (lhs.minLuminance != rhs.minLuminance) {
+        return false;
+    }
+    return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Smpte2086& lhs,
+                const aidl::android::hardware::graphics::common::Smpte2086& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Cta861_3& lhs,
+                const aidl::android::hardware::graphics::common::Cta861_3& rhs) {
+    if (lhs.maxContentLightLevel != rhs.maxContentLightLevel) {
+        return false;
+    }
+    if (lhs.maxFrameAverageLightLevel != rhs.maxFrameAverageLightLevel) {
+        return false;
+    }
+    return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Cta861_3& lhs,
+                const aidl::android::hardware::graphics::common::Cta861_3& rhs) {
+    return !(lhs == rhs);
+}
+
 namespace android {
 
 namespace gralloc4 {
 
 #define GRALLOC4_STANDARD_METADATA_TYPE "android.hardware.graphics.common.StandardMetadataType"
-#define GRALLOC4_CHROMA_SITING "android.hardware.graphics.common.ChromaSiting"
-#define GRALLOC4_COMPRESSION "android.hardware.graphics.common.Compression"
-#define GRALLOC4_INTERLACED "android.hardware.graphics.common.Interlaced"
-#define GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE "android.hardware.graphics.common.PlaneLayoutComponentType"
+#define GRALLOC4_STANDARD_CHROMA_SITING "android.hardware.graphics.common.ChromaSiting"
+#define GRALLOC4_STANDARD_COMPRESSION "android.hardware.graphics.common.Compression"
+#define GRALLOC4_STANDARD_INTERLACED "android.hardware.graphics.common.Interlaced"
+#define GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE \
+    "android.hardware.graphics.common.PlaneLayoutComponentType"
 
 /*---------------------------------------------------------------------------------------------*/
 /**
@@ -112,19 +294,37 @@
         GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::BLEND_MODE)
 };
 
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+        MetadataType_Smpte2086 = {GRALLOC4_STANDARD_METADATA_TYPE,
+                                  static_cast<int64_t>(aidl::android::hardware::graphics::common::
+                                                               StandardMetadataType::SMPTE2086)};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+        MetadataType_Cta861_3 = {GRALLOC4_STANDARD_METADATA_TYPE,
+                                 static_cast<int64_t>(aidl::android::hardware::graphics::common::
+                                                              StandardMetadataType::CTA861_3)};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+        MetadataType_Smpte2094_40 = {GRALLOC4_STANDARD_METADATA_TYPE,
+                                     static_cast<int64_t>(
+                                             aidl::android::hardware::graphics::common::
+                                                     StandardMetadataType::SMPTE2094_40)};
+
 /*---------------------------------------------------------------------------------------------*/
 
 /**
  * Definitions of the standard compression strategies. It is recommended that everyone uses
  * these definitions directly for standard compression strategies.
  */
-static const aidl::android::hardware::graphics::common::ExtendableType Compression_None = {
-        GRALLOC4_COMPRESSION, static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::NONE)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType Compression_None =
+        {GRALLOC4_STANDARD_COMPRESSION,
+         static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::NONE)};
 
-static const aidl::android::hardware::graphics::common::ExtendableType Compression_DisplayStreamCompression = {
-        GRALLOC4_COMPRESSION, static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::DISPLAY_STREAM_COMPRESSION)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType
+        Compression_DisplayStreamCompression =
+                {GRALLOC4_STANDARD_COMPRESSION,
+                 static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::
+                                              DISPLAY_STREAM_COMPRESSION)};
 
 /*---------------------------------------------------------------------------------------------*/
 
@@ -132,17 +332,17 @@
  * Definitions of the standard interlaced strategies. It is recommended that everyone uses
  * these definitions directly for standard interlaced strategies.
  */
-static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_None = {
-        GRALLOC4_INTERLACED, static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::NONE)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_None =
+        {GRALLOC4_STANDARD_INTERLACED,
+         static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::NONE)};
 
-static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_TopBottom = {
-        GRALLOC4_INTERLACED, static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::TOP_BOTTOM)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_TopBottom =
+        {GRALLOC4_STANDARD_INTERLACED,
+         static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::TOP_BOTTOM)};
 
-static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_RightLeft = {
-        GRALLOC4_INTERLACED, static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::RIGHT_LEFT)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_RightLeft =
+        {GRALLOC4_STANDARD_INTERLACED,
+         static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::RIGHT_LEFT)};
 
 /*---------------------------------------------------------------------------------------------*/
 
@@ -150,21 +350,25 @@
  * Definitions of the standard chroma siting. It is recommended that everyone uses
  * these definitions directly for standard chroma siting.
  */
-static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_None = {
-        GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::NONE)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_None =
+        {GRALLOC4_STANDARD_CHROMA_SITING,
+         static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::NONE)};
 
-static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_Unknown = {
-        GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::UNKNOWN)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_Unknown =
+        {GRALLOC4_STANDARD_CHROMA_SITING,
+         static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::UNKNOWN)};
 
-static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_SitedInterstitial = {
-        GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::SITED_INTERSTITIAL)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType
+        ChromaSiting_SitedInterstitial = {GRALLOC4_STANDARD_CHROMA_SITING,
+                                          static_cast<int64_t>(
+                                                  aidl::android::hardware::graphics::common::
+                                                          ChromaSiting::SITED_INTERSTITIAL)};
 
-static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_CositedHorizontal = {
-        GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::COSITED_HORIZONTAL)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType
+        ChromaSiting_CositedHorizontal = {GRALLOC4_STANDARD_CHROMA_SITING,
+                                          static_cast<int64_t>(
+                                                  aidl::android::hardware::graphics::common::
+                                                          ChromaSiting::COSITED_HORIZONTAL)};
 
 /*---------------------------------------------------------------------------------------------*/
 
@@ -172,44 +376,44 @@
  * Definitions of the standard plane layout component types. It is recommended that everyone uses
  * these definitions directly for standard plane layout component types
  */
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_Y = {
-        GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::Y)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_Y =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::Y)};
 
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CB = {
-        GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CB)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CB =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CB)};
 
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CR = {
-        GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CR)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CR =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CR)};
 
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_R = {
-        GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::R)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_R =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::R)};
 
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_G = {
-        GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::G)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_G =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::G)};
 
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_B = {
-        GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::B)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_B =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::B)};
 
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_A = {
-        GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::A)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_A =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::A)};
 
 /*---------------------------------------------------------------------------------------------*/
 
 /**
- * The functions below can be used to parse a StandardMetadataType.
- */
-bool isStandardMetadataType(const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
-
-aidl::android::hardware::graphics::common::StandardMetadataType getStandardMetadataTypeValue(const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
-
-/**
  * The functions below encode and decode standard metadata into a byte stream. It is STRONGLY
  * recommended that both the vendor and system partitions use these functions when getting
  * and setting metadata through gralloc 4 (IMapper 4.0).
@@ -265,6 +469,115 @@
 status_t encodeBlendMode(const aidl::android::hardware::graphics::common::BlendMode& blendMode, android::hardware::hidl_vec<uint8_t>* outBlendMode);
 status_t decodeBlendMode(const android::hardware::hidl_vec<uint8_t>& blendMode, aidl::android::hardware::graphics::common::BlendMode* outBlendMode);
 
+status_t encodeSmpte2086(
+        const std::optional<aidl::android::hardware::graphics::common::Smpte2086>& smpte2086,
+        android::hardware::hidl_vec<uint8_t>* outSmpte2086);
+status_t decodeSmpte2086(
+        const android::hardware::hidl_vec<uint8_t>& smpte2086,
+        std::optional<aidl::android::hardware::graphics::common::Smpte2086>* outSmpte2086);
+
+status_t encodeCta861_3(
+        const std::optional<aidl::android::hardware::graphics::common::Cta861_3>& cta861_3,
+        android::hardware::hidl_vec<uint8_t>* outCta861_3);
+status_t decodeCta861_3(
+        const android::hardware::hidl_vec<uint8_t>& cta861_3,
+        std::optional<aidl::android::hardware::graphics::common::Cta861_3>* outCta861_3);
+
+status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094_40,
+                            android::hardware::hidl_vec<uint8_t>* outSmpte2094_40);
+status_t decodeSmpte2094_40(const android::hardware::hidl_vec<uint8_t>& smpte2094_40,
+                            std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+
+/**
+ * The functions below can be used to encode and decode vendor metadata types.
+ */
+status_t encodeUint32(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        uint32_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeUint32(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, uint32_t* output);
+
+status_t encodeInt32(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        int32_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeInt32(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, int32_t* output);
+
+status_t encodeUint64(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        uint64_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeUint64(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, uint64_t* output);
+
+status_t encodeInt64(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        int64_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeInt64(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, int64_t* output);
+
+status_t encodeFloat(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        float input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeFloat(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, float* output);
+
+status_t encodeDouble(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        double input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeDouble(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, double* output);
+
+status_t encodeString(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const std::string& input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeString(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, std::string* output);
+
+/**
+ * The functions below can be used to parse extendable types.
+ */
+bool isStandardMetadataType(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+bool isStandardCompression(
+        const aidl::android::hardware::graphics::common::ExtendableType& compression);
+bool isStandardInterlaced(
+        const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+bool isStandardChromaSiting(
+        const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+bool isStandardPlaneLayoutComponentType(
+        const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
+aidl::android::hardware::graphics::common::StandardMetadataType getStandardMetadataTypeValue(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+aidl::android::hardware::graphics::common::Compression getStandardCompressionValue(
+        const aidl::android::hardware::graphics::common::ExtendableType& compression);
+aidl::android::hardware::graphics::common::Interlaced getStandardInterlacedValue(
+        const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+aidl::android::hardware::graphics::common::ChromaSiting getStandardChromaSitingValue(
+        const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+aidl::android::hardware::graphics::common::PlaneLayoutComponentType
+getStandardPlaneLayoutComponentTypeValue(
+        const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
+/**
+ * The functions below return string representations of ExtendableTypes
+ */
+std::string getCompressionName(
+        const aidl::android::hardware::graphics::common::ExtendableType& compression);
+std::string getInterlacedName(
+        const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+std::string getChromaSitingName(
+        const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+std::string getPlaneLayoutComponentTypeName(
+        const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
 } // namespace gralloc4
 
 } // namespace android
diff --git a/libs/gralloc/types/tests/Android.bp b/libs/gralloc/types/tests/Android.bp
new file mode 100644
index 0000000..b939c1d
--- /dev/null
+++ b/libs/gralloc/types/tests/Android.bp
@@ -0,0 +1,25 @@
+//
+// 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.
+//
+
+cc_test {
+    name: "GrallocTypes_test",
+    shared_libs: [
+        "libgralloctypes",
+        "libhidlbase",
+    ],
+    srcs: ["Gralloc4_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+}
diff --git a/libs/gralloc/types/tests/Gralloc4_test.cpp b/libs/gralloc/types/tests/Gralloc4_test.cpp
new file mode 100644
index 0000000..3542ed9
--- /dev/null
+++ b/libs/gralloc/types/tests/Gralloc4_test.cpp
@@ -0,0 +1,552 @@
+/*
+ * 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_TAG "Gralloc4Test"
+
+#include <limits>
+
+#include <gralloctypes/Gralloc4.h>
+
+#include <gtest/gtest.h>
+
+using android::hardware::hidl_vec;
+
+using android::hardware::graphics::common::V1_2::PixelFormat;
+
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::ChromaSiting;
+using aidl::android::hardware::graphics::common::Compression;
+using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::Interlaced;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::Rect;
+using aidl::android::hardware::graphics::common::Smpte2086;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using aidl::android::hardware::graphics::common::XyColor;
+
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+namespace android {
+
+template<class T>
+using EncodeFunction = status_t(*)(T, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeConstFunction = status_t(*)(const T&, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeMetadataTypeFunction = status_t(*)(const MetadataType&, T, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeMetadataTypeConstFunction = status_t(*)(const MetadataType&, const T&, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeOptionalFunction = status_t(*)(const std::optional<T>&, hidl_vec<uint8_t>*);
+
+template<class T>
+using DecodeFunction = status_t(*)(const hidl_vec<uint8_t>&, T*);
+
+template<class T>
+using DecodeMetadataTypeFunction = status_t(*)(const MetadataType&, const hidl_vec<uint8_t>&, T*);
+
+template<class T>
+using DecodeOptionalFunction = status_t(*)(const hidl_vec<uint8_t>&, std::optional<T>*);
+
+template<class T>
+void testHelper(const T& input, EncodeFunction<T> encode, DecodeFunction<T> decode) {
+    hidl_vec<uint8_t> vec;
+    T output;
+    ASSERT_EQ(NO_ERROR, encode(input, &vec));
+    ASSERT_EQ(NO_ERROR, decode(vec, &output));
+    ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperConst(const T& input, EncodeConstFunction<T> encode, DecodeFunction<T> decode) {
+    hidl_vec<uint8_t> vec;
+    T output;
+    ASSERT_EQ(NO_ERROR, encode(input, &vec));
+    ASSERT_EQ(NO_ERROR, decode(vec, &output));
+    ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperMetadataType(const T& input, EncodeMetadataTypeFunction<T> encode, DecodeMetadataTypeFunction<T> decode) {
+    hidl_vec<uint8_t> vec;
+    MetadataType metadataType{"vendor.mycompanyname.graphics.common.MetadataType", 0};
+    T output;
+    ASSERT_EQ(NO_ERROR, encode(metadataType, input, &vec));
+    ASSERT_EQ(NO_ERROR, decode(metadataType, vec, &output));
+    ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperMetadataTypeConst(const T& input, EncodeMetadataTypeConstFunction<T> encode, DecodeMetadataTypeFunction<T> decode) {
+    hidl_vec<uint8_t> vec;
+    MetadataType metadataType{"vendor.mycompanyname.graphics.common.MetadataType", 0};
+    T output;
+    ASSERT_EQ(NO_ERROR, encode(metadataType, input, &vec));
+    ASSERT_EQ(NO_ERROR, decode(metadataType, vec, &output));
+    ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperStableAidlType(const T& input, EncodeConstFunction<T> encode, DecodeFunction<T> decode) {
+    hidl_vec<uint8_t> vec;
+    T output;
+    ASSERT_EQ(NO_ERROR, encode(input, &vec));
+    ASSERT_EQ(NO_ERROR, decode(vec, &output));
+    ASSERT_TRUE(input == output);
+}
+
+template<class T>
+void testHelperStableAidlTypeOptional(const std::optional<T>& input, EncodeOptionalFunction<T> encode,
+                                      DecodeOptionalFunction<T> decode) {
+    hidl_vec<uint8_t> vec;
+    std::optional<T> tmp = input;
+    std::optional<T> output;
+    ASSERT_EQ(NO_ERROR, encode(tmp, &vec));
+    ASSERT_EQ(NO_ERROR, decode(vec, &output));
+    ASSERT_EQ(tmp.has_value(), output.has_value());
+    if (!tmp.has_value()) {
+        return;
+    }
+    ASSERT_TRUE(*tmp == *output);
+}
+
+class Gralloc4TestUint32 : public testing::TestWithParam<uint32_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestUint32Params, Gralloc4TestUint32,
+        ::testing::Values(0, -1, 1, 5, 100, 0xFF, std::numeric_limits<uint32_t>::min(),
+                          std::numeric_limits<uint32_t>::max()));
+
+TEST_P(Gralloc4TestUint32, Uint32) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeUint32, gralloc4::decodeUint32));
+}
+
+TEST_P(Gralloc4TestUint32, PixelFormatFourCC) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodePixelFormatFourCC, gralloc4::decodePixelFormatFourCC));
+}
+
+class Gralloc4TestInt32 : public testing::TestWithParam<int32_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestInt32Params, Gralloc4TestInt32,
+        ::testing::Values(0, 1, 5, 100, 0xFF, std::numeric_limits<int32_t>::min(),
+                          std::numeric_limits<int32_t>::max()));
+
+TEST_P(Gralloc4TestInt32, Int32) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeInt32, gralloc4::decodeInt32));
+}
+
+class Gralloc4TestUint64 : public testing::TestWithParam<uint64_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestUint64Params, Gralloc4TestUint64,
+        ::testing::Values(0, -1, 1, 5, 100, 0xFF, std::numeric_limits<uint64_t>::min(),
+                          std::numeric_limits<uint64_t>::max()));
+
+TEST_P(Gralloc4TestUint64, Uint64) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeUint64, gralloc4::decodeUint64));
+}
+
+TEST_P(Gralloc4TestUint64, BufferId) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeBufferId, gralloc4::decodeBufferId));
+}
+
+TEST_P(Gralloc4TestUint64, Width) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeWidth, gralloc4::decodeWidth));
+}
+
+TEST_P(Gralloc4TestUint64, Height) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeHeight, gralloc4::decodeHeight));
+}
+
+TEST_P(Gralloc4TestUint64, LayerCount) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeLayerCount, gralloc4::decodeLayerCount));
+}
+
+TEST_P(Gralloc4TestUint64, PixelFormatModifier) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodePixelFormatModifier, gralloc4::decodePixelFormatModifier));
+}
+
+TEST_P(Gralloc4TestUint64, Usage) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeUsage, gralloc4::decodeUsage));
+}
+
+TEST_P(Gralloc4TestUint64, AllocationSize) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeAllocationSize, gralloc4::decodeAllocationSize));
+}
+
+TEST_P(Gralloc4TestUint64, ProtectedContent) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeProtectedContent, gralloc4::decodeProtectedContent));
+}
+
+class Gralloc4TestInt64 : public testing::TestWithParam<int64_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestInt64Params, Gralloc4TestInt64,
+        ::testing::Values(0, 1, 5, 100, 0xFF, std::numeric_limits<int64_t>::min(),
+                          std::numeric_limits<int64_t>::max()));
+
+TEST_P(Gralloc4TestInt64, Int64) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeInt64, gralloc4::decodeInt64));
+}
+
+class Gralloc4TestFloat : public testing::TestWithParam<float> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestFloatParams, Gralloc4TestFloat,
+        ::testing::Values(0.0, 1.999999, 5.5, 100.1, 1234.5678, std::numeric_limits<float>::min(),
+                          std::numeric_limits<float>::max()));
+
+TEST_P(Gralloc4TestFloat, Float) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeFloat, gralloc4::decodeFloat));
+}
+
+class Gralloc4TestDouble : public testing::TestWithParam<double> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestDoubleParams, Gralloc4TestDouble,
+        ::testing::Values(0.0, 1.999999, 5.5, 100.1, 1234.5678, std::numeric_limits<double>::min(),
+                          std::numeric_limits<double>::max()));
+
+TEST_P(Gralloc4TestDouble, Double) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeDouble, gralloc4::decodeDouble));
+}
+
+class Gralloc4TestString : public testing::TestWithParam<std::string> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestStringParams, Gralloc4TestString,
+        ::testing::Values("name", "aaaaa", "", "abcdefghijklmnopqrstuvwxyz", "0xFF"));
+
+TEST_P(Gralloc4TestString, String) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataTypeConst(GetParam(), gralloc4::encodeString, gralloc4::decodeString));
+}
+
+TEST_P(Gralloc4TestString, Name) {
+    ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeName, gralloc4::decodeName));
+}
+
+class Gralloc4TestPixelFormat : public testing::TestWithParam<PixelFormat> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestPixelFormatParams, Gralloc4TestPixelFormat,
+        ::testing::Values(PixelFormat::RGBA_8888, PixelFormat::BLOB,
+                          PixelFormat::IMPLEMENTATION_DEFINED, PixelFormat::YCBCR_420_888,
+                          PixelFormat::YV12));
+
+TEST_P(Gralloc4TestPixelFormat, PixelFormatRequested) {
+    ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodePixelFormatRequested, gralloc4::decodePixelFormatRequested));
+}
+
+class Gralloc4TestCompression : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestCompressionParams, Gralloc4TestCompression,
+        ::testing::Values(gralloc4::Compression_None, gralloc4::Compression_DisplayStreamCompression,
+            ExtendableType{"", 0},
+            ExtendableType{"vendor.mycompanyname.graphics.common.Compression", 0xFF},
+            ExtendableType{"vendor.mycompanyname.graphics.common.Compression", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestCompression, Compression) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeCompression, gralloc4::decodeCompression));
+}
+
+class Gralloc4TestInterlaced : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestInterlacedParams, Gralloc4TestInterlaced,
+        ::testing::Values(gralloc4::Interlaced_None, gralloc4::Interlaced_TopBottom,
+            gralloc4::Interlaced_RightLeft,
+            ExtendableType{"", 0},
+            ExtendableType{"vendor.mycompanyname.graphics.common.Interlaced", 0xFF},
+            ExtendableType{"vendor.mycompanyname.graphics.common.Interlaced", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestInterlaced, Interlaced) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeInterlaced, gralloc4::decodeInterlaced));
+}
+
+class Gralloc4TestChromaSiting : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestChromaSitingParams, Gralloc4TestChromaSiting,
+        ::testing::Values(gralloc4::ChromaSiting_None, gralloc4::ChromaSiting_Unknown,
+            gralloc4::ChromaSiting_SitedInterstitial, gralloc4::ChromaSiting_CositedHorizontal,
+            ExtendableType{"", 0},
+            ExtendableType{"vendor.mycompanyname.graphics.common.ChromaSiting", 0xFF},
+            ExtendableType{"vendor.mycompanyname.graphics.common.ChromaSiting", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestChromaSiting, ChromaSiting) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeChromaSiting, gralloc4::decodeChromaSiting));
+}
+
+class Gralloc4TestPlaneLayouts : public testing::Test { };
+
+TEST_F(Gralloc4TestPlaneLayouts, PlaneLayouts) {
+    uint32_t width = 64;
+    uint32_t height = 64;
+
+    std::vector<PlaneLayout> planeLayouts;
+    PlaneLayout planeLayoutA;
+    PlaneLayout planeLayoutRGB;
+    PlaneLayoutComponent component;
+
+    planeLayoutA.offsetInBytes = 0;
+    planeLayoutA.sampleIncrementInBits = 8;
+    planeLayoutA.strideInBytes = width + 20;
+    planeLayoutA.widthInSamples = width;
+    planeLayoutA.heightInSamples = height;
+    planeLayoutA.totalSizeInBytes = planeLayoutA.strideInBytes * height;
+    planeLayoutA.horizontalSubsampling = 1;
+    planeLayoutA.verticalSubsampling = 1;
+    planeLayoutA.crop.left = 0;
+    planeLayoutA.crop.top = 0;
+    planeLayoutA.crop.right = width;
+    planeLayoutA.crop.bottom = height;
+
+    component.type = gralloc4::PlaneLayoutComponentType_A;
+    component.offsetInBits = 0;
+    component.sizeInBits = 8;
+    planeLayoutA.components.push_back(component);
+
+    planeLayouts.push_back(planeLayoutA);
+
+    planeLayoutRGB.offsetInBytes = 0;
+    planeLayoutRGB.sampleIncrementInBits = 32;
+    planeLayoutRGB.strideInBytes = width + 20;
+    planeLayoutRGB.widthInSamples = width;
+    planeLayoutRGB.heightInSamples = height;
+    planeLayoutRGB.totalSizeInBytes = planeLayoutRGB.strideInBytes * height;
+    planeLayoutRGB.horizontalSubsampling = 1;
+    planeLayoutRGB.verticalSubsampling = 1;
+    planeLayoutRGB.crop.left = 0;
+    planeLayoutRGB.crop.top = 0;
+    planeLayoutRGB.crop.right = width;
+    planeLayoutRGB.crop.bottom = height;
+
+    component.type = gralloc4::PlaneLayoutComponentType_R;
+    planeLayoutRGB.components.push_back(component);
+    component.type = gralloc4::PlaneLayoutComponentType_G;
+    planeLayoutRGB.components.push_back(component);
+    component.type = gralloc4::PlaneLayoutComponentType_B;
+    planeLayoutRGB.components.push_back(component);
+
+    planeLayouts.push_back(planeLayoutRGB);
+
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(planeLayouts, gralloc4::encodePlaneLayouts, gralloc4::decodePlaneLayouts));
+}
+
+class Gralloc4TestDataspace : public testing::TestWithParam<Dataspace> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestDataspaceParams, Gralloc4TestDataspace,
+        ::testing::Values(Dataspace::UNKNOWN, Dataspace::ARBITRARY, Dataspace::DISPLAY_P3,
+                          Dataspace::ADOBE_RGB));
+
+TEST_P(Gralloc4TestDataspace, DataspaceRequested) {
+    ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeDataspace, gralloc4::decodeDataspace));
+}
+
+class Gralloc4TestBlendMode : public testing::TestWithParam<BlendMode> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestBlendModeParams, Gralloc4TestBlendMode,
+        ::testing::Values(BlendMode::INVALID, BlendMode::NONE, BlendMode::PREMULTIPLIED,
+                          BlendMode::COVERAGE));
+
+TEST_P(Gralloc4TestBlendMode, BlendMode) {
+    ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeBlendMode, gralloc4::decodeBlendMode));
+}
+
+class Gralloc4TestSmpte2086 : public testing::TestWithParam<std::optional<Smpte2086>> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestSmpte2086Params, Gralloc4TestSmpte2086,
+        ::testing::Values(std::optional<Smpte2086>(Smpte2086{XyColor{0.680, 0.320},
+                                                             XyColor{0.265, 0.690},
+                                                             XyColor{0.150, 0.060},
+                                                             XyColor{0.3127, 0.3290},
+                                                             100.0, 0.1}),
+                          std::optional<Smpte2086>(Smpte2086{XyColor{-1.0, 100.0},
+                                                             XyColor{0xFF, -0xFF},
+                                                             XyColor{999.9, 0.0},
+                                                             XyColor{0.0, -1.0},
+                                                             -0.1, -100.0}),
+                          std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2086, Smpte2086) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2086, gralloc4::decodeSmpte2086));
+}
+
+class Gralloc4TestCta861_3 : public testing::TestWithParam<std::optional<Cta861_3>> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestCta861_3Params, Gralloc4TestCta861_3,
+        ::testing::Values(std::optional<Cta861_3>(Cta861_3{78.0, 62.0}),
+                          std::optional<Cta861_3>(Cta861_3{10.0, 10.0}),
+                          std::optional<Cta861_3>(Cta861_3{0.0, 0.0}),
+                          std::optional<Cta861_3>(Cta861_3{std::numeric_limits<float>::min(), std::numeric_limits<float>::min()}),
+                          std::optional<Cta861_3>(Cta861_3{std::numeric_limits<float>::max(), std::numeric_limits<float>::max()}),
+                          std::nullopt));
+
+TEST_P(Gralloc4TestCta861_3, Cta861_3) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeCta861_3, gralloc4::decodeCta861_3));
+}
+
+class Gralloc4TestSmpte2094_40 : public testing::TestWithParam<std::optional<std::vector<uint8_t>>> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestSmpte2094_40Params, Gralloc4TestSmpte2094_40,
+        ::testing::Values(std::optional<std::vector<uint8_t>>({}),
+                          std::optional<std::vector<uint8_t>>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+                          std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::min(),
+                                                               std::numeric_limits<uint8_t>::min() + 1,
+                                                               std::numeric_limits<uint8_t>::min() + 2,
+                                                               std::numeric_limits<uint8_t>::min() + 3,
+                                                               std::numeric_limits<uint8_t>::min() + 4}),
+                          std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::max(),
+                                                               std::numeric_limits<uint8_t>::max() - 1,
+                                                               std::numeric_limits<uint8_t>::max() - 2,
+                                                               std::numeric_limits<uint8_t>::max() - 3,
+                                                               std::numeric_limits<uint8_t>::max() - 4}),
+                          std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2094_40, Smpte2094_40) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2094_40, gralloc4::decodeSmpte2094_40));
+}
+
+class Gralloc4TestErrors : public testing::Test { };
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestEncodeNull) {
+    ASSERT_NE(NO_ERROR, gralloc4::encodeBufferId(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeName("", nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeWidth(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeHeight(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeLayerCount(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatRequested(PixelFormat::RGBA_8888, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatFourCC(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatModifier(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeUsage(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeAllocationSize(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeProtectedContent(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeCompression(gralloc4::Compression_None, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeInterlaced(gralloc4::Interlaced_None, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeChromaSiting(gralloc4::ChromaSiting_None, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodePlaneLayouts({}, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeDataspace(Dataspace::UNKNOWN, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeBlendMode(BlendMode::NONE, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2086({{}}, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeCta861_3({{}}, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2094_40({{}}, nullptr));
+}
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeNull) {
+    hidl_vec<uint8_t> vec;
+
+    ASSERT_NE(NO_ERROR, gralloc4::decodeBufferId(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeName(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeWidth(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeHeight(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeLayerCount(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatRequested(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeUsage(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeAllocationSize(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeProtectedContent(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeCompression(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeInterlaced(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeChromaSiting(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePlaneLayouts(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeDataspace(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeBlendMode(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, nullptr));
+}
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeBadVec) {
+    hidl_vec<uint8_t> vec = { 0 };
+
+    uint64_t bufferId, width, height, layerCount, pixelFormatModifier, usage, allocationSize,
+            protectedContent;
+    std::string name;
+    PixelFormat pixelFormatRequested;
+    uint32_t pixelFormatFourCC;
+    ExtendableType compression, interlaced, chromaSiting;
+    std::vector<PlaneLayout> planeLayouts;
+    Dataspace dataspace;
+    BlendMode blendMode;
+    std::optional<Smpte2086> smpte2086;
+    std::optional<Cta861_3> cta861_3;
+    std::optional<std::vector<uint8_t>> smpte2094_40;
+
+    ASSERT_NE(NO_ERROR, gralloc4::decodeBufferId(vec, &bufferId));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeName(vec, &name));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeWidth(vec, &width));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeHeight(vec, &height));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeLayerCount(vec, &layerCount));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatRequested(vec, &pixelFormatRequested));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, &pixelFormatFourCC));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, &pixelFormatModifier));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeUsage(vec, &usage));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeAllocationSize(vec, &allocationSize));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeProtectedContent(vec, &protectedContent));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeCompression(vec, &compression));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeInterlaced(vec, &interlaced));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeChromaSiting(vec, &chromaSiting));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &planeLayouts));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeDataspace(vec, &dataspace));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeBlendMode(vec, &blendMode));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, &smpte2086));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, &cta861_3));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, &smpte2094_40));
+}
+
+class Gralloc4TestHelpers : public testing::Test { };
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestIsStandard) {
+    ASSERT_TRUE(gralloc4::isStandardMetadataType(gralloc4::MetadataType_BufferId));
+    ASSERT_TRUE(gralloc4::isStandardCompression(gralloc4::Compression_None));
+    ASSERT_TRUE(gralloc4::isStandardInterlaced(gralloc4::Interlaced_None));
+    ASSERT_TRUE(gralloc4::isStandardChromaSiting(gralloc4::ChromaSiting_None));
+    ASSERT_TRUE(gralloc4::isStandardPlaneLayoutComponentType(gralloc4::PlaneLayoutComponentType_Y));
+}
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestIsNotStandard) {
+    ASSERT_FALSE(gralloc4::isStandardMetadataType({"vendor.mycompanyname.graphics.common.MetadataType", 0}));
+    ASSERT_FALSE(gralloc4::isStandardCompression({"vendor.mycompanyname.graphics.common.Compression", 0}));
+    ASSERT_FALSE(gralloc4::isStandardInterlaced({"vendor.mycompanyname.graphics.common.Interlaced", 0}));
+    ASSERT_FALSE(gralloc4::isStandardChromaSiting({"vendor.mycompanyname.graphics.common.ChromaSiting", 0}));
+    ASSERT_FALSE(gralloc4::isStandardPlaneLayoutComponentType({"vendor.mycompanyname.graphics.common.PlaneLayoutComponentType", 0}));
+}
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestGetStandardValue) {
+    ASSERT_EQ(StandardMetadataType::BUFFER_ID, gralloc4::getStandardMetadataTypeValue(gralloc4::MetadataType_BufferId));
+    ASSERT_EQ(Compression::NONE, gralloc4::getStandardCompressionValue(gralloc4::Compression_None));
+    ASSERT_EQ(Interlaced::NONE, gralloc4::getStandardInterlacedValue(gralloc4::Interlaced_None));
+    ASSERT_EQ(ChromaSiting::NONE, gralloc4::getStandardChromaSitingValue(gralloc4::ChromaSiting_None));
+    ASSERT_EQ(PlaneLayoutComponentType::Y, gralloc4::getStandardPlaneLayoutComponentTypeValue(gralloc4::PlaneLayoutComponentType_Y));
+}
+
+} // namespace android
diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp
index 85137f5..f2d0943 100644
--- a/libs/graphicsenv/GpuStatsInfo.cpp
+++ b/libs/graphicsenv/GpuStatsInfo.cpp
@@ -87,6 +87,7 @@
     if ((status = parcel->writeInt64Vector(angleDriverLoadingTime)) != OK) return status;
     if ((status = parcel->writeBool(cpuVulkanInUse)) != OK) return status;
     if ((status = parcel->writeBool(falsePrerotation)) != OK) return status;
+    if ((status = parcel->writeBool(gles1InUse)) != OK) return status;
     return OK;
 }
 
@@ -99,6 +100,7 @@
     if ((status = parcel->readInt64Vector(&angleDriverLoadingTime)) != OK) return status;
     if ((status = parcel->readBool(&cpuVulkanInUse)) != OK) return status;
     if ((status = parcel->readBool(&falsePrerotation)) != OK) return status;
+    if ((status = parcel->readBool(&gles1InUse)) != OK) return status;
     return OK;
 }
 
@@ -108,6 +110,7 @@
     StringAppendF(&result, "driverVersionCode = %" PRIu64 "\n", driverVersionCode);
     StringAppendF(&result, "cpuVulkanInUse = %d\n", cpuVulkanInUse);
     StringAppendF(&result, "falsePrerotation = %d\n", falsePrerotation);
+    StringAppendF(&result, "gles1InUse = %d\n", gles1InUse);
     result.append("glDriverLoadingTime:");
     for (int32_t loadingTime : glDriverLoadingTime) {
         StringAppendF(&result, " %d", loadingTime);
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 354703b..f07c231 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -145,6 +145,12 @@
 void GraphicsEnv::hintActivityLaunch() {
     ATRACE_CALL();
 
+    {
+        std::lock_guard<std::mutex> lock(mStatsLock);
+        if (mActivityLaunched) return;
+        mActivityLaunched = true;
+    }
+
     std::thread trySendGpuStatsThread([this]() {
         // If there's already graphics driver preloaded in the process, just send
         // the stats info to GpuStats directly through async binder.
@@ -228,12 +234,11 @@
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mStatsLock);
-    const bool doNotSend = mGpuStats.appPackageName.empty();
     if (api == GpuStatsInfo::Api::API_GL) {
-        if (doNotSend) mGpuStats.glDriverToSend = true;
+        mGpuStats.glDriverToSend = true;
         mGpuStats.glDriverLoadingTime = driverLoadingTime;
     } else {
-        if (doNotSend) mGpuStats.vkDriverToSend = true;
+        mGpuStats.vkDriverToSend = true;
         mGpuStats.vkDriverLoadingTime = driverLoadingTime;
     }
 
@@ -250,10 +255,18 @@
     return interface_cast<IGpuService>(binder);
 }
 
+bool GraphicsEnv::readyToSendGpuStatsLocked() {
+    // Only send stats for processes having at least one activity launched and that process doesn't
+    // skip the GraphicsEnvironment setup.
+    return mActivityLaunched && !mGpuStats.appPackageName.empty();
+}
+
 void GraphicsEnv::setTargetStats(const GpuStatsInfo::Stats stats, const uint64_t value) {
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mStatsLock);
+    if (!readyToSendGpuStatsLocked()) return;
+
     const sp<IGpuService> gpuService = getGpuService();
     if (gpuService) {
         gpuService->setTargetStats(mGpuStats.appPackageName, mGpuStats.driverVersionCode, stats,
@@ -265,8 +278,7 @@
                                      int64_t driverLoadingTime) {
     ATRACE_CALL();
 
-    // Do not sendGpuStats for those skipping the GraphicsEnvironment setup
-    if (mGpuStats.appPackageName.empty()) return;
+    if (!readyToSendGpuStatsLocked()) return;
 
     ALOGV("sendGpuStats:\n"
           "\tdriverPackageName[%s]\n"
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 7959652..9aba69f 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -71,6 +71,7 @@
     std::vector<int64_t> angleDriverLoadingTime = {};
     bool cpuVulkanInUse = false;
     bool falsePrerotation = false;
+    bool gles1InUse = false;
 };
 
 /*
@@ -95,6 +96,7 @@
     enum Stats {
         CPU_VULKAN_IN_USE = 0,
         FALSE_PREROTATION = 1,
+        GLES_1_IN_USE = 2,
     };
 
     GpuStatsInfo() = default;
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index c6dc1f8..2219074 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -131,6 +131,8 @@
     void updateUseAngle();
     // Link updatable driver namespace with llndk and vndk-sp libs.
     bool linkDriverNamespaceLocked(android_namespace_t* vndkNamespace);
+    // Check whether this process is ready to send stats.
+    bool readyToSendGpuStatsLocked();
     // Send the initial complete GpuStats to GpuService.
     void sendGpuStatsLocked(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime);
 
@@ -141,6 +143,8 @@
     std::string mSphalLibraries;
     // This mutex protects mGpuStats and get gpuservice call.
     std::mutex mStatsLock;
+    // Cache the activity launch info
+    bool mActivityLaunched = false;
     // Information bookkept for GpuStats.
     GpuStatsInfo mGpuStats;
     // Path to ANGLE libs.
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 1ae148c..5959340 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -40,6 +40,7 @@
     defaults: ["libgui_bufferqueue-defaults"],
 
     srcs: [
+        ":framework_native_aidl",
         ":libgui_bufferqueue_sources",
 
         "BitTube.cpp",
@@ -106,6 +107,10 @@
         "libdvr_headers",
         "libpdx_headers",
     ],
+
+    aidl: {
+        export_aidl_headers: true,
+    }
 }
 
 // Used by media codec services exclusively as a static lib for
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index b9cf9e1..a5e5693 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -17,10 +17,14 @@
 #undef LOG_TAG
 #define LOG_TAG "BLASTBufferQueue"
 
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
 #include <gui/BLASTBufferQueue.h>
 #include <gui/BufferItemConsumer.h>
 #include <gui/GLConsumer.h>
 
+#include <utils/Trace.h>
+
 #include <chrono>
 
 using namespace std::chrono_literals;
@@ -29,23 +33,29 @@
 
 BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height)
       : mSurfaceControl(surface),
-        mPendingCallbacks(0),
         mWidth(width),
         mHeight(height),
         mNextTransaction(nullptr) {
     BufferQueue::createBufferQueue(&mProducer, &mConsumer);
     mConsumer->setMaxBufferCount(MAX_BUFFERS);
     mProducer->setMaxDequeuedBufferCount(MAX_BUFFERS - 1);
+    mConsumer->setMaxAcquiredBufferCount(MAX_ACQUIRED_BUFFERS);
     mBufferItemConsumer =
             new BufferItemConsumer(mConsumer, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, 1, true);
-    mBufferItemConsumer->setName(String8("BLAST Consumer"));
+    static int32_t id = 0;
+    auto name = std::string("BLAST Consumer") + std::to_string(id);
+    id++;
+    mBufferItemConsumer->setName(String8(name.c_str()));
     mBufferItemConsumer->setFrameAvailableListener(this);
     mBufferItemConsumer->setBufferFreedListener(this);
     mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
     mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
     mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint());
 
-    mAcquired = false;
+    mNumAcquired = 0;
+    mNumFrameAvailable = 0;
+    mPendingReleaseItem.item = BufferItem();
+    mPendingReleaseItem.releaseFence = nullptr;
 }
 
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
@@ -70,34 +80,46 @@
 void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
                                            const std::vector<SurfaceControlStats>& stats) {
     std::unique_lock _lock{mMutex};
+    ATRACE_CALL();
 
-    if (stats.size() > 0 && !mShadowQueue.empty()) {
-        mBufferItemConsumer->releaseBuffer(mNextCallbackBufferItem,
-                                           stats[0].previousReleaseFence
-                                                   ? stats[0].previousReleaseFence
+    if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
+        if (stats.size() > 0) {
+            mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
+            mTransformHint = stats[0].transformHint;
+        } else {
+            ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
+            mPendingReleaseItem.releaseFence = nullptr;
+        }
+        mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
+                                           mPendingReleaseItem.releaseFence
+                                                   ? mPendingReleaseItem.releaseFence
                                                    : Fence::NO_FENCE);
-        mAcquired = false;
-        mNextCallbackBufferItem = BufferItem();
-        mBufferItemConsumer->setTransformHint(stats[0].transformHint);
+        mNumAcquired--;
+        mPendingReleaseItem.item = BufferItem();
+        mPendingReleaseItem.releaseFence = nullptr;
     }
-    mPendingCallbacks--;
+
+    if (mSubmitted.empty()) {
+        ALOGE("ERROR: callback with no corresponding submitted buffer item");
+    }
+    mPendingReleaseItem.item = std::move(mSubmitted.front());
+    mSubmitted.pop();
     processNextBufferLocked();
     mCallbackCV.notify_all();
     decStrong((void*)transactionCallbackThunk);
 }
 
 void BLASTBufferQueue::processNextBufferLocked() {
-    if (mShadowQueue.empty()) {
+    ATRACE_CALL();
+    if (mNumFrameAvailable == 0) {
         return;
     }
 
-    if (mAcquired) {
+    if (mSurfaceControl == nullptr) {
+        ALOGE("ERROR : surface control is null");
         return;
     }
 
-    BufferItem item = std::move(mShadowQueue.front());
-    mShadowQueue.pop();
-
     SurfaceComposerClient::Transaction localTransaction;
     bool applyTransaction = true;
     SurfaceComposerClient::Transaction* t = &localTransaction;
@@ -107,33 +129,34 @@
         applyTransaction = false;
     }
 
-    mNextCallbackBufferItem = mLastSubmittedBufferItem;
-    mLastSubmittedBufferItem = BufferItem();
+    BufferItem bufferItem;
 
-    status_t status = mBufferItemConsumer->acquireBuffer(&mLastSubmittedBufferItem, -1, false);
-    mAcquired = true;
+    status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, -1, false);
     if (status != OK) {
-        ALOGE("Failed to acquire?");
-    }
-
-    auto buffer = mLastSubmittedBufferItem.mGraphicBuffer;
-
-    if (buffer == nullptr) {
-        ALOGE("Null buffer");
         return;
     }
+    auto buffer = bufferItem.mGraphicBuffer;
+    mNumFrameAvailable--;
+
+    if (buffer == nullptr) {
+        mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
+        return;
+    }
+
+    mNumAcquired++;
+    mSubmitted.push(bufferItem);
 
     // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
     incStrong((void*)transactionCallbackThunk);
 
     t->setBuffer(mSurfaceControl, buffer);
     t->setAcquireFence(mSurfaceControl,
-                       item.mFence ? new Fence(item.mFence->dup()) : Fence::NO_FENCE);
+                       bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
     t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
 
     t->setFrame(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
-    t->setCrop(mSurfaceControl, computeCrop(mLastSubmittedBufferItem));
-    t->setTransform(mSurfaceControl, mLastSubmittedBufferItem.mTransform);
+    t->setCrop(mSurfaceControl, computeCrop(bufferItem));
+    t->setTransform(mSurfaceControl, bufferItem.mTransform);
 
     if (applyTransaction) {
         t->apply();
@@ -147,13 +170,13 @@
     return item.mCrop;
 }
 
-void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
+void BLASTBufferQueue::onFrameAvailable(const BufferItem& /*item*/) {
+    ATRACE_CALL();
     std::lock_guard _lock{mMutex};
 
     // add to shadow queue
-    mShadowQueue.push(item);
+    mNumFrameAvailable++;
     processNextBufferLocked();
-    mPendingCallbacks++;
 }
 
 void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 3a7cb44..6418e8c 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -44,6 +44,28 @@
 
 namespace android {
 
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...)                                                                           \
+    ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGD(x, ...)                                                                           \
+    ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGI(x, ...)                                                                           \
+    ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGW(x, ...)                                                                           \
+    ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGE(x, ...)                                                                           \
+    ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+
 BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
     mCore(core),
     mSlots(core->mSlots),
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index d6009d6..3b0120b 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -42,6 +42,23 @@
 
 namespace android {
 
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...)                                                                           \
+    ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+          mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGD(x, ...)                                                                           \
+    ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+          mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGI(x, ...)                                                                           \
+    ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+          mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGW(x, ...)                                                                           \
+    ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+          mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGE(x, ...)                                                                           \
+    ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+          mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+
 static String8 getUniqueName() {
     static volatile int32_t counter = 0;
     return String8::format("unnamed-%d-%d", getpid(),
@@ -54,6 +71,19 @@
     return id | counter++;
 }
 
+static status_t getProcessName(int pid, String8& name) {
+    FILE* fp = fopen(String8::format("/proc/%d/cmdline", pid), "r");
+    if (NULL != fp) {
+        const size_t size = 64;
+        char proc_name[size];
+        fgets(proc_name, size, fp);
+        fclose(fp);
+        name = proc_name;
+        return NO_ERROR;
+    }
+    return INVALID_OPERATION;
+}
+
 BufferQueueCore::BufferQueueCore() :
     mMutex(),
     mIsAbandoned(false),
@@ -132,6 +162,20 @@
                             mTransformHintInUse, mAutoPrerotation);
 
     outResult->appendFormat("%sFIFO(%zu):\n", prefix.string(), mQueue.size());
+
+    outResult->appendFormat("%s(mConsumerName=%s, ", prefix.string(), mConsumerName.string());
+
+    outResult->appendFormat("mConnectedApi=%d, mConsumerUsageBits=%" PRIu64 ", ", mConnectedApi,
+                            mConsumerUsageBits);
+
+    String8 producerProcName = String8("\?\?\?");
+    String8 consumerProcName = String8("\?\?\?");
+    int32_t pid = getpid();
+    getProcessName(mConnectedPid, producerProcName);
+    getProcessName(pid, consumerProcName);
+    outResult->appendFormat("mId=%" PRIx64 ", producer=[%d:%s], consumer=[%d:%s])\n", mUniqueId,
+                            mConnectedPid, producerProcName.string(), pid,
+                            consumerProcName.string());
     Fifo::const_iterator current(mQueue.begin());
     while (current != mQueue.end()) {
         double timestamp = current->mTimestamp / 1e9;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index a307d04..e6df757 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -44,6 +44,28 @@
 
 namespace android {
 
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...)                                                                           \
+    ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGD(x, ...)                                                                           \
+    ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGI(x, ...)                                                                           \
+    ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGW(x, ...)                                                                           \
+    ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGE(x, ...)                                                                           \
+    ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+
 static constexpr uint32_t BQ_LAYER_COUNT = 1;
 
 BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
@@ -998,6 +1020,17 @@
         item.mGraphicBuffer.clear();
     }
 
+    // Update and get FrameEventHistory.
+    nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    NewFrameEventsEntry newFrameEventsEntry = {
+        currentFrameNumber,
+        postedTime,
+        requestedPresentTimestamp,
+        std::move(acquireFenceTime)
+    };
+    addAndGetFrameTimestamps(&newFrameEventsEntry,
+            getFrameTimestamps ? &output->frameTimestamps : nullptr);
+
     // Call back without the main BufferQueue lock held, but with the callback
     // lock held so we can ensure that callbacks occur in order
 
@@ -1027,17 +1060,6 @@
         mCallbackCondition.notify_all();
     }
 
-    // Update and get FrameEventHistory.
-    nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
-    NewFrameEventsEntry newFrameEventsEntry = {
-        currentFrameNumber,
-        postedTime,
-        requestedPresentTimestamp,
-        std::move(acquireFenceTime)
-    };
-    addAndGetFrameTimestamps(&newFrameEventsEntry,
-            getFrameTimestamps ? &output->frameTimestamps : nullptr);
-
     // Wait without lock held
     if (connectedApi == NATIVE_WINDOW_API_EGL) {
         // Waiting here allows for two full buffers to be queued but not a
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 54f383e..8af1a1c 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -36,7 +36,10 @@
 DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
                                                ISurfaceComposer::VsyncSource vsyncSource,
                                                ISurfaceComposer::ConfigChanged configChanged)
-      : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
+      : mLooper(looper),
+        mReceiver(vsyncSource, configChanged),
+        mWaitingForVsync(false),
+        mConfigChangeFlag(configChanged) {
     ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
 }
 
@@ -47,17 +50,20 @@
         return result;
     }
 
-    int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL);
-    if (rc < 0) {
-        return UNKNOWN_ERROR;
+    if (mLooper != nullptr) {
+        int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL);
+        if (rc < 0) {
+            return UNKNOWN_ERROR;
+        }
     }
+
     return OK;
 }
 
 void DisplayEventDispatcher::dispose() {
     ALOGV("dispatcher %p ~ Disposing display event dispatcher.", this);
 
-    if (!mReceiver.initCheck()) {
+    if (!mReceiver.initCheck() && mLooper != nullptr) {
         mLooper->removeFd(mReceiver.getFd());
     }
 }
@@ -86,6 +92,22 @@
     return OK;
 }
 
+void DisplayEventDispatcher::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) {
+    if (mConfigChangeFlag == configChangeFlag) {
+        return;
+    }
+    status_t status = mReceiver.toggleConfigEvents(configChangeFlag);
+    if (status) {
+        ALOGW("Failed enable config events, status=%d", status);
+        return;
+    }
+    mConfigChangeFlag = configChangeFlag;
+}
+
+int DisplayEventDispatcher::getFd() {
+    return mReceiver.getFd();
+}
+
 int DisplayEventDispatcher::handleEvent(int, int events, void*) {
     if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
         ALOGE("Display event receiver pipe was closed or an error occurred.  "
@@ -140,7 +162,7 @@
                     break;
                 case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
                     dispatchConfigChanged(ev.header.timestamp, ev.header.displayId,
-                                          ev.config.configId);
+                                          ev.config.configId, ev.config.vsyncPeriod);
                     break;
                 default:
                     ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index b8faa2d..fd6aaf8 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -79,6 +79,14 @@
     return NO_INIT;
 }
 
+status_t DisplayEventReceiver::toggleConfigEvents(
+        ISurfaceComposer::ConfigChanged configChangeFlag) {
+    if (mEventConnection != nullptr) {
+        mEventConnection->toggleConfigEvents(configChangeFlag);
+        return NO_ERROR;
+    }
+    return NO_INIT;
+}
 
 ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
         size_t count) {
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index c0e246f..dda5acf 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -26,7 +26,8 @@
     STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
     SET_VSYNC_RATE,
     REQUEST_NEXT_VSYNC,
-    LAST = REQUEST_NEXT_VSYNC,
+    TOGGLE_CONFIG_EVENTS,
+    LAST = TOGGLE_CONFIG_EVENTS,
 };
 
 } // Anonymous namespace
@@ -53,6 +54,12 @@
         callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
                 Tag::REQUEST_NEXT_VSYNC);
     }
+
+    void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override {
+        callRemoteAsync<decltype(
+                &IDisplayEventConnection::toggleConfigEvents)>(Tag::TOGGLE_CONFIG_EVENTS,
+                                                               configChangeFlag);
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this translation unit (see
@@ -74,6 +81,8 @@
             return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
         case Tag::REQUEST_NEXT_VSYNC:
             return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
+        case Tag::TOGGLE_CONFIG_EVENTS:
+            return callLocalAsync(data, reply, &IDisplayEventConnection::toggleConfigEvents);
     }
 }
 
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 546757b..ab4d51e 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -525,6 +525,88 @@
         return static_cast<status_t>(reply.readInt32());
     }
 
+    virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
+                                                  bool* outSupport) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        status_t result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("getAutoLowLatencyModeSupport failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::GET_AUTO_LOW_LATENCY_MODE_SUPPORT, data,
+                                    &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getAutoLowLatencyModeSupport failed to transact: %d", result);
+            return result;
+        }
+        return reply.readBool(outSupport);
+    }
+
+    virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("setAutoLowLatencyMode failed to writeInterfaceToken: %d", result);
+            return;
+        }
+
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("setAutoLowLatencyMode failed to writeStrongBinder: %d", result);
+            return;
+        }
+        result = data.writeBool(on);
+        if (result != NO_ERROR) {
+            ALOGE("setAutoLowLatencyMode failed to writeBool: %d", result);
+            return;
+        }
+        result = remote()->transact(BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("setAutoLowLatencyMode failed to transact: %d", result);
+            return;
+        }
+    }
+
+    virtual status_t getGameContentTypeSupport(const sp<IBinder>& display, bool* outSupport) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        status_t result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("getGameContentTypeSupport failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::GET_GAME_CONTENT_TYPE_SUPPORT, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getGameContentTypeSupport failed to transact: %d", result);
+            return result;
+        }
+        return reply.readBool(outSupport);
+    }
+
+    virtual void setGameContentType(const sp<IBinder>& display, bool on) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("setGameContentType failed to writeInterfaceToken: %d", result);
+            return;
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("setGameContentType failed to writeStrongBinder: %d", result);
+            return;
+        }
+        result = data.writeBool(on);
+        if (result != NO_ERROR) {
+            ALOGE("setGameContentType failed to writeBool: %d", result);
+            return;
+        }
+        result = remote()->transact(BnSurfaceComposer::SET_GAME_CONTENT_TYPE, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("setGameContentType failed to transact: %d", result);
+        }
+    }
+
     virtual status_t clearAnimationFrameStats() {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -852,61 +934,8 @@
         return error;
     }
 
-    virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                              const std::vector<int32_t>& allowedConfigs) {
-        Parcel data, reply;
-        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        if (result != NO_ERROR) {
-            ALOGE("setAllowedDisplayConfigs failed to writeInterfaceToken: %d", result);
-            return result;
-        }
-        result = data.writeStrongBinder(displayToken);
-        if (result != NO_ERROR) {
-            ALOGE("setAllowedDisplayConfigs failed to writeStrongBinder: %d", result);
-            return result;
-        }
-        result = data.writeInt32Vector(allowedConfigs);
-        if (result != NO_ERROR) {
-            ALOGE("setAllowedDisplayConfigs failed to writeInt32Vector: %d", result);
-            return result;
-        }
-        result = remote()->transact(BnSurfaceComposer::SET_ALLOWED_DISPLAY_CONFIGS, data, &reply);
-        if (result != NO_ERROR) {
-            ALOGE("setAllowedDisplayConfigs failed to transact: %d", result);
-            return result;
-        }
-        return reply.readInt32();
-    }
-
-    virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                              std::vector<int32_t>* outAllowedConfigs) {
-        if (!outAllowedConfigs) return BAD_VALUE;
-        Parcel data, reply;
-        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        if (result != NO_ERROR) {
-            ALOGE("getAllowedDisplayConfigs failed to writeInterfaceToken: %d", result);
-            return result;
-        }
-        result = data.writeStrongBinder(displayToken);
-        if (result != NO_ERROR) {
-            ALOGE("getAllowedDisplayConfigs failed to writeStrongBinder: %d", result);
-            return result;
-        }
-        result = remote()->transact(BnSurfaceComposer::GET_ALLOWED_DISPLAY_CONFIGS, data, &reply);
-        if (result != NO_ERROR) {
-            ALOGE("getAllowedDisplayConfigs failed to transact: %d", result);
-            return result;
-        }
-        result = reply.readInt32Vector(outAllowedConfigs);
-        if (result != NO_ERROR) {
-            ALOGE("getAllowedDisplayConfigs failed to readInt32Vector: %d", result);
-            return result;
-        }
-        return reply.readInt32();
-    }
-
     virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                  int32_t defaultModeId, float minRefreshRate,
+                                                  int32_t defaultConfig, float minRefreshRate,
                                                   float maxRefreshRate) {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -919,9 +948,9 @@
             ALOGE("setDesiredDisplayConfigSpecs: failed to write display token: %d", result);
             return result;
         }
-        result = data.writeInt32(defaultModeId);
+        result = data.writeInt32(defaultConfig);
         if (result != NO_ERROR) {
-            ALOGE("setDesiredDisplayConfigSpecs failed to write defaultModeId: %d", result);
+            ALOGE("setDesiredDisplayConfigSpecs failed to write defaultConfig: %d", result);
             return result;
         }
         result = data.writeFloat(minRefreshRate);
@@ -945,10 +974,10 @@
     }
 
     virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                  int32_t* outDefaultModeId,
+                                                  int32_t* outDefaultConfig,
                                                   float* outMinRefreshRate,
                                                   float* outMaxRefreshRate) {
-        if (!outDefaultModeId || !outMinRefreshRate || !outMaxRefreshRate) return BAD_VALUE;
+        if (!outDefaultConfig || !outMinRefreshRate || !outMaxRefreshRate) return BAD_VALUE;
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         if (result != NO_ERROR) {
@@ -966,9 +995,9 @@
             ALOGE("getDesiredDisplayConfigSpecs failed to transact: %d", result);
             return result;
         }
-        result = reply.readInt32(outDefaultModeId);
+        result = reply.readInt32(outDefaultConfig);
         if (result != NO_ERROR) {
-            ALOGE("getDesiredDisplayConfigSpecs failed to read defaultModeId: %d", result);
+            ALOGE("getDesiredDisplayConfigSpecs failed to read defaultConfig: %d", result);
             return result;
         }
         result = reply.readFloat(outMinRefreshRate);
@@ -1407,6 +1436,75 @@
             result = reply->writeInt32(result);
             return result;
         }
+
+        case GET_AUTO_LOW_LATENCY_MODE_SUPPORT: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("getAutoLowLatencyModeSupport failed to readStrongBinder: %d", result);
+                return result;
+            }
+            bool supported = false;
+            result = getAutoLowLatencyModeSupport(display, &supported);
+            if (result == NO_ERROR) {
+                result = reply->writeBool(supported);
+            }
+            return result;
+        }
+
+        case SET_AUTO_LOW_LATENCY_MODE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("setAutoLowLatencyMode failed to readStrongBinder: %d", result);
+                return result;
+            }
+            bool setAllm = false;
+            result = data.readBool(&setAllm);
+            if (result != NO_ERROR) {
+                ALOGE("setAutoLowLatencyMode failed to readBool: %d", result);
+                return result;
+            }
+            setAutoLowLatencyMode(display, setAllm);
+            return result;
+        }
+
+        case GET_GAME_CONTENT_TYPE_SUPPORT: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("getGameContentTypeSupport failed to readStrongBinder: %d", result);
+                return result;
+            }
+            bool supported = false;
+            result = getGameContentTypeSupport(display, &supported);
+            if (result == NO_ERROR) {
+                result = reply->writeBool(supported);
+            }
+            return result;
+        }
+
+        case SET_GAME_CONTENT_TYPE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("setGameContentType failed to readStrongBinder: %d", result);
+                return result;
+            }
+            bool setGameContentTypeOn = false;
+            result = data.readBool(&setGameContentTypeOn);
+            if (result != NO_ERROR) {
+                ALOGE("setGameContentType failed to readBool: %d", result);
+                return result;
+            }
+            setGameContentType(display, setGameContentTypeOn);
+            return result;
+        }
+
         case CLEAR_ANIMATION_FRAME_STATS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             status_t result = clearAnimationFrameStats();
@@ -1644,31 +1742,13 @@
             }
             return removeRegionSamplingListener(listener);
         }
-        case SET_ALLOWED_DISPLAY_CONFIGS: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IBinder> displayToken = data.readStrongBinder();
-            std::vector<int32_t> allowedConfigs;
-            data.readInt32Vector(&allowedConfigs);
-            status_t result = setAllowedDisplayConfigs(displayToken, allowedConfigs);
-            reply->writeInt32(result);
-            return result;
-        }
-        case GET_ALLOWED_DISPLAY_CONFIGS: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IBinder> displayToken = data.readStrongBinder();
-            std::vector<int32_t> allowedConfigs;
-            status_t result = getAllowedDisplayConfigs(displayToken, &allowedConfigs);
-            reply->writeInt32Vector(allowedConfigs);
-            reply->writeInt32(result);
-            return result;
-        }
         case SET_DESIRED_DISPLAY_CONFIG_SPECS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> displayToken = data.readStrongBinder();
-            int32_t defaultModeId;
-            status_t result = data.readInt32(&defaultModeId);
+            int32_t defaultConfig;
+            status_t result = data.readInt32(&defaultConfig);
             if (result != NO_ERROR) {
-                ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultModeId: %d", result);
+                ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultConfig: %d", result);
                 return result;
             }
             float minRefreshRate;
@@ -1683,7 +1763,7 @@
                 ALOGE("setDesiredDisplayConfigSpecs: failed to read maxRefreshRate: %d", result);
                 return result;
             }
-            result = setDesiredDisplayConfigSpecs(displayToken, defaultModeId, minRefreshRate,
+            result = setDesiredDisplayConfigSpecs(displayToken, defaultConfig, minRefreshRate,
                                                   maxRefreshRate);
             if (result != NO_ERROR) {
                 ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: "
@@ -1697,11 +1777,11 @@
         case GET_DESIRED_DISPLAY_CONFIG_SPECS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> displayToken = data.readStrongBinder();
-            int32_t defaultModeId;
+            int32_t defaultConfig;
             float minRefreshRate;
             float maxRefreshRate;
 
-            status_t result = getDesiredDisplayConfigSpecs(displayToken, &defaultModeId,
+            status_t result = getDesiredDisplayConfigSpecs(displayToken, &defaultConfig,
                                                            &minRefreshRate, &maxRefreshRate);
             if (result != NO_ERROR) {
                 ALOGE("getDesiredDisplayConfigSpecs: failed to get getDesiredDisplayConfigSpecs: "
@@ -1710,9 +1790,9 @@
                 return result;
             }
 
-            result = reply->writeInt32(defaultModeId);
+            result = reply->writeInt32(defaultConfig);
             if (result != NO_ERROR) {
-                ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultModeId: %d", result);
+                ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultConfig: %d", result);
                 return result;
             }
             result = reply->writeFloat(minRefreshRate);
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index 04d2871..b3eb994 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -18,6 +18,8 @@
 #include <binder/Parcel.h>
 #include <gui/LayerMetadata.h>
 
+#include "android/view/LayerMetadataKey.h"
+
 using android::base::StringPrintf;
 
 namespace android {
@@ -113,12 +115,12 @@
 
 std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const {
     if (!has(key)) return std::string();
-    switch (key) {
-        case METADATA_OWNER_UID:
+    switch (static_cast<view::LayerMetadataKey>(key)) {
+        case view::LayerMetadataKey::METADATA_OWNER_UID:
             return StringPrintf("ownerUID%s%d", separator, getInt32(key, 0));
-        case METADATA_WINDOW_TYPE:
+        case view::LayerMetadataKey::METADATA_WINDOW_TYPE:
             return StringPrintf("windowType%s%d", separator, getInt32(key, 0));
-        case METADATA_TASK_ID:
+        case view::LayerMetadataKey::METADATA_TASK_ID:
             return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
         default:
             return StringPrintf("%d%s%dbytes", key, separator,
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index 73150dc..274153c 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -4,4 +4,5 @@
 marissaw@google.com
 mathias@google.com
 racarr@google.com
+steventhomas@google.com
 stoza@google.com
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9d7d7d0..f378fc5 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -484,14 +484,22 @@
         mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator(
                                                                 callbackIds.begin()),
                                                         std::make_move_iterator(callbackIds.end()));
-        // register surface controls for this listener that is merging
-        for (const auto& surfaceControl : surfaceControls) {
-            registerSurfaceControlForCallback(surfaceControl);
-        }
 
-        mListenerCallbacks[listener]
-                .surfaceControls.insert(std::make_move_iterator(surfaceControls.begin()),
-                                        std::make_move_iterator(surfaceControls.end()));
+        mListenerCallbacks[listener].surfaceControls.insert(surfaceControls.begin(),
+                                                            surfaceControls.end());
+
+        auto& currentProcessCallbackInfo =
+                mListenerCallbacks[TransactionCompletedListener::getIInstance()];
+        currentProcessCallbackInfo.surfaceControls
+                .insert(std::make_move_iterator(surfaceControls.begin()),
+                        std::make_move_iterator(surfaceControls.end()));
+
+        // register all surface controls for all callbackIds for this listener that is merging
+        for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) {
+            TransactionCompletedListener::getInstance()
+                    ->addSurfaceControlToCallbacks(surfaceControl,
+                                                   currentProcessCallbackInfo.callbackIds);
+        }
     }
 
     mInputWindowCommands.merge(other.mInputWindowCommands);
@@ -1605,34 +1613,22 @@
     return ComposerService::getComposerService()->setActiveConfig(display, id);
 }
 
-status_t SurfaceComposerClient::setAllowedDisplayConfigs(
-        const sp<IBinder>& displayToken, const std::vector<int32_t>& allowedConfigs) {
-    return ComposerService::getComposerService()->setAllowedDisplayConfigs(displayToken,
-                                                                           allowedConfigs);
-}
-
-status_t SurfaceComposerClient::getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                                         std::vector<int32_t>* outAllowedConfigs) {
-    return ComposerService::getComposerService()->getAllowedDisplayConfigs(displayToken,
-                                                                           outAllowedConfigs);
-}
-
 status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                             int32_t defaultModeId,
+                                                             int32_t defaultConfig,
                                                              float minRefreshRate,
                                                              float maxRefreshRate) {
     return ComposerService::getComposerService()->setDesiredDisplayConfigSpecs(displayToken,
-                                                                               defaultModeId,
+                                                                               defaultConfig,
                                                                                minRefreshRate,
                                                                                maxRefreshRate);
 }
 
 status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                             int32_t* outDefaultModeId,
+                                                             int32_t* outDefaultConfig,
                                                              float* outMinRefreshRate,
                                                              float* outMaxRefreshRate) {
     return ComposerService::getComposerService()->getDesiredDisplayConfigSpecs(displayToken,
-                                                                               outDefaultModeId,
+                                                                               outDefaultConfig,
                                                                                outMinRefreshRate,
                                                                                outMaxRefreshRate);
 }
@@ -1656,6 +1652,26 @@
     return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
 }
 
+bool SurfaceComposerClient::getAutoLowLatencyModeSupport(const sp<IBinder>& display) {
+    bool supported = false;
+    ComposerService::getComposerService()->getAutoLowLatencyModeSupport(display, &supported);
+    return supported;
+}
+
+void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
+    ComposerService::getComposerService()->setAutoLowLatencyMode(display, on);
+}
+
+bool SurfaceComposerClient::getGameContentTypeSupport(const sp<IBinder>& display) {
+    bool supported = false;
+    ComposerService::getComposerService()->getGameContentTypeSupport(display, &supported);
+    return supported;
+}
+
+void SurfaceComposerClient::setGameContentType(const sp<IBinder>& display, bool on) {
+    ComposerService::getComposerService()->setGameContentType(display, on);
+}
+
 void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token,
         int mode) {
     ComposerService::getComposerService()->setPowerMode(token, mode);
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index dd0b470..0a0a03c 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -68,23 +68,25 @@
 
     std::mutex mMutex;
     std::condition_variable mCallbackCV;
-    uint64_t mPendingCallbacks GUARDED_BY(mMutex);
 
     static const int MAX_BUFFERS = 3;
-    struct BufferInfo {
-        sp<GraphicBuffer> buffer;
-        int fence;
+    static const int MAX_ACQUIRED_BUFFERS = 2;
+
+    int32_t mNumFrameAvailable GUARDED_BY(mMutex);
+    int32_t mNumAcquired GUARDED_BY(mMutex);
+
+    struct PendingReleaseItem {
+        BufferItem item;
+        sp<Fence> releaseFence;
     };
 
-    std::queue<const BufferItem> mShadowQueue GUARDED_BY(mMutex);
-    bool mAcquired GUARDED_BY(mMutex);
+    std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
+    PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
 
     int mWidth GUARDED_BY(mMutex);
     int mHeight GUARDED_BY(mMutex);
 
-    BufferItem mLastSubmittedBufferItem GUARDED_BY(mMutex);
-    BufferItem mNextCallbackBufferItem GUARDED_BY(mMutex);
-    sp<Fence> mLastFence GUARDED_BY(mMutex);
+    uint32_t mTransformHint GUARDED_BY(mMutex);
 
     sp<IGraphicBufferConsumer> mConsumer;
     sp<IGraphicBufferProducer> mProducer;
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 3c96089..557c28b 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -34,12 +34,6 @@
 #include <mutex>
 #include <condition_variable>
 
-#define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGI(x, ...) ALOGI("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGW(x, ...) ALOGW("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGE(x, ...) ALOGE("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-
 #define ATRACE_BUFFER_INDEX(index)                                                         \
     do {                                                                                   \
         if (ATRACE_ENABLED()) {                                                            \
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index f0b7ff5..679d572 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -31,6 +31,9 @@
     status_t initialize();
     void dispose();
     status_t scheduleVsync();
+    void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag);
+    int getFd();
+    virtual int handleEvent(int receiveFd, int events, void* data);
 
 protected:
     virtual ~DisplayEventDispatcher() = default;
@@ -39,14 +42,14 @@
     sp<Looper> mLooper;
     DisplayEventReceiver mReceiver;
     bool mWaitingForVsync;
+    ISurfaceComposer::ConfigChanged mConfigChangeFlag;
 
     virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
     virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
                                  bool connected) = 0;
     virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
-                                       int32_t configId) = 0;
+                                       int32_t configId, nsecs_t vsyncPeriod) = 0;
 
-    virtual int handleEvent(int receiveFd, int events, void* data);
     bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
                               uint32_t* outCount);
 };
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index a558cf9..109e28b 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -73,6 +73,7 @@
 
         struct Config {
             int32_t configId;
+            nsecs_t vsyncPeriod;
         };
 
         Header header;
@@ -144,6 +145,11 @@
      */
     status_t requestNextVsync();
 
+    /*
+     * toggleConfigEvents() toggles delivery of config change events.
+     */
+    status_t toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag);
+
 private:
     sp<IDisplayEventConnection> mEventConnection;
     std::unique_ptr<gui::BitTube> mDataChannel;
diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h
index d783f74..8b35ef6 100644
--- a/libs/gui/include/gui/IDisplayEventConnection.h
+++ b/libs/gui/include/gui/IDisplayEventConnection.h
@@ -18,7 +18,7 @@
 
 #include <binder/IInterface.h>
 #include <binder/SafeInterface.h>
-
+#include <gui/ISurfaceComposer.h>
 #include <utils/Errors.h>
 
 #include <cstdint>
@@ -51,6 +51,13 @@
      * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
      */
     virtual void requestNextVsync() = 0; // Asynchronous
+
+    /*
+     * togglesConfigEvents() configures whether or not display config changes
+     * should be propagated.
+     */
+    virtual void toggleConfigEvents(
+            ISurfaceComposer::ConfigChanged configChangeFlag) = 0; // Asynchronous
 };
 
 class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index abe1e3f..25ce1ca 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -286,36 +286,6 @@
     virtual status_t attachBuffer(int* outSlot,
             const sp<GraphicBuffer>& buffer) = 0;
 
-    // queueBuffer indicates that the client has finished filling in the
-    // contents of the buffer associated with slot and transfers ownership of
-    // that slot back to the server.
-    //
-    // It is not valid to call queueBuffer on a slot that is not owned
-    // by the client or one for which a buffer associated via requestBuffer
-    // (an attempt to do so will fail with a return value of BAD_VALUE).
-    //
-    // In addition, the input must be described by the client (as documented
-    // below). Any other properties (zero point, etc)
-    // are client-dependent, and should be documented by the client.
-    //
-    // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
-    //
-    // Upon success, the output will be filled with meaningful values
-    // (refer to the documentation below).
-    //
-    // Return of a value other than NO_ERROR means an error has occurred:
-    // * NO_INIT - the buffer queue has been abandoned or the producer is not
-    //             connected.
-    // * BAD_VALUE - one of the below conditions occurred:
-    //              * fence was NULL
-    //              * scaling mode was unknown
-    //              * both in async mode and buffer count was less than the
-    //                max numbers of buffers that can be allocated at once
-    //              * slot index was out of range (see above).
-    //              * the slot was not in the dequeued state
-    //              * the slot was enqueued without requesting a buffer
-    //              * crop rect is out of bounds of the buffer dimensions
-
     struct QueueBufferInput : public Flattenable<QueueBufferInput> {
         friend class Flattenable<QueueBufferInput>;
         explicit inline QueueBufferInput(const Parcel& parcel);
@@ -415,6 +385,35 @@
         int maxBufferCount{0};
     };
 
+    // queueBuffer indicates that the client has finished filling in the
+    // contents of the buffer associated with slot and transfers ownership of
+    // that slot back to the server.
+    //
+    // It is not valid to call queueBuffer on a slot that is not owned
+    // by the client or one for which a buffer associated via requestBuffer
+    // (an attempt to do so will fail with a return value of BAD_VALUE).
+    //
+    // In addition, the input must be described by the client (as documented
+    // below). Any other properties (zero point, etc)
+    // are client-dependent, and should be documented by the client.
+    //
+    // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+    //
+    // Upon success, the output will be filled with meaningful values
+    // (refer to the documentation below).
+    //
+    // Return of a value other than NO_ERROR means an error has occurred:
+    // * NO_INIT - the buffer queue has been abandoned or the producer is not
+    //             connected.
+    // * BAD_VALUE - one of the below conditions occurred:
+    //              * fence was NULL
+    //              * scaling mode was unknown
+    //              * both in async mode and buffer count was less than the
+    //                max numbers of buffers that can be allocated at once
+    //              * slot index was out of range (see above).
+    //              * the slot was not in the dequeued state
+    //              * the slot was enqueued without requesting a buffer
+    //              * crop rect is out of bounds of the buffer dimensions
     virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
             QueueBufferOutput* output) = 0;
 
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 345425d..998973c 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -195,6 +195,37 @@
             ui::ColorMode colorMode) = 0;
 
     /**
+     * Returns true if the connected display reports support for HDMI 2.1 Auto
+     * Low Latency Mode.
+     * For more information, see the HDMI 2.1 specification.
+     */
+    virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
+                                                  bool* outSupport) const = 0;
+
+    /**
+     * Switches Auto Low Latency Mode on/off on the connected display, if it is
+     * available. This should only be called if #getAutoLowLatencyMode returns
+     * true.
+     * For more information, see the HDMI 2.1 specification.
+     */
+    virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) = 0;
+
+    /**
+     * Returns true if the connected display reports support for Game Content Type.
+     * For more information, see the HDMI 1.4 specification.
+     */
+    virtual status_t getGameContentTypeSupport(const sp<IBinder>& display,
+                                               bool* outSupport) const = 0;
+
+    /**
+     * This will start sending infoframes to the connected display with
+     * ContentType=Game (if on=true). This will switch the disply to Game mode.
+     * This should only be called if #getGameContentTypeSupport returns true.
+     * For more information, see the HDMI 1.4 specification.
+     */
+    virtual void setGameContentType(const sp<IBinder>& display, bool on) = 0;
+
+    /**
      * Capture the specified screen. This requires READ_FRAME_BUFFER
      * permission.  This function will fail if there is a secure window on
      * screen.
@@ -386,31 +417,16 @@
     virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
 
     /*
-     * Sets the allowed display configurations to be used.
-     * The allowedConfigs in a vector of indexes corresponding to the configurations
-     * returned from getDisplayConfigs().
-     */
-    virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                              const std::vector<int32_t>& allowedConfigs) = 0;
-
-    /*
-     * Returns the allowed display configurations currently set.
-     * The allowedConfigs in a vector of indexes corresponding to the configurations
-     * returned from getDisplayConfigs().
-     */
-    virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                              std::vector<int32_t>* outAllowedConfigs) = 0;
-    /*
      * Sets the refresh rate boundaries for display configuration.
      * For all other parameters, default configuration is used. The index for the default is
      * corresponding to the configs returned from getDisplayConfigs().
      */
     virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                  int32_t defaultModeId, float minRefreshRate,
+                                                  int32_t defaultConfig, float minRefreshRate,
                                                   float maxRefreshRate) = 0;
 
     virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                  int32_t* outDefaultModeId,
+                                                  int32_t* outDefaultConfig,
                                                   float* outMinRefreshRate,
                                                   float* outMaxRefreshRate) = 0;
     /*
@@ -523,8 +539,6 @@
         GET_PHYSICAL_DISPLAY_IDS,
         ADD_REGION_SAMPLING_LISTENER,
         REMOVE_REGION_SAMPLING_LISTENER,
-        SET_ALLOWED_DISPLAY_CONFIGS,
-        GET_ALLOWED_DISPLAY_CONFIGS,
         SET_DESIRED_DISPLAY_CONFIG_SPECS,
         GET_DESIRED_DISPLAY_CONFIG_SPECS,
         GET_DISPLAY_BRIGHTNESS_SUPPORT,
@@ -532,6 +546,10 @@
         CAPTURE_SCREEN_BY_ID,
         NOTIFY_POWER_HINT,
         SET_GLOBAL_SHADOW_SETTINGS,
+        GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
+        SET_AUTO_LOW_LATENCY_MODE,
+        GET_GAME_CONTENT_TYPE_SUPPORT,
+        SET_GAME_CONTENT_TYPE,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2c0b143..86468a4 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -117,23 +117,11 @@
     // returned by getDisplayInfo
     static status_t setActiveConfig(const sp<IBinder>& display, int id);
 
-    // Sets the allowed display configurations to be used.
-    // The allowedConfigs in a vector of indexes corresponding to the configurations
-    // returned from getDisplayConfigs().
-    static status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                             const std::vector<int32_t>& allowedConfigs);
-
-    // Returns the allowed display configurations currently set.
-    // The allowedConfigs in a vector of indexes corresponding to the configurations
-    // returned from getDisplayConfigs().
-    static status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                             std::vector<int32_t>* outAllowedConfigs);
-
     // Sets the refresh rate boundaries for display configuration.
     // For all other parameters, default configuration is used. The index for the default is
     // corresponting to the configs returned from getDisplayConfigs().
     static status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                 int32_t defaultModeId, float minRefreshRate,
+                                                 int32_t defaultConfig, float minRefreshRate,
                                                  float maxRefreshRate);
     // Gets the refresh rate boundaries for display configuration.
     // For all other parameters, default configuration is used. The index for the default is
@@ -141,7 +129,7 @@
     // The reason is passed in for telemetry tracking, and it corresponds to the list of all
     // the policy rules that were used.
     static status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                 int32_t* outDefaultModeId,
+                                                 int32_t* outDefaultConfig,
                                                  float* outMinRefreshRate,
                                                  float* outMaxRefreshRate);
 
@@ -160,6 +148,21 @@
     static status_t setActiveColorMode(const sp<IBinder>& display,
             ui::ColorMode colorMode);
 
+    // Reports whether the connected display supports Auto Low Latency Mode
+    static bool getAutoLowLatencyModeSupport(const sp<IBinder>& display);
+
+    // Switches on/off Auto Low Latency Mode on the connected display. This should only be
+    // called if the connected display supports Auto Low Latency Mode as reported by
+    // #getAutoLowLatencyModeSupport
+    static void setAutoLowLatencyMode(const sp<IBinder>& display, bool on);
+
+    // Reports whether the connected display supports Game content type
+    static bool getGameContentTypeSupport(const sp<IBinder>& display);
+
+    // Turns Game mode on/off on the connected display. This should only be called
+    // if the display supports Game content type, as reported by #getGameContentTypeSupport
+    static void setGameContentType(const sp<IBinder>& display, bool on);
+
     /* Triggers screen on/off or low power mode and waits for it to complete */
     static void setDisplayPowerMode(const sp<IBinder>& display, int mode);
 
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index 48a5cb6..0f618f1 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -69,7 +69,7 @@
 
     void waitForCallbacks() {
         std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
-        while (mBlastBufferQueueAdapter->mPendingCallbacks > 0) {
+        while (mBlastBufferQueueAdapter->mSubmitted.size() > 0) {
             mBlastBufferQueueAdapter->mCallbackCV.wait(lock);
         }
     }
@@ -302,7 +302,7 @@
         igbProducer->cancelBuffer(allocated[i].first, allocated[i].second);
     }
 
-    for (int i = 0; i < 10; i++) {
+    for (int i = 0; i < 100; i++) {
         int slot;
         sp<Fence> fence;
         sp<GraphicBuffer> buf;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 3d90369..0445755 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -750,6 +750,16 @@
                            bool /*captureSecureLayers*/) override {
         return NO_ERROR;
     }
+    status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/,
+                                          bool* /*outSupport*/) const override {
+        return NO_ERROR;
+    }
+    void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
+    status_t getGameContentTypeSupport(const sp<IBinder>& /*display*/,
+                                       bool* /*outSupport*/) const override {
+        return NO_ERROR;
+    }
+    void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
     status_t captureScreen(uint64_t /*displayOrLayerStack*/, ui::Dataspace* /*outDataspace*/,
                            sp<GraphicBuffer>* /*outBuffer*/) override {
         return NO_ERROR;
@@ -823,21 +833,13 @@
             const sp<IRegionSamplingListener>& /*listener*/) override {
         return NO_ERROR;
     }
-    status_t setAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/,
-                                      const std::vector<int32_t>& /*allowedConfigs*/) override {
-        return NO_ERROR;
-    }
-    status_t getAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/,
-                                      std::vector<int32_t>* /*outAllowedConfigs*/) override {
-        return NO_ERROR;
-    }
     status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
-                                          int32_t /*defaultModeId*/, float /*minRefreshRate*/,
-                                          float /*maxRefreshRate*/) override {
+                                          int32_t /*defaultConfig*/, float /*minRefreshRate*/,
+                                          float /*maxRefreshRate*/) {
         return NO_ERROR;
     }
     status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
-                                          int32_t* /*outDefaultModeId*/,
+                                          int32_t* /*outDefaultConfig*/,
                                           float* /*outMinRefreshRate*/,
                                           float* /*outMaxRefreshRate*/) override {
         return NO_ERROR;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 200e1f3..d53a557 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -301,8 +301,8 @@
     if (nWrite < 0) {
         int error = errno;
 #if DEBUG_CHANNEL_MESSAGES
-        ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.c_str(),
-                msg->header.type, error);
+        ALOGD("channel '%s' ~ error sending message of type %d, %s", mName.c_str(),
+              msg->header.type, strerror(error));
 #endif
         if (error == EAGAIN || error == EWOULDBLOCK) {
             return WOULD_BLOCK;
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index ec28757..03ca459 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -73,6 +73,7 @@
     status_t s = output.writeStrongBinder(token);
     if (s != OK) return s;
 
+    output.writeInt32(id);
     output.writeString8(String8(name.c_str()));
     output.writeInt32(layoutParamsFlags);
     output.writeInt32(layoutParamsType);
@@ -90,7 +91,6 @@
     output.writeBool(hasFocus);
     output.writeBool(hasWallpaper);
     output.writeBool(paused);
-    output.writeInt32(layer);
     output.writeInt32(ownerPid);
     output.writeInt32(ownerUid);
     output.writeInt32(inputFeatures);
@@ -116,6 +116,7 @@
     }
 
     ret.token = token;
+    ret.id = from.readInt32();
     ret.name = from.readString8().c_str();
     ret.layoutParamsFlags = from.readInt32();
     ret.layoutParamsType = from.readInt32();
@@ -133,7 +134,6 @@
     ret.hasFocus = from.readBool();
     ret.hasWallpaper = from.readBool();
     ret.paused = from.readBool();
-    ret.layer = from.readInt32();
     ret.ownerPid = from.readInt32();
     ret.ownerUid = from.readInt32();
     ret.inputFeatures = from.readInt32();
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 6db18ab..d1cb527 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -40,6 +40,7 @@
     sp<IBinder> touchableRegionCropHandle = new BBinder();
     InputWindowInfo i;
     i.token = new BBinder();
+    i.id = 1;
     i.name = "Foobar";
     i.layoutParamsFlags = 7;
     i.layoutParamsType = 39;
@@ -57,7 +58,6 @@
     i.hasFocus = false;
     i.hasWallpaper = false;
     i.paused = false;
-    i.layer = 7;
     i.ownerPid = 19;
     i.ownerUid = 24;
     i.inputFeatures = 29;
@@ -72,6 +72,7 @@
     p.setDataPosition(0);
     InputWindowInfo i2 = InputWindowInfo::read(p);
     ASSERT_EQ(i.token, i2.token);
+    ASSERT_EQ(i.id, i2.id);
     ASSERT_EQ(i.name, i2.name);
     ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
     ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
@@ -89,7 +90,6 @@
     ASSERT_EQ(i.hasFocus, i2.hasFocus);
     ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
     ASSERT_EQ(i.paused, i2.paused);
-    ASSERT_EQ(i.layer, i2.layer);
     ASSERT_EQ(i.ownerPid, i2.ownerPid);
     ASSERT_EQ(i.ownerUid, i2.ownerUid);
     ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 05ff93e..15d937e 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -17,11 +17,7 @@
 #define LOG_TAG "Choreographer"
 //#define LOG_NDEBUG 0
 
-#include <cinttypes>
-#include <queue>
-#include <thread>
-
-#include <android/choreographer.h>
+#include <apex/choreographer.h>
 #include <gui/DisplayEventDispatcher.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
@@ -29,6 +25,11 @@
 #include <utils/Mutex.h>
 #include <utils/Timers.h>
 
+#include <cinttypes>
+#include <optional>
+#include <queue>
+#include <thread>
+
 namespace android {
 
 static inline const char* toString(bool value) {
@@ -48,11 +49,18 @@
     }
 };
 
+struct RefreshRateCallback {
+    AChoreographer_refreshRateCallback callback;
+    void* data;
+};
 
 class Choreographer : public DisplayEventDispatcher, public MessageHandler {
 public:
+    explicit Choreographer(const sp<Looper>& looper);
     void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
                                   AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
+    void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
+    void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb);
 
     enum {
         MSG_SCHEDULE_CALLBACKS = 0,
@@ -61,28 +69,30 @@
     virtual void handleMessage(const Message& message) override;
 
     static Choreographer* getForThread();
-
-protected:
     virtual ~Choreographer() = default;
 
 private:
-    explicit Choreographer(const sp<Looper>& looper);
     Choreographer(const Choreographer&) = delete;
 
     void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
     void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
-    void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
-                               int32_t configId) override;
+    void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId,
+                               nsecs_t vsyncPeriod) override;
 
     void scheduleCallbacks();
 
     // Protected by mLock
-    std::priority_queue<FrameCallback> mCallbacks;
+    std::priority_queue<FrameCallback> mFrameCallbacks;
+
+    // Protected by mLock
+    std::vector<RefreshRateCallback> mRefreshRateCallbacks;
+    nsecs_t mVsyncPeriod = 0;
 
     mutable Mutex mLock;
 
     const sp<Looper> mLooper;
     const std::thread::id mThreadId;
+    const std::optional<PhysicalDisplayId> mInternalDisplayId;
 };
 
 
@@ -104,9 +114,11 @@
     return gChoreographer;
 }
 
-Choreographer::Choreographer(const sp<Looper>& looper) :
-    DisplayEventDispatcher(looper), mLooper(looper), mThreadId(std::this_thread::get_id()) {
-}
+Choreographer::Choreographer(const sp<Looper>& looper)
+      : DisplayEventDispatcher(looper),
+        mLooper(looper),
+        mThreadId(std::this_thread::get_id()),
+        mInternalDisplayId(SurfaceComposerClient::getInternalDisplayId()) {}
 
 void Choreographer::postFrameCallbackDelayed(
         AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
@@ -114,25 +126,55 @@
     FrameCallback callback{cb, cb64, data, now + delay};
     {
         AutoMutex _l{mLock};
-        mCallbacks.push(callback);
+        mFrameCallbacks.push(callback);
     }
     if (callback.dueTime <= now) {
         if (std::this_thread::get_id() != mThreadId) {
-            Message m{MSG_SCHEDULE_VSYNC};
-            mLooper->sendMessage(this, m);
+            if (mLooper != nullptr) {
+                Message m{MSG_SCHEDULE_VSYNC};
+                mLooper->sendMessage(this, m);
+            } else {
+                scheduleVsync();
+            }
         } else {
             scheduleVsync();
         }
     } else {
-        Message m{MSG_SCHEDULE_CALLBACKS};
-        mLooper->sendMessageDelayed(delay, this, m);
+        if (mLooper != nullptr) {
+            Message m{MSG_SCHEDULE_CALLBACKS};
+            mLooper->sendMessageDelayed(delay, this, m);
+        } else {
+            scheduleCallbacks();
+        }
+    }
+}
+
+void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
+    {
+        AutoMutex _l{mLock};
+        mRefreshRateCallbacks.emplace_back(RefreshRateCallback{cb, data});
+        toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedDispatch);
+    }
+}
+
+void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb) {
+    {
+        AutoMutex _l{mLock};
+        mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
+                                                   mRefreshRateCallbacks.end(),
+                                                   [&](const RefreshRateCallback& callback) {
+                                                       return cb == callback.callback;
+                                                   }));
+        if (mRefreshRateCallbacks.empty()) {
+            toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedSuppress);
+        }
     }
 }
 
 void Choreographer::scheduleCallbacks() {
     AutoMutex _{mLock};
     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-    if (mCallbacks.top().dueTime <= now) {
+    if (mFrameCallbacks.top().dueTime <= now) {
         ALOGV("choreographer %p ~ scheduling vsync", this);
         scheduleVsync();
         return;
@@ -147,9 +189,9 @@
     {
         AutoMutex _l{mLock};
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
-        while (!mCallbacks.empty() && mCallbacks.top().dueTime < now) {
-            callbacks.push_back(mCallbacks.top());
-            mCallbacks.pop();
+        while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
+            callbacks.push_back(mFrameCallbacks.top());
+            mFrameCallbacks.pop();
         }
     }
     for (const auto& cb : callbacks) {
@@ -167,11 +209,25 @@
             this, displayId, toString(connected));
 }
 
-void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId,
-                                          int32_t configId) {
-    ALOGV("choreographer %p ~ received config changed event (displayId=%"
-            ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%s), ignoring.",
-            this, displayId, toString(configId));
+// TODO(b/74619554): The PhysicalDisplayId is ignored because currently
+// Choreographer only supports dispatching VSYNC events for the internal
+// display, so as such Choreographer does not support the notion of multiple
+// displays. When multi-display choreographer is properly supported, then
+// PhysicalDisplayId should no longer be ignored.
+void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId, int32_t,
+                                          nsecs_t vsyncPeriod) {
+    {
+        AutoMutex _l{mLock};
+        for (const auto& cb : mRefreshRateCallbacks) {
+            // Only perform the callback when the old refresh rate is different
+            // from the new refresh rate, so that we don't dispatch the callback
+            // on every single configuration change.
+            if (mVsyncPeriod != vsyncPeriod) {
+                cb.callback(vsyncPeriod, cb.data);
+                mVsyncPeriod = vsyncPeriod;
+            }
+        }
+    }
 }
 
 void Choreographer::handleMessage(const Message& message) {
@@ -189,7 +245,7 @@
 
 /* Glue for the NDK interface */
 
-using android::Choreographer;
+using namespace android;
 
 static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
     return reinterpret_cast<Choreographer*>(choreographer);
@@ -223,3 +279,41 @@
     AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
             nullptr, callback, data, ms2ns(delayMillis));
 }
+void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
+                                                AChoreographer_refreshRateCallback callback,
+                                                void* data) {
+    AChoreographer_to_Choreographer(choreographer)->registerRefreshRateCallback(callback, data);
+}
+void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
+                                                  AChoreographer_refreshRateCallback callback) {
+    AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback);
+}
+
+AChoreographer* AChoreographer_create() {
+    Choreographer* choreographer = new Choreographer(nullptr);
+    status_t result = choreographer->initialize();
+    if (result != OK) {
+        ALOGW("Failed to initialize");
+        return nullptr;
+    }
+    return Choreographer_to_AChoreographer(choreographer);
+}
+
+void AChoreographer_destroy(AChoreographer* choreographer) {
+    if (choreographer == nullptr) {
+        return;
+    }
+
+    delete AChoreographer_to_Choreographer(choreographer);
+}
+
+int AChoreographer_getFd(AChoreographer* choreographer) {
+    return AChoreographer_to_Choreographer(choreographer)->getFd();
+}
+
+void AChoreographer_handlePendingEvents(AChoreographer* choreographer, void* data) {
+    // Pass dummy fd and events args to handleEvent, since the underlying
+    // DisplayEventDispatcher doesn't need them outside of validating that a
+    // Looper instance didn't break, but these args circumvent those checks.
+    AChoreographer_to_Choreographer(choreographer)->handleEvent(-1, Looper::EVENT_INPUT, data);
+}
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index 7a497ea..d37b9a1 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 ndk_headers {
-    name: "libachoreographer_ndk_headers",
+    name: "libnativedisplay_ndk_headers",
     from: "include/android",
     to: "android",
     srcs: ["include/android/*.h"],
diff --git a/libs/nativedisplay/include/apex/choreographer.h b/libs/nativedisplay/include/apex/choreographer.h
new file mode 100644
index 0000000..5251fd3
--- /dev/null
+++ b/libs/nativedisplay/include/apex/choreographer.h
@@ -0,0 +1,78 @@
+/*
+ * 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/choreographer.h>
+#include <inttypes.h>
+
+__BEGIN_DECLS
+
+/**
+ * Prototype of the function that is called when the display refresh rate
+ * changes. It's passed the new vsync period in nanoseconds, as well as the data
+ * pointer provided by the application that registered a callback.
+ */
+typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, void* data);
+
+/**
+ * Registers a callback to be run when the display refresh rate changes.
+ */
+void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
+                                                AChoreographer_refreshRateCallback, void* data);
+
+/**
+ * Unregisters a callback to be run when the display refresh rate changes.
+ */
+void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
+                                                  AChoreographer_refreshRateCallback);
+
+/**
+ * Creates an instance of AChoreographer.
+ *
+ * The key differences between this method and AChoreographer_getInstance are:
+ * 1. The returned AChoreographer instance is not a thread-local, and
+ * 2. This method does not require an existing ALooper attached to the thread.
+ */
+AChoreographer* AChoreographer_create();
+
+/**
+ * Destroys a choreographer instance created from AChoreographer_create.
+ */
+void AChoreographer_destroy(AChoreographer* choreographer);
+
+/**
+ * Returns the underlying file descriptor associated with this choreographer
+ * instance.
+ *
+ * The caller can listen to the file descriptor to respond to any AChoreographer
+ * events. One such way is registering the file descriptor to a Looper instance,
+ * although this is not a requirement.
+ */
+int AChoreographer_getFd(AChoreographer* choreographer);
+
+/**
+ * Provides a callback to handle all pending events emitted by this
+ * choreographer instance. Specifically, this delegates to the callbacks
+ * previously registered to choreographer.
+ *
+ * If the associated file descriptor is attached to a Looper instance, then the
+ * callback attached to that Looper is expected to handle exceptional Looper
+ * events.
+ */
+void AChoreographer_handlePendingEvents(AChoreographer* choreographer, void* data);
+
+__END_DECLS
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
index 3b29c18..3b6a241 100644
--- a/libs/nativedisplay/libnativedisplay.map.txt
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -5,6 +5,8 @@
     AChoreographer_postFrameCallbackDelayed; # apex # introduced=30
     AChoreographer_postFrameCallback64; # apex # introduced=30
     AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30
+    AChoreographer_registerRefreshRateCallback; # apex # introduced=30
+    AChoreographer_unregisterRefreshRateCallback; # apex # introduced=30
   local:
     *;
 };
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 0ba01f4..842af18 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -138,6 +138,11 @@
     static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB));
     static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
     static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ));
+    static_assert(static_cast<int>(ADATASPACE_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_ADOBE_RGB));
+    static_assert(static_cast<int>(ADATASPACE_BT2020) == static_cast<int>(HAL_DATASPACE_BT2020));
+    static_assert(static_cast<int>(ADATASPACE_BT709) == static_cast<int>(HAL_DATASPACE_V0_BT709));
+    static_assert(static_cast<int>(ADATASPACE_DCI_P3) == static_cast<int>(HAL_DATASPACE_DCI_P3));
+    static_assert(static_cast<int>(ADATASPACE_SRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SRGB_LINEAR));
 
     if (!window || !query(window, NATIVE_WINDOW_IS_VALID) ||
             !isDataSpaceValid(window, dataSpace)) {
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index 2899bcf..e759513 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -101,6 +101,56 @@
      * Use full range, SMPTE 2084 (PQ) transfer and BT2020 standard
      */
     ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
+
+    /**
+     * Adobe RGB
+     *
+     * Use full range, gamma 2.2 transfer and Adobe RGB primaries
+     * Note: Application is responsible for gamma encoding the data as
+     * a 2.2 gamma encoding is not supported in HW.
+     */
+    ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL
+
+    /**
+     * ITU-R Recommendation 2020 (BT.2020)
+     *
+     * Ultra High-definition television
+     *
+     * Use full range, BT.709 transfer and BT2020 standard
+     */
+    ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL
+
+    /**
+     * ITU-R Recommendation 709 (BT.709)
+     *
+     * High-definition television
+     *
+     * Use limited range, BT.709 transfer and BT.709 standard.
+     */
+    ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+
+    /**
+     * SMPTE EG 432-1 and SMPTE RP 431-2.
+     *
+     * Digital Cinema DCI-P3
+     *
+     * Use full range, gamma 2.6 transfer and D65 DCI-P3 standard
+     * Note: Application is responsible for gamma encoding the data as
+     * a 2.6 gamma encoding is not supported in HW.
+     */
+    ADATASPACE_DCI_P3 = 155844608, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL
+
+    /**
+     * sRGB linear encoding:
+     *
+     * The red, green, and blue components are stored in sRGB space, but
+     * are linear, not gamma-encoded.
+     * The RGB primaries and the white point are the same as BT.709.
+     *
+     * The values are encoded using the full range ([0,255] for 8-bit) for all
+     * components.
+     */
+    ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
 };
 
 __END_DECLS
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 136ad0d..348377e 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -52,6 +52,8 @@
         "gl/GLExtensions.cpp",
         "gl/GLFramebuffer.cpp",
         "gl/GLImage.cpp",
+        "gl/GLShadowVertexGenerator.cpp",
+        "gl/GLSkiaShadowPort.cpp",
         "gl/ImageManager.cpp",
         "gl/Program.cpp",
         "gl/ProgramCache.cpp",
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index d1378d5..0748dfb 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -46,6 +46,7 @@
 #include "GLExtensions.h"
 #include "GLFramebuffer.h"
 #include "GLImage.h"
+#include "GLShadowVertexGenerator.h"
 #include "Program.h"
 #include "ProgramCache.h"
 
@@ -1046,7 +1047,8 @@
         setSourceDataSpace(layer.sourceDataspace);
 
         if (layer.shadow.length > 0.0f) {
-            // handle shadows
+            handleShadow(layer.geometry.boundaries, layer.geometry.roundedCornersRadius,
+                         layer.shadow);
         }
         // We only want to do a special handling for rounded corners when having rounded corners
         // is the only reason it needs to turn on blending, otherwise, we handle it like the
@@ -1561,6 +1563,35 @@
     }
 }
 
+void GLESRenderEngine::handleShadow(const FloatRect& casterRect, float casterCornerRadius,
+                                    const ShadowSettings& settings) {
+    ATRACE_CALL();
+    const float casterZ = settings.length / 2.0f;
+    const GLShadowVertexGenerator shadows(casterRect, casterCornerRadius, casterZ,
+                                          settings.casterIsTranslucent, settings.ambientColor,
+                                          settings.spotColor, settings.lightPos,
+                                          settings.lightRadius);
+
+    // setup mesh for both shadows
+    Mesh mesh = Mesh::Builder()
+                        .setPrimitive(Mesh::TRIANGLES)
+                        .setVertices(shadows.getVertexCount(), 2 /* size */)
+                        .setShadowAttrs()
+                        .setIndices(shadows.getIndexCount())
+                        .build();
+
+    Mesh::VertexArray<vec2> position = mesh.getPositionArray<vec2>();
+    Mesh::VertexArray<vec4> shadowColor = mesh.getShadowColorArray<vec4>();
+    Mesh::VertexArray<vec3> shadowParams = mesh.getShadowParamsArray<vec3>();
+    shadows.fillVertices(position, shadowColor, shadowParams);
+    shadows.fillIndices(mesh.getIndicesArray());
+
+    mState.cornerRadius = 0.0f;
+    mState.drawShadows = true;
+    drawMesh(mesh);
+    mState.drawShadows = false;
+}
+
 } // namespace gl
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 397bc16..f41eda2 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -148,6 +148,8 @@
     bool waitFence(base::unique_fd fenceFd);
     void clearWithColor(float red, float green, float blue, float alpha);
     void fillRegionWithColor(const Region& region, float red, float green, float blue, float alpha);
+    void handleShadow(const FloatRect& casterRect, float casterCornerRadius,
+                      const ShadowSettings& shadowSettings);
     void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
                             const half4& color, float cornerRadius);
     void setupLayerTexturing(const Texture& texture);
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.cpp b/libs/renderengine/gl/GLShadowVertexGenerator.cpp
new file mode 100644
index 0000000..3181f9b
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowVertexGenerator.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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 <renderengine/Mesh.h>
+
+#include <math/vec4.h>
+
+#include <ui/Rect.h>
+#include <ui/Transform.h>
+
+#include "GLShadowVertexGenerator.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLShadowVertexGenerator::GLShadowVertexGenerator(const FloatRect& casterRect,
+                                                 float casterCornerRadius, float casterZ,
+                                                 bool casterIsTranslucent, const vec4& ambientColor,
+                                                 const vec4& spotColor, const vec3& lightPosition,
+                                                 float lightRadius) {
+    mDrawAmbientShadow = ambientColor.a > 0.f;
+    mDrawSpotShadow = spotColor.a > 0.f;
+
+    // Generate geometries and find number of vertices to generate
+    if (mDrawAmbientShadow) {
+        mAmbientShadowGeometry = getAmbientShadowGeometry(casterRect, casterCornerRadius, casterZ,
+                                                          casterIsTranslucent, ambientColor);
+        mAmbientShadowVertexCount = getVertexCountForGeometry(*mAmbientShadowGeometry.get());
+        mAmbientShadowIndexCount = getIndexCountForGeometry(*mAmbientShadowGeometry.get());
+    } else {
+        mAmbientShadowVertexCount = 0;
+        mAmbientShadowIndexCount = 0;
+    }
+
+    if (mDrawSpotShadow) {
+        mSpotShadowGeometry =
+                getSpotShadowGeometry(casterRect, casterCornerRadius, casterZ, casterIsTranslucent,
+                                      spotColor, lightPosition, lightRadius);
+        mSpotShadowVertexCount = getVertexCountForGeometry(*mSpotShadowGeometry.get());
+        mSpotShadowIndexCount = getIndexCountForGeometry(*mSpotShadowGeometry.get());
+    } else {
+        mSpotShadowVertexCount = 0;
+        mSpotShadowIndexCount = 0;
+    }
+}
+
+size_t GLShadowVertexGenerator::getVertexCount() const {
+    return mAmbientShadowVertexCount + mSpotShadowVertexCount;
+}
+
+size_t GLShadowVertexGenerator::getIndexCount() const {
+    return mAmbientShadowIndexCount + mSpotShadowIndexCount;
+}
+
+void GLShadowVertexGenerator::fillVertices(Mesh::VertexArray<vec2>& position,
+                                           Mesh::VertexArray<vec4>& color,
+                                           Mesh::VertexArray<vec3>& params) const {
+    if (mDrawAmbientShadow) {
+        fillVerticesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowVertexCount, position,
+                                color, params);
+    }
+    if (mDrawSpotShadow) {
+        fillVerticesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowVertexCount,
+                                Mesh::VertexArray<vec2>(position, mAmbientShadowVertexCount),
+                                Mesh::VertexArray<vec4>(color, mAmbientShadowVertexCount),
+                                Mesh::VertexArray<vec3>(params, mAmbientShadowVertexCount));
+    }
+}
+
+void GLShadowVertexGenerator::fillIndices(uint16_t* indices) const {
+    if (mDrawAmbientShadow) {
+        fillIndicesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowIndexCount,
+                               0 /* starting vertex offset */, indices);
+    }
+    if (mDrawSpotShadow) {
+        fillIndicesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowIndexCount,
+                               mAmbientShadowVertexCount /* starting vertex offset */,
+                               &(indices[mAmbientShadowIndexCount]));
+    }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.h b/libs/renderengine/gl/GLShadowVertexGenerator.h
new file mode 100644
index 0000000..112f976
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowVertexGenerator.h
@@ -0,0 +1,65 @@
+/*
+ * 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 <math/vec4.h>
+#include <ui/Rect.h>
+
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+
+class Mesh;
+
+namespace gl {
+
+/**
+ * Generates gl attributes required to draw shadow spot and/or ambient shadows.
+ *
+ * Each shadow can support different colors. This class generates three vertex attributes for
+ * each shadow, its position, color and shadow params(offset and distance). These can be sent
+ * using a single glDrawElements call.
+ */
+class GLShadowVertexGenerator {
+public:
+    GLShadowVertexGenerator(const FloatRect& casterRect, float casterCornerRadius, float casterZ,
+                            bool casterIsTranslucent, const vec4& ambientColor,
+                            const vec4& spotColor, const vec3& lightPosition, float lightRadius);
+    ~GLShadowVertexGenerator() = default;
+
+    size_t getVertexCount() const;
+    size_t getIndexCount() const;
+    void fillVertices(Mesh::VertexArray<vec2>& position, Mesh::VertexArray<vec4>& color,
+                      Mesh::VertexArray<vec3>& params) const;
+    void fillIndices(uint16_t* indices) const;
+
+private:
+    bool mDrawAmbientShadow;
+    std::unique_ptr<Geometry> mAmbientShadowGeometry;
+    int mAmbientShadowVertexCount = 0;
+    int mAmbientShadowIndexCount = 0;
+
+    bool mDrawSpotShadow;
+    std::unique_ptr<Geometry> mSpotShadowGeometry;
+    int mSpotShadowVertexCount = 0;
+    int mSpotShadowIndexCount = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.cpp b/libs/renderengine/gl/GLSkiaShadowPort.cpp
new file mode 100644
index 0000000..224ce6c
--- /dev/null
+++ b/libs/renderengine/gl/GLSkiaShadowPort.cpp
@@ -0,0 +1,649 @@
+/*
+ * 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 <math/vec4.h>
+
+#include <renderengine/Mesh.h>
+
+#include <ui/Rect.h>
+#include <ui/Transform.h>
+
+#include <utils/Log.h>
+
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+/**
+ * The shadow geometry logic and vertex generation code has been ported from skia shadow
+ * fast path OpenGL implementation to draw shadows around rects and rounded rects including
+ * circles.
+ *
+ * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
+ *
+ * Modifications made:
+ * - Switched to using std lib math functions
+ * - Fall off function is implemented in vertex shader rather than a shadow texture
+ * - Removed transformations applied on the caster rect since the caster will be in local
+ *   coordinate space and will be transformed by the vertex shader.
+ */
+
+static inline float divide_and_pin(float numer, float denom, float min, float max) {
+    if (denom == 0.0f) return min;
+    return std::clamp(numer / denom, min, max);
+}
+
+static constexpr auto SK_ScalarSqrt2 = 1.41421356f;
+static constexpr auto kAmbientHeightFactor = 1.0f / 128.0f;
+static constexpr auto kAmbientGeomFactor = 64.0f;
+// Assuming that we have a light height of 600 for the spot shadow,
+// the spot values will reach their maximum at a height of approximately 292.3077.
+// We'll round up to 300 to keep it simple.
+static constexpr auto kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor;
+
+inline float AmbientBlurRadius(float height) {
+    return std::min(height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius);
+}
+inline float AmbientRecipAlpha(float height) {
+    return 1.0f + std::max(height * kAmbientHeightFactor, 0.0f);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Circle Data
+//
+// We have two possible cases for geometry for a circle:
+
+// In the case of a normal fill, we draw geometry for the circle as an octagon.
+static const uint16_t gFillCircleIndices[] = {
+        // enter the octagon
+        // clang-format off
+         0, 1, 8, 1, 2, 8,
+         2, 3, 8, 3, 4, 8,
+         4, 5, 8, 5, 6, 8,
+         6, 7, 8, 7, 0, 8,
+        // clang-format on
+};
+
+// For stroked circles, we use two nested octagons.
+static const uint16_t gStrokeCircleIndices[] = {
+        // enter the octagon
+        // clang-format off
+         0, 1,  9, 0,  9,  8,
+         1, 2, 10, 1, 10,  9,
+         2, 3, 11, 2, 11, 10,
+         3, 4, 12, 3, 12, 11,
+         4, 5, 13, 4, 13, 12,
+         5, 6, 14, 5, 14, 13,
+         6, 7, 15, 6, 15, 14,
+         7, 0,  8, 7,  8, 15,
+        // clang-format on
+};
+
+#define SK_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0]))
+static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
+static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
+static const int kVertsPerStrokeCircle = 16;
+static const int kVertsPerFillCircle = 9;
+
+static int circle_type_to_vert_count(bool stroked) {
+    return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
+}
+
+static int circle_type_to_index_count(bool stroked) {
+    return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
+}
+
+static const uint16_t* circle_type_to_indices(bool stroked) {
+    return stroked ? gStrokeCircleIndices : gFillCircleIndices;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// RoundRect Data
+//
+// The geometry for a shadow roundrect is similar to a 9-patch:
+//    ____________
+//   |_|________|_|
+//   | |        | |
+//   | |        | |
+//   | |        | |
+//   |_|________|_|
+//   |_|________|_|
+//
+// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
+// shows the upper part of the upper left corner. The bottom triangle would similarly be split
+// into two triangles.)
+//    ________
+//   |\  \   |
+//   |  \ \  |
+//   |    \\ |
+//   |      \|
+//   --------
+//
+// The center of the fan handles the curve of the corner. For roundrects where the stroke width
+// is greater than the corner radius, the outer triangles blend from the curve to the straight
+// sides. Otherwise these triangles will be degenerate.
+//
+// In the case where the stroke width is greater than the corner radius and the
+// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
+// This rectangle extends the coverage values of the center edges of the 9-patch.
+//    ____________
+//   |_|________|_|
+//   | |\ ____ /| |
+//   | | |    | | |
+//   | | |____| | |
+//   |_|/______\|_|
+//   |_|________|_|
+//
+// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
+
+static const uint16_t gRRectIndices[] = {
+        // clang-format off
+     // overstroke quads
+     // we place this at the beginning so that we can skip these indices when rendering as filled
+     0, 6, 25, 0, 25, 24,
+     6, 18, 27, 6, 27, 25,
+     18, 12, 26, 18, 26, 27,
+     12, 0, 24, 12, 24, 26,
+
+     // corners
+     0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
+     6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
+     12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
+     18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
+
+     // edges
+     0, 5, 11, 0, 11, 6,
+     6, 7, 19, 6, 19, 18,
+     18, 23, 17, 18, 17, 12,
+     12, 13, 1, 12, 1, 0,
+
+     // fill quad
+     // we place this at the end so that we can skip these indices when rendering as stroked
+     0, 6, 18, 0, 18, 12,
+        // clang-format on
+};
+
+// overstroke count
+static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
+// simple stroke count skips overstroke indices
+static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4;
+// fill count adds final quad to stroke count
+static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
+static const int kVertsPerStrokeRRect = 24;
+static const int kVertsPerOverstrokeRRect = 28;
+static const int kVertsPerFillRRect = 24;
+
+static int rrect_type_to_vert_count(RRectType type) {
+    switch (type) {
+        case kFill_RRectType:
+            return kVertsPerFillRRect;
+        case kStroke_RRectType:
+            return kVertsPerStrokeRRect;
+        case kOverstroke_RRectType:
+            return kVertsPerOverstrokeRRect;
+    }
+    ALOGE("Invalid rect type: %d", type);
+    return -1;
+}
+
+static int rrect_type_to_index_count(RRectType type) {
+    switch (type) {
+        case kFill_RRectType:
+            return kIndicesPerFillRRect;
+        case kStroke_RRectType:
+            return kIndicesPerStrokeRRect;
+        case kOverstroke_RRectType:
+            return kIndicesPerOverstrokeRRect;
+    }
+    ALOGE("Invalid rect type: %d", type);
+    return -1;
+}
+
+static const uint16_t* rrect_type_to_indices(RRectType type) {
+    switch (type) {
+        case kFill_RRectType:
+        case kStroke_RRectType:
+            return gRRectIndices + 6 * 4;
+        case kOverstroke_RRectType:
+            return gRRectIndices;
+    }
+    ALOGE("Invalid rect type: %d", type);
+    return nullptr;
+}
+
+static void fillInCircleVerts(const Geometry& args, bool isStroked,
+                              Mesh::VertexArray<vec2>& position,
+                              Mesh::VertexArray<vec4>& shadowColor,
+                              Mesh::VertexArray<vec3>& shadowParams) {
+    vec4 color = args.fColor;
+    float outerRadius = args.fOuterRadius;
+    float innerRadius = args.fInnerRadius;
+    float blurRadius = args.fBlurRadius;
+    float distanceCorrection = outerRadius / blurRadius;
+
+    const FloatRect& bounds = args.fDevBounds;
+
+    // The inner radius in the vertex data must be specified in normalized space.
+    innerRadius = innerRadius / outerRadius;
+
+    vec2 center = vec2(bounds.getWidth() / 2.0f, bounds.getHeight() / 2.0f);
+    float halfWidth = 0.5f * bounds.getWidth();
+    float octOffset = 0.41421356237f; // sqrt(2) - 1
+    int vertexCount = 0;
+
+    position[vertexCount] = center + vec2(-octOffset * halfWidth, -halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(-octOffset, -1, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(octOffset * halfWidth, -halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(octOffset, -1, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(halfWidth, -octOffset * halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(1, -octOffset, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(halfWidth, octOffset * halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(1, octOffset, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(octOffset * halfWidth, halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(octOffset, 1, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(-octOffset * halfWidth, halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(-octOffset, 1, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(-halfWidth, octOffset * halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(-1, octOffset, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(-halfWidth, -octOffset * halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(-1, -octOffset, distanceCorrection);
+    vertexCount++;
+
+    if (isStroked) {
+        // compute the inner ring
+
+        // cosine and sine of pi/8
+        float c = 0.923579533f;
+        float s = 0.382683432f;
+        float r = args.fInnerRadius;
+
+        position[vertexCount] = center + vec2(-s * r, -c * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(-s * innerRadius, -c * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(s * r, -c * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(s * innerRadius, -c * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(c * r, -s * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(c * innerRadius, -s * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(c * r, s * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(c * innerRadius, s * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(s * r, c * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(s * innerRadius, c * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(-s * r, c * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(-s * innerRadius, c * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(-c * r, s * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(-c * innerRadius, s * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(-c * r, -s * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(-c * innerRadius, -s * innerRadius, distanceCorrection);
+        vertexCount++;
+    } else {
+        // filled
+        position[vertexCount] = center;
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+        vertexCount++;
+    }
+}
+
+static void fillInRRectVerts(const Geometry& args, Mesh::VertexArray<vec2>& position,
+                             Mesh::VertexArray<vec4>& shadowColor,
+                             Mesh::VertexArray<vec3>& shadowParams) {
+    vec4 color = args.fColor;
+    float outerRadius = args.fOuterRadius;
+
+    const FloatRect& bounds = args.fDevBounds;
+
+    float umbraInset = args.fUmbraInset;
+    float minDim = 0.5f * std::min(bounds.getWidth(), bounds.getHeight());
+    if (umbraInset > minDim) {
+        umbraInset = minDim;
+    }
+
+    float xInner[4] = {bounds.left + umbraInset, bounds.right - umbraInset,
+                       bounds.left + umbraInset, bounds.right - umbraInset};
+    float xMid[4] = {bounds.left + outerRadius, bounds.right - outerRadius,
+                     bounds.left + outerRadius, bounds.right - outerRadius};
+    float xOuter[4] = {bounds.left, bounds.right, bounds.left, bounds.right};
+    float yInner[4] = {bounds.top + umbraInset, bounds.top + umbraInset, bounds.bottom - umbraInset,
+                       bounds.bottom - umbraInset};
+    float yMid[4] = {bounds.top + outerRadius, bounds.top + outerRadius,
+                     bounds.bottom - outerRadius, bounds.bottom - outerRadius};
+    float yOuter[4] = {bounds.top, bounds.top, bounds.bottom, bounds.bottom};
+
+    float blurRadius = args.fBlurRadius;
+
+    // In the case where we have to inset more for the umbra, our two triangles in the
+    // corner get skewed to a diamond rather than a square. To correct for that,
+    // we also skew the vectors we send to the shader that help define the circle.
+    // By doing so, we end up with a quarter circle in the corner rather than the
+    // elliptical curve.
+
+    // This is a bit magical, but it gives us the correct results at extrema:
+    //   a) umbraInset == outerRadius produces an orthogonal vector
+    //   b) outerRadius == 0 produces a diagonal vector
+    // And visually the corner looks correct.
+    vec2 outerVec = vec2(outerRadius - umbraInset, -outerRadius - umbraInset);
+    outerVec = normalize(outerVec);
+    // We want the circle edge to fall fractionally along the diagonal at
+    //      (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
+    //
+    // Setting the components of the diagonal offset to the following value will give us that.
+    float diagVal = umbraInset / (SK_ScalarSqrt2 * (outerRadius - umbraInset) - outerRadius);
+    vec2 diagVec = vec2(diagVal, diagVal);
+    float distanceCorrection = umbraInset / blurRadius;
+
+    int vertexCount = 0;
+    // build corner by corner
+    for (int i = 0; i < 4; ++i) {
+        // inner point
+        position[vertexCount] = vec2(xInner[i], yInner[i]);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+        vertexCount++;
+
+        // outer points
+        position[vertexCount] = vec2(xOuter[i], yInner[i]);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = vec2(xOuter[i], yMid[i]);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = vec2(xOuter[i], yOuter[i]);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(diagVec.x, diagVec.y, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = vec2(xMid[i], yOuter[i]);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = vec2(xInner[i], yOuter[i]);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
+        vertexCount++;
+    }
+
+    // Add the additional vertices for overstroked rrects.
+    // Effectively this is an additional stroked rrect, with its
+    // parameters equal to those in the center of the 9-patch. This will
+    // give constant values across this inner ring.
+    if (kOverstroke_RRectType == args.fType) {
+        float inset = umbraInset + args.fInnerRadius;
+
+        // TL
+        position[vertexCount] = vec2(bounds.left + inset, bounds.top + inset);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+        vertexCount++;
+
+        // TR
+        position[vertexCount] = vec2(bounds.right - inset, bounds.top + inset);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+        vertexCount++;
+
+        // BL
+        position[vertexCount] = vec2(bounds.left + inset, bounds.bottom - inset);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+        vertexCount++;
+
+        // BR
+        position[vertexCount] = vec2(bounds.right - inset, bounds.bottom - inset);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+        vertexCount++;
+    }
+}
+
+int getVertexCountForGeometry(const Geometry& shadowGeometry) {
+    if (shadowGeometry.fIsCircle) {
+        return circle_type_to_vert_count(shadowGeometry.fType);
+    }
+
+    return rrect_type_to_vert_count(shadowGeometry.fType);
+}
+
+int getIndexCountForGeometry(const Geometry& shadowGeometry) {
+    if (shadowGeometry.fIsCircle) {
+        return circle_type_to_index_count(kStroke_RRectType == shadowGeometry.fType);
+    }
+
+    return rrect_type_to_index_count(shadowGeometry.fType);
+}
+
+void fillVerticesForGeometry(const Geometry& shadowGeometry, int /* vertexCount */,
+                             Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
+                             Mesh::VertexArray<vec3> shadowParams) {
+    if (shadowGeometry.fIsCircle) {
+        fillInCircleVerts(shadowGeometry, shadowGeometry.fIsStroked, position, shadowColor,
+                          shadowParams);
+    } else {
+        fillInRRectVerts(shadowGeometry, position, shadowColor, shadowParams);
+    }
+}
+
+void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
+                            int startingVertexOffset, uint16_t* indices) {
+    if (shadowGeometry.fIsCircle) {
+        const uint16_t* primIndices = circle_type_to_indices(shadowGeometry.fIsStroked);
+        for (int i = 0; i < indexCount; ++i) {
+            indices[i] = primIndices[i] + startingVertexOffset;
+        }
+    } else {
+        const uint16_t* primIndices = rrect_type_to_indices(shadowGeometry.fType);
+        for (int i = 0; i < indexCount; ++i) {
+            indices[i] = primIndices[i] + startingVertexOffset;
+        }
+    }
+}
+
+inline void GetSpotParams(float occluderZ, float lightX, float lightY, float lightZ,
+                          float lightRadius, float& blurRadius, float& scale, vec2& translate) {
+    float zRatio = divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
+    blurRadius = lightRadius * zRatio;
+    scale = divide_and_pin(lightZ, lightZ - occluderZ, 1.0f, 1.95f);
+    translate.x = -zRatio * lightX;
+    translate.y = -zRatio * lightY;
+}
+
+static std::unique_ptr<Geometry> getShadowGeometry(const vec4& color, const FloatRect& devRect,
+                                                   float devRadius, float blurRadius,
+                                                   float insetWidth) {
+    // An insetWidth > 1/2 rect width or height indicates a simple fill.
+    const bool isCircle = ((devRadius >= devRect.getWidth()) && (devRadius >= devRect.getHeight()));
+
+    FloatRect bounds = devRect;
+    float innerRadius = 0.0f;
+    float outerRadius = devRadius;
+    float umbraInset;
+
+    RRectType type = kFill_RRectType;
+    if (isCircle) {
+        umbraInset = 0;
+    } else {
+        umbraInset = std::max(outerRadius, blurRadius);
+    }
+
+    // If stroke is greater than width or height, this is still a fill,
+    // otherwise we compute stroke params.
+    if (isCircle) {
+        innerRadius = devRadius - insetWidth;
+        type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
+    } else {
+        if (insetWidth <= 0.5f * std::min(devRect.getWidth(), devRect.getHeight())) {
+            // We don't worry about a real inner radius, we just need to know if we
+            // need to create overstroke vertices.
+            innerRadius = std::max(insetWidth - umbraInset, 0.0f);
+            type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
+        }
+    }
+    const bool isStroked = (kStroke_RRectType == type);
+    return std::make_unique<Geometry>(Geometry{color, outerRadius, umbraInset, innerRadius,
+                                               blurRadius, bounds, type, isCircle, isStroked});
+}
+
+std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
+                                                   float casterCornerRadius, float casterZ,
+                                                   bool casterIsTranslucent,
+                                                   const vec4& ambientColor) {
+    float devSpaceInsetWidth = AmbientBlurRadius(casterZ);
+    const float umbraRecipAlpha = AmbientRecipAlpha(casterZ);
+    const float devSpaceAmbientBlur = devSpaceInsetWidth * umbraRecipAlpha;
+
+    // Outset the shadow rrect to the border of the penumbra
+    float ambientPathOutset = devSpaceInsetWidth;
+    FloatRect outsetRect(casterRect);
+    outsetRect.left -= ambientPathOutset;
+    outsetRect.top -= ambientPathOutset;
+    outsetRect.right += ambientPathOutset;
+    outsetRect.bottom += ambientPathOutset;
+
+    float outsetRad = casterCornerRadius + ambientPathOutset;
+    if (casterIsTranslucent) {
+        // set a large inset to force a fill
+        devSpaceInsetWidth = outsetRect.getWidth();
+    }
+
+    return getShadowGeometry(ambientColor, outsetRect, std::abs(outsetRad), devSpaceAmbientBlur,
+                             std::abs(devSpaceInsetWidth));
+}
+
+std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
+                                                float casterCornerRadius, float casterZ,
+                                                bool casterIsTranslucent, const vec4& spotColor,
+                                                const vec3& lightPosition, float lightRadius) {
+    float devSpaceSpotBlur;
+    float spotScale;
+    vec2 spotOffset;
+    GetSpotParams(casterZ, lightPosition.x, lightPosition.y, lightPosition.z, lightRadius,
+                  devSpaceSpotBlur, spotScale, spotOffset);
+    // handle scale of radius due to CTM
+    const float srcSpaceSpotBlur = devSpaceSpotBlur;
+
+    // Adjust translate for the effect of the scale.
+    spotOffset.x += spotScale;
+    spotOffset.y += spotScale;
+
+    // Compute the transformed shadow rect
+    ui::Transform shadowTransform;
+    shadowTransform.set(spotOffset.x, spotOffset.y);
+    shadowTransform.set(spotScale, 0, 0, spotScale);
+    FloatRect spotShadowRect = shadowTransform.transform(casterRect);
+    float spotShadowRadius = casterCornerRadius * spotScale;
+
+    // Compute the insetWidth
+    float blurOutset = srcSpaceSpotBlur;
+    float insetWidth = blurOutset;
+    if (casterIsTranslucent) {
+        // If transparent, just do a fill
+        insetWidth += spotShadowRect.getWidth();
+    } else {
+        // For shadows, instead of using a stroke we specify an inset from the penumbra
+        // border. We want to extend this inset area so that it meets up with the caster
+        // geometry. The inset geometry will by default already be inset by the blur width.
+        //
+        // We compare the min and max corners inset by the radius between the original
+        // rrect and the shadow rrect. The distance between the two plus the difference
+        // between the scaled radius and the original radius gives the distance from the
+        // transformed shadow shape to the original shape in that corner. The max
+        // of these gives the maximum distance we need to cover.
+        //
+        // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to
+        // that to get the full insetWidth.
+        float maxOffset;
+        if (casterCornerRadius <= 0.f) {
+            // Manhattan distance works better for rects
+            maxOffset = std::max(std::max(std::abs(spotShadowRect.left - casterRect.left),
+                                          std::abs(spotShadowRect.top - casterRect.top)),
+                                 std::max(std::abs(spotShadowRect.right - casterRect.right),
+                                          std::abs(spotShadowRect.bottom - casterRect.bottom)));
+        } else {
+            float dr = spotShadowRadius - casterCornerRadius;
+            vec2 upperLeftOffset = vec2(spotShadowRect.left - casterRect.left + dr,
+                                        spotShadowRect.top - casterRect.top + dr);
+            vec2 lowerRightOffset = vec2(spotShadowRect.right - casterRect.right - dr,
+                                         spotShadowRect.bottom - casterRect.bottom - dr);
+            maxOffset = sqrt(std::max(dot(upperLeftOffset, lowerRightOffset),
+                                      dot(lowerRightOffset, lowerRightOffset))) +
+                    dr;
+        }
+        insetWidth += std::max(blurOutset, maxOffset);
+    }
+
+    // Outset the shadow rrect to the border of the penumbra
+    spotShadowRadius += blurOutset;
+    spotShadowRect.left -= blurOutset;
+    spotShadowRect.top -= blurOutset;
+    spotShadowRect.right += blurOutset;
+    spotShadowRect.bottom += blurOutset;
+
+    return getShadowGeometry(spotColor, spotShadowRect, std::abs(spotShadowRadius),
+                             2.0f * devSpaceSpotBlur, std::abs(insetWidth));
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.h b/libs/renderengine/gl/GLSkiaShadowPort.h
new file mode 100644
index 0000000..e7d1861
--- /dev/null
+++ b/libs/renderengine/gl/GLSkiaShadowPort.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 <math/vec4.h>
+#include <ui/Rect.h>
+
+namespace android {
+namespace renderengine {
+
+class Mesh;
+
+namespace gl {
+
+/**
+ * The shadow geometry logic and vertex generation code has been ported from skia shadow
+ * fast path OpenGL implementation to draw shadows around rects and rounded rects including
+ * circles.
+ *
+ * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
+ *
+ * Modifications made:
+ * - Switched to using std lib math functions
+ * - Fall off function is implemented in vertex shader rather than a shadow texture
+ * - Removed transformations applied on the caster rect since the caster will be in local
+ *   coordinate space and will be transformed by the vertex shader.
+ */
+
+enum RRectType {
+    kFill_RRectType,
+    kStroke_RRectType,
+    kOverstroke_RRectType,
+};
+
+struct Geometry {
+    vec4 fColor;
+    float fOuterRadius;
+    float fUmbraInset;
+    float fInnerRadius;
+    float fBlurRadius;
+    FloatRect fDevBounds;
+    RRectType fType;
+    bool fIsCircle;
+    bool fIsStroked;
+};
+
+std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
+                                                float casterCornerRadius, float casterZ,
+                                                bool casterIsTranslucent, const vec4& spotColor,
+                                                const vec3& lightPosition, float lightRadius);
+
+std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
+                                                   float casterCornerRadius, float casterZ,
+                                                   bool casterIsTranslucent,
+                                                   const vec4& ambientColor);
+
+int getVertexCountForGeometry(const Geometry& shadowGeometry);
+
+int getIndexCountForGeometry(const Geometry& shadowGeometry);
+
+void fillVerticesForGeometry(const Geometry& shadowGeometry, int vertexCount,
+                             Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
+                             Mesh::VertexArray<vec3> shadowParams);
+
+void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
+                            int startingVertexOffset, uint16_t* indices);
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index e2757e1..ba0e4ad 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -174,16 +174,15 @@
             .set(Key::OPACITY_MASK,
                  description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
             .set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK,
-                 description.hasInputTransformMatrix()
-                         ? Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF)
+                 description.hasInputTransformMatrix() ? Key::INPUT_TRANSFORM_MATRIX_ON
+                                                       : Key::INPUT_TRANSFORM_MATRIX_OFF)
             .set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK,
                  description.hasOutputTransformMatrix() || description.hasColorMatrix()
                          ? Key::OUTPUT_TRANSFORM_MATRIX_ON
                          : Key::OUTPUT_TRANSFORM_MATRIX_OFF)
             .set(Key::ROUNDED_CORNERS_MASK,
-                 description.cornerRadius > 0
-                         ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
-
+                 description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
+            .set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
     needs.set(Key::Y410_BT2020_MASK,
               description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
 
@@ -559,6 +558,12 @@
         vs << "attribute lowp vec4 cropCoords;";
         vs << "varying lowp vec2 outCropCoords;";
     }
+    if (needs.drawShadows()) {
+        vs << "attribute vec4 shadowColor;";
+        vs << "varying vec4 outShadowColor;";
+        vs << "attribute vec4 shadowParams;";
+        vs << "varying vec3 outShadowParams;";
+    }
     vs << "attribute vec4 position;"
        << "uniform mat4 projection;"
        << "uniform mat4 texture;"
@@ -569,6 +574,10 @@
     if (needs.hasRoundedCorners()) {
         vs << "outCropCoords = cropCoords.st;";
     }
+    if (needs.drawShadows()) {
+        vs << "outShadowColor = shadowColor;";
+        vs << "outShadowParams = shadowParams.xyz;";
+    }
     vs << dedent << "}";
     return vs.getString();
 }
@@ -614,6 +623,26 @@
             )__SHADER__";
     }
 
+    if (needs.drawShadows()) {
+        fs << R"__SHADER__(
+            varying vec4 outShadowColor;
+            varying vec3 outShadowParams;
+
+            /**
+             * Returns the shadow color.
+             */
+            vec4 getShadowColor()
+            {
+                // exponential falloff function provided by UX
+                float d = length(outShadowParams.xy);
+                float distance = outShadowParams.z * (1.0 - d);
+                float factor = 1.0 - clamp(distance, 0.0, 1.0);
+                factor = exp(-factor * factor * 4.0) - 0.018;
+                return outShadowColor * factor;
+            }
+            )__SHADER__";
+    }
+
     if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
         fs << "uniform vec4 color;";
     }
@@ -677,25 +706,29 @@
     }
 
     fs << "void main(void) {" << indent;
-    if (needs.isTexturing()) {
-        fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
-        if (needs.isY410BT2020()) {
-            fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
-        }
+    if (needs.drawShadows()) {
+        fs << "gl_FragColor = getShadowColor();";
     } else {
-        fs << "gl_FragColor.rgb = color.rgb;";
-        fs << "gl_FragColor.a = 1.0;";
-    }
-    if (needs.isOpaque()) {
-        fs << "gl_FragColor.a = 1.0;";
-    }
-    if (needs.hasAlpha()) {
-        // modulate the current alpha value with alpha set
-        if (needs.isPremultiplied()) {
-            // ... and the color too if we're premultiplied
-            fs << "gl_FragColor *= color.a;";
+        if (needs.isTexturing()) {
+            fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
+            if (needs.isY410BT2020()) {
+                fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
+            }
         } else {
-            fs << "gl_FragColor.a *= color.a;";
+            fs << "gl_FragColor.rgb = color.rgb;";
+            fs << "gl_FragColor.a = 1.0;";
+        }
+        if (needs.isOpaque()) {
+            fs << "gl_FragColor.a = 1.0;";
+        }
+        if (needs.hasAlpha()) {
+            // modulate the current alpha value with alpha set
+            if (needs.isPremultiplied()) {
+                // ... and the color too if we're premultiplied
+                fs << "gl_FragColor *= color.a;";
+            } else {
+                fs << "gl_FragColor.a *= color.a;";
+            }
         }
     }
 
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index 175c6e8..c8b6da7 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -112,6 +112,11 @@
             Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
             Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
             Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
+
+            SHADOW_SHIFT = 13,
+            SHADOW_MASK = 1 << SHADOW_SHIFT,
+            SHADOW_OFF = 0 << SHADOW_SHIFT,
+            SHADOW_ON = 1 << SHADOW_SHIFT,
         };
 
         inline Key() : mKey(0) {}
@@ -130,6 +135,7 @@
         inline bool hasRoundedCorners() const {
             return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON;
         }
+        inline bool drawShadows() const { return (mKey & SHADOW_MASK) == SHADOW_ON; }
         inline bool hasInputTransformMatrix() const {
             return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
         }
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index 9c9884a..c4a29a9 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <iosfwd>
+
 #include <math/mat4.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
@@ -62,5 +64,31 @@
     uint32_t orientation = ui::Transform::ROT_0;
 };
 
+static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
+    return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip &&
+            lhs.globalTransform == rhs.globalTransform && lhs.maxLuminance == rhs.maxLuminance &&
+            lhs.outputDataspace == rhs.outputDataspace &&
+            lhs.colorTransform == rhs.colorTransform &&
+            lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.orientation == rhs.orientation;
+}
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const DisplaySettings& settings, ::std::ostream* os) {
+    *os << "DisplaySettings {";
+    *os << "\n    .physicalDisplay = ";
+    PrintTo(settings.physicalDisplay, os);
+    *os << "\n    .clip = ";
+    PrintTo(settings.clip, os);
+    *os << "\n    .globalTransform = " << settings.globalTransform;
+    *os << "\n    .maxLuminance = " << settings.maxLuminance;
+    *os << "\n    .outputDataspace = ";
+    PrintTo(settings.outputDataspace, os);
+    *os << "\n    .colorTransform = " << settings.colorTransform;
+    *os << "\n    .clearRegion = ";
+    PrintTo(settings.clearRegion, os);
+    *os << "\n    .orientation = " << settings.orientation;
+    *os << "\n}";
+}
+
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index d890ccd..5aa3f3b 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <iosfwd>
+
 #include <math/mat4.h>
 #include <math/vec3.h>
 #include <renderengine/Texture.h>
@@ -149,5 +151,102 @@
     ShadowSettings shadow;
 };
 
+static inline bool operator==(const Buffer& lhs, const Buffer& rhs) {
+    return lhs.buffer == rhs.buffer && lhs.fence == rhs.fence &&
+            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;
+}
+
+static inline bool operator==(const Geometry& lhs, const Geometry& rhs) {
+    return lhs.boundaries == rhs.boundaries && lhs.positionTransform == rhs.positionTransform &&
+            lhs.roundedCornersRadius == rhs.roundedCornersRadius &&
+            lhs.roundedCornersCrop == rhs.roundedCornersCrop;
+}
+
+static inline bool operator==(const PixelSource& lhs, const PixelSource& rhs) {
+    return lhs.buffer == rhs.buffer && lhs.solidColor == rhs.solidColor;
+}
+
+static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
+    return lhs.ambientColor == rhs.ambientColor && lhs.spotColor == rhs.spotColor &&
+            lhs.lightPos == rhs.lightPos && lhs.lightRadius == rhs.lightRadius &&
+            lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
+}
+
+static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
+    return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
+            lhs.sourceDataspace == rhs.sourceDataspace &&
+            lhs.colorTransform == rhs.colorTransform &&
+            lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow;
+}
+
+// Defining PrintTo helps with Google Tests.
+
+static inline void PrintTo(const Buffer& settings, ::std::ostream* os) {
+    *os << "Buffer {";
+    *os << "\n    .buffer = " << settings.buffer.get();
+    *os << "\n    .fence = " << settings.fence.get();
+    *os << "\n    .textureName = " << settings.textureName;
+    *os << "\n    .useTextureFiltering = " << settings.useTextureFiltering;
+    *os << "\n    .textureTransform = " << settings.textureTransform;
+    *os << "\n    .usePremultipliedAlpha = " << settings.usePremultipliedAlpha;
+    *os << "\n    .isOpaque = " << settings.isOpaque;
+    *os << "\n    .isY410BT2020 = " << settings.isY410BT2020;
+    *os << "\n    .maxMasteringLuminance = " << settings.maxMasteringLuminance;
+    *os << "\n    .maxContentLuminance = " << settings.maxContentLuminance;
+    *os << "\n}";
+}
+
+static inline void PrintTo(const Geometry& settings, ::std::ostream* os) {
+    *os << "Geometry {";
+    *os << "\n    .boundaries = ";
+    PrintTo(settings.boundaries, os);
+    *os << "\n    .positionTransform = " << settings.positionTransform;
+    *os << "\n    .roundedCornersRadius = " << settings.roundedCornersRadius;
+    *os << "\n    .roundedCornersCrop = ";
+    PrintTo(settings.roundedCornersCrop, os);
+    *os << "\n}";
+}
+
+static inline void PrintTo(const PixelSource& settings, ::std::ostream* os) {
+    *os << "PixelSource {";
+    *os << "\n    .buffer = ";
+    PrintTo(settings.buffer, os);
+    *os << "\n    .solidColor = " << settings.solidColor;
+    *os << "\n}";
+}
+
+static inline void PrintTo(const ShadowSettings& settings, ::std::ostream* os) {
+    *os << "ShadowSettings {";
+    *os << "\n    .ambientColor = " << settings.ambientColor;
+    *os << "\n    .spotColor = " << settings.spotColor;
+    *os << "\n    .lightPos = " << settings.lightPos;
+    *os << "\n    .lightRadius = " << settings.lightRadius;
+    *os << "\n    .length = " << settings.length;
+    *os << "\n    .casterIsTranslucent = " << settings.casterIsTranslucent;
+    *os << "\n}";
+}
+
+static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
+    *os << "LayerSettings {";
+    *os << "\n    .geometry = ";
+    PrintTo(settings.geometry, os);
+    *os << "\n    .source = ";
+    PrintTo(settings.source, os);
+    *os << "\n    .alpha = " << settings.alpha;
+    *os << "\n    .sourceDataspace = ";
+    PrintTo(settings.sourceDataspace, os);
+    *os << "\n    .colorTransform = " << settings.colorTransform;
+    *os << "\n    .disableBlending = " << settings.disableBlending;
+    *os << "\n    .shadow = ";
+    PrintTo(settings.shadow, os);
+    *os << "\n}";
+}
+
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/include/renderengine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h
index 59db87a..167f13f 100644
--- a/libs/renderengine/include/renderengine/Mesh.h
+++ b/libs/renderengine/include/renderengine/Mesh.h
@@ -47,12 +47,12 @@
         size_t mOffset = 0;
         VertexArray(float* data, size_t stride) : mData(data), mStride(stride) {}
 
+    public:
         // Returns a vertex array at an offset so its easier to append attributes from
         // multiple sources.
         VertexArray(VertexArray<TYPE>& other, size_t offset)
               : mData(other.mData), mStride(other.mStride), mOffset(offset) {}
 
-    public:
         TYPE& operator[](size_t index) {
             return *reinterpret_cast<TYPE*>(&mData[(index + mOffset) * mStride]);
         }
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index d01c740..ba5a3f5 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -16,6 +16,7 @@
 
 #include <chrono>
 #include <condition_variable>
+#include <fstream>
 
 #include <gtest/gtest.h>
 #include <renderengine/RenderEngine.h>
@@ -26,6 +27,7 @@
 constexpr int DEFAULT_DISPLAY_WIDTH = 128;
 constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
 constexpr int DEFAULT_DISPLAY_OFFSET = 64;
+constexpr bool WRITE_BUFFER_TO_FILE_ON_FAILURE = false;
 
 namespace android {
 
@@ -68,21 +70,80 @@
     RenderEngineTest() { mBuffer = allocateDefaultBuffer(); }
 
     ~RenderEngineTest() {
+        if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) {
+            writeBufferToFile("/data/texture_out_");
+        }
         for (uint32_t texName : mTexNames) {
             sRE->deleteTextures(1, &texName);
         }
     }
 
-    void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
-                           uint8_t tolerance = 0) {
+    void writeBufferToFile(const char* basename) {
+        std::string filename(basename);
+        filename.append(::testing::UnitTest::GetInstance()->current_test_info()->name());
+        filename.append(".ppm");
+        std::ofstream file(filename.c_str(), std::ios::binary);
+        if (!file.is_open()) {
+            ALOGE("Unable to open file: %s", filename.c_str());
+            ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
+                  "surfaceflinger to write debug images");
+            return;
+        }
+
         uint8_t* pixels;
         mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
                       reinterpret_cast<void**>(&pixels));
 
-        auto colorCompare = [tolerance](uint8_t a, uint8_t b) {
-            uint8_t tmp = a >= b ? a - b : b - a;
-            return tmp <= tolerance;
+        file << "P6\n";
+        file << mBuffer->getWidth() << "\n";
+        file << mBuffer->getHeight() << "\n";
+        file << 255 << "\n";
+
+        std::vector<uint8_t> outBuffer(mBuffer->getWidth() * mBuffer->getHeight() * 3);
+        auto outPtr = reinterpret_cast<uint8_t*>(outBuffer.data());
+
+        for (int32_t j = 0; j < mBuffer->getHeight(); j++) {
+            const uint8_t* src = pixels + (mBuffer->getStride() * j) * 4;
+            for (int32_t i = 0; i < mBuffer->getWidth(); i++) {
+                // Only copy R, G and B components
+                outPtr[0] = src[0];
+                outPtr[1] = src[1];
+                outPtr[2] = src[2];
+                outPtr += 3;
+
+                src += 4;
+            }
+        }
+        file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
+        mBuffer->unlock();
+    }
+
+    void expectBufferColor(const Region& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
+        size_t c;
+        Rect const* rect = region.getArray(&c);
+        for (size_t i = 0; i < c; i++, rect++) {
+            expectBufferColor(*rect, r, g, b, a);
+        }
+    }
+
+    void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+                           uint8_t tolerance = 0) {
+        auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
+            auto colorBitCompare = [tolerance](uint8_t a, uint8_t b) {
+                uint8_t tmp = a >= b ? a - b : b - a;
+                return tmp <= tolerance;
+            };
+            return std::equal(colorA, colorA + 4, colorB, colorBitCompare);
         };
+
+        expectBufferColor(rect, r, g, b, a, colorCompare);
+    }
+
+    void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+                           std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) {
+        uint8_t* pixels;
+        mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                      reinterpret_cast<void**>(&pixels));
         int32_t maxFails = 10;
         int32_t fails = 0;
         for (int32_t j = 0; j < region.getHeight(); j++) {
@@ -90,7 +151,7 @@
                     pixels + (mBuffer->getStride() * (region.top + j) + region.left) * 4;
             for (int32_t i = 0; i < region.getWidth(); i++) {
                 const uint8_t expected[4] = {r, g, b, a};
-                bool equal = std::equal(src, src + 4, expected, colorCompare);
+                bool equal = colorCompare(src, expected);
                 EXPECT_TRUE(equal)
                         << "pixel @ (" << region.left + i << ", " << region.top + j << "): "
                         << "expected (" << static_cast<uint32_t>(r) << ", "
@@ -111,6 +172,64 @@
         mBuffer->unlock();
     }
 
+    void expectAlpha(const Rect& rect, uint8_t a) {
+        auto colorCompare = [](const uint8_t* colorA, const uint8_t* colorB) {
+            return colorA[3] == colorB[3];
+        };
+        expectBufferColor(rect, 0.0f /* r */, 0.0f /*g */, 0.0f /* b */, a, colorCompare);
+    }
+
+    void expectShadowColor(const renderengine::LayerSettings& castingLayer,
+                           const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+                           const ubyte4& backgroundColor) {
+        const Rect casterRect(castingLayer.geometry.boundaries);
+        Region casterRegion = Region(casterRect);
+        const float casterCornerRadius = castingLayer.geometry.roundedCornersRadius;
+        if (casterCornerRadius > 0.0f) {
+            // ignore the corners if a corner radius is set
+            Rect cornerRect(casterCornerRadius, casterCornerRadius);
+            casterRegion.subtractSelf(cornerRect.offsetTo(casterRect.left, casterRect.top));
+            casterRegion.subtractSelf(
+                    cornerRect.offsetTo(casterRect.right - casterCornerRadius, casterRect.top));
+            casterRegion.subtractSelf(
+                    cornerRect.offsetTo(casterRect.left, casterRect.bottom - casterCornerRadius));
+            casterRegion.subtractSelf(cornerRect.offsetTo(casterRect.right - casterCornerRadius,
+                                                          casterRect.bottom - casterCornerRadius));
+        }
+
+        const float shadowInset = shadow.length * -1.0f;
+        const Rect casterWithShadow =
+                Rect(casterRect).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+        const Region shadowRegion = Region(casterWithShadow).subtractSelf(casterRect);
+        const Region backgroundRegion = Region(fullscreenRect()).subtractSelf(casterWithShadow);
+
+        // verify casting layer
+        expectBufferColor(casterRegion, casterColor.r, casterColor.g, casterColor.b, casterColor.a);
+
+        // verify shadows by testing just the alpha since its difficult to validate the shadow color
+        size_t c;
+        Rect const* r = shadowRegion.getArray(&c);
+        for (size_t i = 0; i < c; i++, r++) {
+            expectAlpha(*r, 255);
+        }
+
+        // verify background
+        expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+                          backgroundColor.a);
+    }
+
+    static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
+                                                          bool casterIsTranslucent) {
+        renderengine::ShadowSettings shadow;
+        shadow.ambientColor = {0.0f, 0.0f, 0.0f, 0.039f};
+        shadow.spotColor = {0.0f, 0.0f, 0.0f, 0.19f};
+        shadow.lightPos = vec3(casterPos.x, casterPos.y, 0);
+        shadow.lightRadius = 0.0f;
+        shadow.length = shadowLength;
+        shadow.casterIsTranslucent = casterIsTranslucent;
+        return shadow;
+    }
+
     static Rect fullscreenRect() { return Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); }
 
     static Rect offsetRect() {
@@ -225,6 +344,11 @@
 
     void clearRegion();
 
+    template <typename SourceVariant>
+    void drawShadow(const renderengine::LayerSettings& castingLayer,
+                    const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+                    const ubyte4& backgroundColor);
+
     // Keep around the same renderengine object to save on initialization time.
     // For now, exercise the GL backend directly so that some caching specifics
     // can be tested without changing the interface.
@@ -766,6 +890,40 @@
                       0, 0, 0, 0);
 }
 
+template <typename SourceVariant>
+void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,
+                                  const renderengine::ShadowSettings& shadow,
+                                  const ubyte4& casterColor, const ubyte4& backgroundColor) {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    // add background layer
+    renderengine::LayerSettings bgLayer;
+    bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+    ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
+                                  backgroundColor.b / 255.0f, this);
+    bgLayer.alpha = backgroundColor.a / 255.0f;
+    layers.push_back(bgLayer);
+
+    // add shadow layer
+    renderengine::LayerSettings shadowLayer;
+    shadowLayer.geometry.boundaries = castingLayer.geometry.boundaries;
+    shadowLayer.alpha = castingLayer.alpha;
+    shadowLayer.shadow = shadow;
+    layers.push_back(shadowLayer);
+
+    // add layer casting the shadow
+    renderengine::LayerSettings layer = castingLayer;
+    SourceVariant::fillColor(layer, casterColor.r / 255.0f, casterColor.g / 255.0f,
+                             casterColor.b / 255.0f, this);
+    layers.push_back(layer);
+
+    invokeDraw(settings, layers, mBuffer);
+}
+
 TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
     drawEmptyLayers();
 }
@@ -1083,4 +1241,101 @@
     EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
 }
 
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
+    const ubyte4 casterColor(255, 0, 0, 255);
+    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const float shadowLength = 5.0f;
+    Rect casterBounds(1, 1);
+    casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+    renderengine::LayerSettings castingLayer;
+    castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+    castingLayer.alpha = 1.0f;
+    renderengine::ShadowSettings settings =
+            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+                              false /* casterIsTranslucent */);
+
+    drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
+    expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
+    const ubyte4 casterColor(255, 0, 0, 255);
+    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const float shadowLength = 5.0f;
+    Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+    casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+    renderengine::LayerSettings castingLayer;
+    castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+    castingLayer.alpha = 1.0f;
+    renderengine::ShadowSettings settings =
+            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+                              false /* casterIsTranslucent */);
+
+    drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
+    expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
+    const ubyte4 casterColor(255, 0, 0, 255);
+    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const float shadowLength = 5.0f;
+    Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+    casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+    renderengine::LayerSettings castingLayer;
+    castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+    castingLayer.alpha = 1.0f;
+    renderengine::ShadowSettings settings =
+            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+                              false /* casterIsTranslucent */);
+
+    drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+                                                              backgroundColor);
+    expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
+    const ubyte4 casterColor(255, 0, 0, 255);
+    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const float shadowLength = 5.0f;
+    Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+    casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+    renderengine::LayerSettings castingLayer;
+    castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+    castingLayer.geometry.roundedCornersRadius = 3.0f;
+    castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
+    castingLayer.alpha = 1.0f;
+    renderengine::ShadowSettings settings =
+            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+                              false /* casterIsTranslucent */);
+
+    drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+                                                              backgroundColor);
+    expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
+    const ubyte4 casterColor(255, 0, 0, 255);
+    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const float shadowLength = 5.0f;
+    Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+    casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+    renderengine::LayerSettings castingLayer;
+    castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+    castingLayer.alpha = 0.5f;
+    renderengine::ShadowSettings settings =
+            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+                              true /* casterIsTranslucent */);
+
+    drawShadow<BufferSourceVariant<RelaxOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+                                                              backgroundColor);
+
+    // verify only the background since the shadow will draw behind the caster
+    const float shadowInset = settings.length * -1.0f;
+    const Rect casterWithShadow =
+            Rect(casterBounds).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+    const Region backgroundRegion = Region(fullscreenRect()).subtractSelf(casterWithShadow);
+    expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+                      backgroundColor.a);
+}
+
 } // namespace android
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 3775e2b..043f2ca 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -82,6 +82,7 @@
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
         "android.hardware.graphics.allocator@4.0",
+        "android.hardware.graphics.common-ndk_platform",
         "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@2.1",
@@ -89,6 +90,7 @@
         "android.hardware.graphics.mapper@4.0",
         "libbase",
         "libcutils",
+        "libgralloctypes",
         "libhidlbase",
         "libsync",
         "libutils",
@@ -97,6 +99,9 @@
 
     export_shared_lib_headers: [
         "android.hardware.graphics.common@1.2",
+        "android.hardware.graphics.common-ndk_platform",
+        "android.hardware.graphics.mapper@4.0",
+        "libgralloctypes",
     ],
 
     static_libs: [
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index 4ce891e..33ab7c4 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -62,8 +62,26 @@
     int warningTimeout = 3000;
     int err = sync_wait(mFenceFd, warningTimeout);
     if (err < 0 && errno == ETIME) {
-        ALOGE("%s: fence %d didn't signal in %u ms", logname, mFenceFd.get(),
-                warningTimeout);
+        ALOGE("waitForever: %s: fence %d didn't signal in %u ms", logname, mFenceFd.get(),
+              warningTimeout);
+
+        struct sync_file_info* finfo = sync_file_info(mFenceFd);
+        if (finfo) {
+            // status: active(0) signaled(1) error(<0)
+            ALOGI("waitForever: fence(%s) status(%d)", finfo->name, finfo->status);
+
+            struct sync_fence_info* pinfo = sync_get_fence_info(finfo);
+            for (uint32_t i = 0; i < finfo->num_fences; i++) {
+                uint64_t ts_sec = pinfo[i].timestamp_ns / 1000000000LL;
+                uint64_t ts_usec = (pinfo[i].timestamp_ns % 1000000000LL) / 1000LL;
+
+                ALOGI("waitForever: sync point: timeline(%s) drv(%s) status(%d) timestamp(%" PRIu64
+                      ".%06" PRIu64 ")",
+                      pinfo[i].obj_name, pinfo[i].driver_name, pinfo[i].status, ts_sec, ts_usec);
+            }
+            sync_file_info_free(finfo);
+        }
+
         err = sync_wait(mFenceFd, TIMEOUT_NEVER);
     }
     return err < 0 ? -errno : status_t(NO_ERROR);
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 0e23ddf..040a62b 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -351,12 +351,6 @@
     return releaseFence;
 }
 
-status_t Gralloc2Mapper::isSupported(uint32_t /*width*/, uint32_t /*height*/,
-                                     android::PixelFormat /*format*/, uint32_t /*layerCount*/,
-                                     uint64_t /*usage*/, bool* /*outSupported*/) const {
-    return INVALID_OPERATION;
-}
-
 Gralloc2Allocator::Gralloc2Allocator(const Gralloc2Mapper& mapper) : mMapper(mapper) {
     mAllocator = IAllocator::getService();
     if (mAllocator == nullptr) {
@@ -369,7 +363,7 @@
     return mAllocator != nullptr;
 }
 
-std::string Gralloc2Allocator::dumpDebugInfo() const {
+std::string Gralloc2Allocator::dumpDebugInfo(bool /*less*/) const {
     std::string debugInfo;
 
     mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) {
@@ -379,10 +373,10 @@
     return debugInfo;
 }
 
-status_t Gralloc2Allocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
-                                     uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
-                                     uint32_t* outStride, buffer_handle_t* outBufferHandles,
-                                     bool importBuffers) const {
+status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
+                                     PixelFormat format, uint32_t layerCount, uint64_t usage,
+                                     uint32_t bufferCount, uint32_t* outStride,
+                                     buffer_handle_t* outBufferHandles, bool importBuffers) const {
     IMapper::BufferDescriptorInfo descriptorInfo = {};
     descriptorInfo.width = width;
     descriptorInfo.height = height;
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index e189281..882674f 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -352,7 +352,7 @@
     return mAllocator != nullptr;
 }
 
-std::string Gralloc3Allocator::dumpDebugInfo() const {
+std::string Gralloc3Allocator::dumpDebugInfo(bool /*less*/) const {
     std::string debugInfo;
 
     mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); });
@@ -360,10 +360,10 @@
     return debugInfo;
 }
 
-status_t Gralloc3Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format,
-                                     uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
-                                     uint32_t* outStride, buffer_handle_t* outBufferHandles,
-                                     bool importBuffers) const {
+status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
+                                     android::PixelFormat format, uint32_t layerCount,
+                                     uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+                                     buffer_handle_t* outBufferHandles, bool importBuffers) const {
     IMapper::BufferDescriptorInfo descriptorInfo;
     sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
 
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index afe26b7..30c48c8 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -27,11 +27,20 @@
 #include <sync/sync.h>
 #pragma clang diagnostic pop
 
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using android::hardware::hidl_vec;
 using android::hardware::graphics::allocator::V4_0::IAllocator;
 using android::hardware::graphics::common::V1_2::BufferUsage;
 using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
 using android::hardware::graphics::mapper::V4_0::Error;
 using android::hardware::graphics::mapper::V4_0::IMapper;
+using BufferDump = android::hardware::graphics::mapper::V4_0::IMapper::BufferDump;
+using MetadataDump = android::hardware::graphics::mapper::V4_0::IMapper::MetadataDump;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+using MetadataTypeDescription =
+        android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription;
 
 namespace android {
 
@@ -59,10 +68,10 @@
     outRect.height = rect.height();
     return outRect;
 }
-static inline void sBufferDescriptorInfo(uint32_t width, uint32_t height,
-                                         android::PixelFormat format, uint32_t layerCount,
-                                         uint64_t usage,
+static inline void sBufferDescriptorInfo(std::string name, uint32_t width, uint32_t height,
+                                         PixelFormat format, uint32_t layerCount, uint64_t usage,
                                          IMapper::BufferDescriptorInfo* outDescriptorInfo) {
+    outDescriptorInfo->name = name;
     outDescriptorInfo->width = width;
     outDescriptorInfo->height = height;
     outDescriptorInfo->layerCount = layerCount;
@@ -151,11 +160,12 @@
 }
 
 status_t Gralloc4Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
-                                            uint32_t height, android::PixelFormat format,
+                                            uint32_t height, PixelFormat format,
                                             uint32_t layerCount, uint64_t usage,
                                             uint32_t stride) const {
     IMapper::BufferDescriptorInfo descriptorInfo;
-    sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+    sBufferDescriptorInfo("validateBufferSize", width, height, format, layerCount, usage,
+                          &descriptorInfo);
 
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
     auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride);
@@ -189,14 +199,36 @@
 status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
                               int acquireFence, void** outData, int32_t* outBytesPerPixel,
                               int32_t* outBytesPerStride) const {
-    // In Gralloc 4 we can get this info per plane. Clients should check per plane.
-    if (outBytesPerPixel) {
-        // TODO add support to check per plane
-        *outBytesPerPixel = -1;
-    }
-    if (outBytesPerStride) {
-        // TODO add support to check per plane
-        *outBytesPerStride = -1;
+    std::vector<ui::PlaneLayout> planeLayouts;
+    status_t err = getPlaneLayouts(bufferHandle, &planeLayouts);
+
+    if (err != NO_ERROR && !planeLayouts.empty()) {
+        if (outBytesPerPixel) {
+            int32_t bitsPerPixel = planeLayouts.front().sampleIncrementInBits;
+            for (const auto& planeLayout : planeLayouts) {
+                if (bitsPerPixel != planeLayout.sampleIncrementInBits) {
+                    bitsPerPixel = -1;
+                }
+            }
+            if (bitsPerPixel >= 0 && bitsPerPixel % 8 == 0) {
+                *outBytesPerPixel = bitsPerPixel / 8;
+            } else {
+                *outBytesPerPixel = -1;
+            }
+        }
+        if (outBytesPerStride) {
+            int32_t bytesPerStride = planeLayouts.front().strideInBytes;
+            for (const auto& planeLayout : planeLayouts) {
+                if (bytesPerStride != planeLayout.strideInBytes) {
+                    bytesPerStride = -1;
+                }
+            }
+            if (bytesPerStride >= 0) {
+                *outBytesPerStride = bytesPerStride;
+            } else {
+                *outBytesPerStride = -1;
+            }
+        }
     }
 
     auto buffer = const_cast<native_handle_t*>(bufferHandle);
@@ -234,10 +266,104 @@
     return static_cast<status_t>(error);
 }
 
-status_t Gralloc4Mapper::lock(buffer_handle_t /*bufferHandle*/, uint64_t /*usage*/,
-                              const Rect& /*bounds*/, int /*acquireFence*/,
-                              android_ycbcr* /*ycbcr*/) const {
-    // TODO add lockYCbCr support
+status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, android_ycbcr* outYcbcr) const {
+    if (!outYcbcr) {
+        return BAD_VALUE;
+    }
+
+    std::vector<ui::PlaneLayout> planeLayouts;
+    status_t error = getPlaneLayouts(bufferHandle, &planeLayouts);
+    if (error != NO_ERROR) {
+        return error;
+    }
+
+    void* data = nullptr;
+    error = lock(bufferHandle, usage, bounds, acquireFence, &data, nullptr, nullptr);
+    if (error != NO_ERROR) {
+        return error;
+    }
+
+    android_ycbcr ycbcr;
+
+    ycbcr.y = nullptr;
+    ycbcr.cb = nullptr;
+    ycbcr.cr = nullptr;
+    ycbcr.ystride = 0;
+    ycbcr.cstride = 0;
+    ycbcr.chroma_step = 0;
+
+    for (const auto& planeLayout : planeLayouts) {
+        for (const auto& planeLayoutComponent : planeLayout.components) {
+            if (!gralloc4::isStandardPlaneLayoutComponentType(planeLayoutComponent.type)) {
+                continue;
+            }
+            if (0 != planeLayoutComponent.offsetInBits % 8) {
+                unlock(bufferHandle);
+                return BAD_VALUE;
+            }
+
+            uint8_t* tmpData = static_cast<uint8_t*>(data) + planeLayout.offsetInBytes +
+                    (planeLayoutComponent.offsetInBits / 8);
+            uint64_t sampleIncrementInBytes;
+
+            auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value);
+            switch (type) {
+                case PlaneLayoutComponentType::Y:
+                    if ((ycbcr.y != nullptr) || (planeLayoutComponent.sizeInBits != 8) ||
+                        (planeLayout.sampleIncrementInBits != 8)) {
+                        unlock(bufferHandle);
+                        return BAD_VALUE;
+                    }
+                    ycbcr.y = tmpData;
+                    ycbcr.ystride = planeLayout.strideInBytes;
+                    break;
+
+                case PlaneLayoutComponentType::CB:
+                case PlaneLayoutComponentType::CR:
+                    if (planeLayout.sampleIncrementInBits % 8 != 0) {
+                        unlock(bufferHandle);
+                        return BAD_VALUE;
+                    }
+
+                    sampleIncrementInBytes = planeLayout.sampleIncrementInBits / 8;
+                    if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2)) {
+                        unlock(bufferHandle);
+                        return BAD_VALUE;
+                    }
+
+                    if (ycbcr.cstride == 0 && ycbcr.chroma_step == 0) {
+                        ycbcr.cstride = planeLayout.strideInBytes;
+                        ycbcr.chroma_step = sampleIncrementInBytes;
+                    } else {
+                        if ((static_cast<int64_t>(ycbcr.cstride) != planeLayout.strideInBytes) ||
+                            (ycbcr.chroma_step != sampleIncrementInBytes)) {
+                            unlock(bufferHandle);
+                            return BAD_VALUE;
+                        }
+                    }
+
+                    if (type == PlaneLayoutComponentType::CB) {
+                        if (ycbcr.cb != nullptr) {
+                            unlock(bufferHandle);
+                            return BAD_VALUE;
+                        }
+                        ycbcr.cb = tmpData;
+                    } else {
+                        if (ycbcr.cr != nullptr) {
+                            unlock(bufferHandle);
+                            return BAD_VALUE;
+                        }
+                        ycbcr.cr = tmpData;
+                    }
+                    break;
+                default:
+                    break;
+            };
+        }
+    }
+
+    *outYcbcr = ycbcr;
     return static_cast<status_t>(Error::UNSUPPORTED);
 }
 
@@ -275,11 +401,11 @@
     return releaseFence;
 }
 
-status_t Gralloc4Mapper::isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
+status_t Gralloc4Mapper::isSupported(uint32_t width, uint32_t height, PixelFormat format,
                                      uint32_t layerCount, uint64_t usage,
                                      bool* outSupported) const {
     IMapper::BufferDescriptorInfo descriptorInfo;
-    sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+    sBufferDescriptorInfo("isSupported", width, height, format, layerCount, usage, &descriptorInfo);
 
     Error error;
     auto ret = mMapper->isSupported(descriptorInfo,
@@ -305,6 +431,623 @@
     return static_cast<status_t>(error);
 }
 
+template <class T>
+status_t Gralloc4Mapper::get(buffer_handle_t bufferHandle, const MetadataType& metadataType,
+                             DecodeFunction<T> decodeFunction, T* outMetadata) const {
+    if (!outMetadata) {
+        return BAD_VALUE;
+    }
+
+    hidl_vec<uint8_t> vec;
+    Error error;
+    auto ret = mMapper->get(const_cast<native_handle_t*>(bufferHandle), metadataType,
+                            [&](const auto& tmpError, const hidl_vec<uint8_t>& tmpVec) {
+                                error = tmpError;
+                                vec = tmpVec;
+                            });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("get(%s, %" PRIu64 ", ...) failed with %d", metadataType.name.c_str(),
+              metadataType.value, error);
+        return static_cast<status_t>(error);
+    }
+
+    return decodeFunction(vec, outMetadata);
+}
+
+status_t Gralloc4Mapper::getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) const {
+    return get(bufferHandle, gralloc4::MetadataType_BufferId, gralloc4::decodeBufferId,
+               outBufferId);
+}
+
+status_t Gralloc4Mapper::getName(buffer_handle_t bufferHandle, std::string* outName) const {
+    return get(bufferHandle, gralloc4::MetadataType_Name, gralloc4::decodeName, outName);
+}
+
+status_t Gralloc4Mapper::getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) const {
+    return get(bufferHandle, gralloc4::MetadataType_Width, gralloc4::decodeWidth, outWidth);
+}
+
+status_t Gralloc4Mapper::getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) const {
+    return get(bufferHandle, gralloc4::MetadataType_Height, gralloc4::decodeHeight, outHeight);
+}
+
+status_t Gralloc4Mapper::getLayerCount(buffer_handle_t bufferHandle,
+                                       uint64_t* outLayerCount) const {
+    return get(bufferHandle, gralloc4::MetadataType_LayerCount, gralloc4::decodeLayerCount,
+               outLayerCount);
+}
+
+status_t Gralloc4Mapper::getPixelFormatRequested(buffer_handle_t bufferHandle,
+                                                 ui::PixelFormat* outPixelFormatRequested) const {
+    return get(bufferHandle, gralloc4::MetadataType_PixelFormatRequested,
+               gralloc4::decodePixelFormatRequested, outPixelFormatRequested);
+}
+
+status_t Gralloc4Mapper::getPixelFormatFourCC(buffer_handle_t bufferHandle,
+                                              uint32_t* outPixelFormatFourCC) const {
+    return get(bufferHandle, gralloc4::MetadataType_PixelFormatFourCC,
+               gralloc4::decodePixelFormatFourCC, outPixelFormatFourCC);
+}
+
+status_t Gralloc4Mapper::getPixelFormatModifier(buffer_handle_t bufferHandle,
+                                                uint64_t* outPixelFormatModifier) const {
+    return get(bufferHandle, gralloc4::MetadataType_PixelFormatModifier,
+               gralloc4::decodePixelFormatModifier, outPixelFormatModifier);
+}
+
+status_t Gralloc4Mapper::getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) const {
+    return get(bufferHandle, gralloc4::MetadataType_Usage, gralloc4::decodeUsage, outUsage);
+}
+
+status_t Gralloc4Mapper::getAllocationSize(buffer_handle_t bufferHandle,
+                                           uint64_t* outAllocationSize) const {
+    return get(bufferHandle, gralloc4::MetadataType_AllocationSize, gralloc4::decodeAllocationSize,
+               outAllocationSize);
+}
+
+status_t Gralloc4Mapper::getProtectedContent(buffer_handle_t bufferHandle,
+                                             uint64_t* outProtectedContent) const {
+    return get(bufferHandle, gralloc4::MetadataType_ProtectedContent,
+               gralloc4::decodeProtectedContent, outProtectedContent);
+}
+
+status_t Gralloc4Mapper::getCompression(buffer_handle_t bufferHandle,
+                                        ExtendableType* outCompression) const {
+    return get(bufferHandle, gralloc4::MetadataType_Compression, gralloc4::decodeCompression,
+               outCompression);
+}
+
+status_t Gralloc4Mapper::getCompression(buffer_handle_t bufferHandle,
+                                        ui::Compression* outCompression) const {
+    if (!outCompression) {
+        return BAD_VALUE;
+    }
+    ExtendableType compression;
+    status_t error = getCompression(bufferHandle, &compression);
+    if (error) {
+        return error;
+    }
+    if (!gralloc4::isStandardCompression(compression)) {
+        return BAD_TYPE;
+    }
+    *outCompression = gralloc4::getStandardCompressionValue(compression);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getInterlaced(buffer_handle_t bufferHandle,
+                                       ExtendableType* outInterlaced) const {
+    return get(bufferHandle, gralloc4::MetadataType_Interlaced, gralloc4::decodeInterlaced,
+               outInterlaced);
+}
+
+status_t Gralloc4Mapper::getInterlaced(buffer_handle_t bufferHandle,
+                                       ui::Interlaced* outInterlaced) const {
+    if (!outInterlaced) {
+        return BAD_VALUE;
+    }
+    ExtendableType interlaced;
+    status_t error = getInterlaced(bufferHandle, &interlaced);
+    if (error) {
+        return error;
+    }
+    if (!gralloc4::isStandardInterlaced(interlaced)) {
+        return BAD_TYPE;
+    }
+    *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getChromaSiting(buffer_handle_t bufferHandle,
+                                         ExtendableType* outChromaSiting) const {
+    return get(bufferHandle, gralloc4::MetadataType_ChromaSiting, gralloc4::decodeChromaSiting,
+               outChromaSiting);
+}
+
+status_t Gralloc4Mapper::getChromaSiting(buffer_handle_t bufferHandle,
+                                         ui::ChromaSiting* outChromaSiting) const {
+    if (!outChromaSiting) {
+        return BAD_VALUE;
+    }
+    ExtendableType chromaSiting;
+    status_t error = getChromaSiting(bufferHandle, &chromaSiting);
+    if (error) {
+        return error;
+    }
+    if (!gralloc4::isStandardChromaSiting(chromaSiting)) {
+        return BAD_TYPE;
+    }
+    *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getPlaneLayouts(buffer_handle_t bufferHandle,
+                                         std::vector<ui::PlaneLayout>* outPlaneLayouts) const {
+    return get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, gralloc4::decodePlaneLayouts,
+               outPlaneLayouts);
+}
+
+status_t Gralloc4Mapper::getDataspace(buffer_handle_t bufferHandle,
+                                      ui::Dataspace* outDataspace) const {
+    if (!outDataspace) {
+        return BAD_VALUE;
+    }
+    aidl::android::hardware::graphics::common::Dataspace dataspace;
+    status_t error = get(bufferHandle, gralloc4::MetadataType_Dataspace, gralloc4::decodeDataspace,
+                         &dataspace);
+    if (error) {
+        return error;
+    }
+
+    // Gralloc4 uses stable AIDL dataspace but the rest of the system still uses HIDL dataspace
+    *outDataspace = static_cast<ui::Dataspace>(dataspace);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getBlendMode(buffer_handle_t bufferHandle,
+                                      ui::BlendMode* outBlendMode) const {
+    return get(bufferHandle, gralloc4::MetadataType_BlendMode, gralloc4::decodeBlendMode,
+               outBlendMode);
+}
+
+status_t Gralloc4Mapper::getSmpte2086(buffer_handle_t bufferHandle,
+                                      std::optional<ui::Smpte2086>* outSmpte2086) const {
+    return get(bufferHandle, gralloc4::MetadataType_Smpte2086, gralloc4::decodeSmpte2086,
+               outSmpte2086);
+}
+
+status_t Gralloc4Mapper::getCta861_3(buffer_handle_t bufferHandle,
+                                     std::optional<ui::Cta861_3>* outCta861_3) const {
+    return get(bufferHandle, gralloc4::MetadataType_Cta861_3, gralloc4::decodeCta861_3,
+               outCta861_3);
+}
+
+status_t Gralloc4Mapper::getSmpte2094_40(
+        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_40) const {
+    return get(bufferHandle, gralloc4::MetadataType_Smpte2094_40, gralloc4::decodeSmpte2094_40,
+               outSmpte2094_40);
+}
+
+template <class T>
+status_t Gralloc4Mapper::getDefault(uint32_t width, uint32_t height, PixelFormat format,
+                                    uint32_t layerCount, uint64_t usage,
+                                    const MetadataType& metadataType,
+                                    DecodeFunction<T> decodeFunction, T* outMetadata) const {
+    if (!outMetadata) {
+        return BAD_VALUE;
+    }
+
+    IMapper::BufferDescriptorInfo descriptorInfo;
+    sBufferDescriptorInfo("getDefault", width, height, format, layerCount, usage, &descriptorInfo);
+
+    hidl_vec<uint8_t> vec;
+    Error error;
+    auto ret = mMapper->getFromBufferDescriptorInfo(descriptorInfo, metadataType,
+                                                    [&](const auto& tmpError,
+                                                        const hidl_vec<uint8_t>& tmpVec) {
+                                                        error = tmpError;
+                                                        vec = tmpVec;
+                                                    });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("getDefault(%s, %" PRIu64 ", ...) failed with %d", metadataType.name.c_str(),
+              metadataType.value, error);
+        return static_cast<status_t>(error);
+    }
+
+    return decodeFunction(vec, outMetadata);
+}
+
+status_t Gralloc4Mapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
+                                                     PixelFormat format, uint32_t layerCount,
+                                                     uint64_t usage,
+                                                     uint32_t* outPixelFormatFourCC) const {
+    return getDefault(width, height, format, layerCount, usage,
+                      gralloc4::MetadataType_PixelFormatFourCC, gralloc4::decodePixelFormatFourCC,
+                      outPixelFormatFourCC);
+}
+
+status_t Gralloc4Mapper::getDefaultPixelFormatModifier(uint32_t width, uint32_t height,
+                                                       PixelFormat format, uint32_t layerCount,
+                                                       uint64_t usage,
+                                                       uint64_t* outPixelFormatModifier) const {
+    return getDefault(width, height, format, layerCount, usage,
+                      gralloc4::MetadataType_PixelFormatModifier,
+                      gralloc4::decodePixelFormatModifier, outPixelFormatModifier);
+}
+
+status_t Gralloc4Mapper::getDefaultAllocationSize(uint32_t width, uint32_t height,
+                                                  PixelFormat format, uint32_t layerCount,
+                                                  uint64_t usage,
+                                                  uint64_t* outAllocationSize) const {
+    return getDefault(width, height, format, layerCount, usage,
+                      gralloc4::MetadataType_AllocationSize, gralloc4::decodeAllocationSize,
+                      outAllocationSize);
+}
+
+status_t Gralloc4Mapper::getDefaultProtectedContent(uint32_t width, uint32_t height,
+                                                    PixelFormat format, uint32_t layerCount,
+                                                    uint64_t usage,
+                                                    uint64_t* outProtectedContent) const {
+    return getDefault(width, height, format, layerCount, usage,
+                      gralloc4::MetadataType_ProtectedContent, gralloc4::decodeProtectedContent,
+                      outProtectedContent);
+}
+
+status_t Gralloc4Mapper::getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+                                               uint32_t layerCount, uint64_t usage,
+                                               ExtendableType* outCompression) const {
+    return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_Compression,
+                      gralloc4::decodeCompression, outCompression);
+}
+
+status_t Gralloc4Mapper::getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+                                               uint32_t layerCount, uint64_t usage,
+                                               ui::Compression* outCompression) const {
+    if (!outCompression) {
+        return BAD_VALUE;
+    }
+    ExtendableType compression;
+    status_t error = getDefaultCompression(width, height, format, layerCount, usage, &compression);
+    if (error) {
+        return error;
+    }
+    if (!gralloc4::isStandardCompression(compression)) {
+        return BAD_TYPE;
+    }
+    *outCompression = gralloc4::getStandardCompressionValue(compression);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+                                              uint32_t layerCount, uint64_t usage,
+                                              ExtendableType* outInterlaced) const {
+    return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_Interlaced,
+                      gralloc4::decodeInterlaced, outInterlaced);
+}
+
+status_t Gralloc4Mapper::getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+                                              uint32_t layerCount, uint64_t usage,
+                                              ui::Interlaced* outInterlaced) const {
+    if (!outInterlaced) {
+        return BAD_VALUE;
+    }
+    ExtendableType interlaced;
+    status_t error = getDefaultInterlaced(width, height, format, layerCount, usage, &interlaced);
+    if (error) {
+        return error;
+    }
+    if (!gralloc4::isStandardInterlaced(interlaced)) {
+        return BAD_TYPE;
+    }
+    *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+                                                uint32_t layerCount, uint64_t usage,
+                                                ExtendableType* outChromaSiting) const {
+    return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_ChromaSiting,
+                      gralloc4::decodeChromaSiting, outChromaSiting);
+}
+
+status_t Gralloc4Mapper::getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+                                                uint32_t layerCount, uint64_t usage,
+                                                ui::ChromaSiting* outChromaSiting) const {
+    if (!outChromaSiting) {
+        return BAD_VALUE;
+    }
+    ExtendableType chromaSiting;
+    status_t error =
+            getDefaultChromaSiting(width, height, format, layerCount, usage, &chromaSiting);
+    if (error) {
+        return error;
+    }
+    if (!gralloc4::isStandardChromaSiting(chromaSiting)) {
+        return BAD_TYPE;
+    }
+    *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultPlaneLayouts(
+        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+        std::vector<ui::PlaneLayout>* outPlaneLayouts) const {
+    return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_PlaneLayouts,
+                      gralloc4::decodePlaneLayouts, outPlaneLayouts);
+}
+
+std::vector<MetadataTypeDescription> Gralloc4Mapper::listSupportedMetadataTypes() const {
+    hidl_vec<MetadataTypeDescription> descriptions;
+    Error error;
+    auto ret = mMapper->listSupportedMetadataTypes(
+            [&](const auto& tmpError, const auto& tmpDescriptions) {
+                error = tmpError;
+                descriptions = tmpDescriptions;
+            });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("listSupportedMetadataType() failed with %d", error);
+        return {};
+    }
+
+    return static_cast<std::vector<MetadataTypeDescription>>(descriptions);
+}
+
+template <class T>
+status_t Gralloc4Mapper::metadataDumpHelper(const BufferDump& bufferDump,
+                                            StandardMetadataType metadataType,
+                                            DecodeFunction<T> decodeFunction, T* outT) const {
+    const auto& metadataDump = bufferDump.metadataDump;
+
+    auto itr =
+            std::find_if(metadataDump.begin(), metadataDump.end(),
+                         [&](const MetadataDump& tmpMetadataDump) {
+                             if (!gralloc4::isStandardMetadataType(tmpMetadataDump.metadataType)) {
+                                 return false;
+                             }
+                             return metadataType ==
+                                     gralloc4::getStandardMetadataTypeValue(
+                                             tmpMetadataDump.metadataType);
+                         });
+    if (itr == metadataDump.end()) {
+        return BAD_VALUE;
+    }
+
+    return decodeFunction(itr->metadata, outT);
+}
+
+status_t Gralloc4Mapper::bufferDumpHelper(const BufferDump& bufferDump, std::ostringstream* outDump,
+                                          uint64_t* outAllocationSize, bool less) const {
+    uint64_t bufferId;
+    std::string name;
+    uint64_t width;
+    uint64_t height;
+    uint64_t layerCount;
+    ui::PixelFormat pixelFormatRequested;
+    uint32_t pixelFormatFourCC;
+    uint64_t pixelFormatModifier;
+    uint64_t usage;
+    uint64_t allocationSize;
+    uint64_t protectedContent;
+    ExtendableType compression;
+    ExtendableType interlaced;
+    ExtendableType chromaSiting;
+    std::vector<ui::PlaneLayout> planeLayouts;
+
+    status_t error = metadataDumpHelper(bufferDump, StandardMetadataType::BUFFER_ID,
+                                        gralloc4::decodeBufferId, &bufferId);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::NAME, gralloc4::decodeName, &name);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::WIDTH, gralloc4::decodeWidth,
+                               &width);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::HEIGHT, gralloc4::decodeHeight,
+                               &height);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::LAYER_COUNT,
+                               gralloc4::decodeLayerCount, &layerCount);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_REQUESTED,
+                               gralloc4::decodePixelFormatRequested, &pixelFormatRequested);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_FOURCC,
+                               gralloc4::decodePixelFormatFourCC, &pixelFormatFourCC);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_MODIFIER,
+                               gralloc4::decodePixelFormatModifier, &pixelFormatModifier);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::USAGE, gralloc4::decodeUsage,
+                               &usage);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::ALLOCATION_SIZE,
+                               gralloc4::decodeAllocationSize, &allocationSize);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::PROTECTED_CONTENT,
+                               gralloc4::decodeProtectedContent, &protectedContent);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::COMPRESSION,
+                               gralloc4::decodeCompression, &compression);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::INTERLACED,
+                               gralloc4::decodeInterlaced, &interlaced);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::CHROMA_SITING,
+                               gralloc4::decodeChromaSiting, &chromaSiting);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::PLANE_LAYOUTS,
+                               gralloc4::decodePlaneLayouts, &planeLayouts);
+    if (error != NO_ERROR) {
+        return error;
+    }
+
+    if (outAllocationSize) {
+        *outAllocationSize = allocationSize;
+    }
+    double allocationSizeKiB = static_cast<double>(allocationSize) / 1024;
+
+    *outDump << "+ name:" << name << ", id:" << bufferId << ", size:" << allocationSizeKiB
+             << "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage
+             << std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
+             << ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier
+             << ", compressed: ";
+
+    if (less) {
+        bool isCompressed = !gralloc4::isStandardCompression(compression) ||
+                (gralloc4::getStandardCompressionValue(compression) != ui::Compression::NONE);
+        *outDump << std::boolalpha << isCompressed << "\n";
+    } else {
+        *outDump << gralloc4::getCompressionName(compression) << "\n";
+    }
+
+    bool firstPlane = true;
+    for (const auto& planeLayout : planeLayouts) {
+        if (firstPlane) {
+            firstPlane = false;
+            *outDump << "\tplanes: ";
+        } else {
+            *outDump << "\t        ";
+        }
+
+        for (size_t i = 0; i < planeLayout.components.size(); i++) {
+            const auto& planeLayoutComponent = planeLayout.components[i];
+            *outDump << gralloc4::getPlaneLayoutComponentTypeName(planeLayoutComponent.type);
+            if (i < planeLayout.components.size() - 1) {
+                *outDump << "/";
+            } else {
+                *outDump << ":\t";
+            }
+        }
+        *outDump << " w/h:" << planeLayout.widthInSamples << "x" << planeLayout.heightInSamples
+                 << ", stride:" << planeLayout.strideInBytes
+                 << " bytes, size:" << planeLayout.totalSizeInBytes;
+        if (!less) {
+            *outDump << ", inc:" << planeLayout.sampleIncrementInBits
+                     << " bits, subsampling w/h:" << planeLayout.horizontalSubsampling << "x"
+                     << planeLayout.verticalSubsampling;
+        }
+        *outDump << "\n";
+    }
+
+    if (!less) {
+        *outDump << "\tlayer cnt: " << layerCount << ", protected content: " << protectedContent
+                 << ", interlaced: " << gralloc4::getInterlacedName(interlaced)
+                 << ", chroma siting:" << gralloc4::getChromaSitingName(chromaSiting) << "\n";
+    }
+
+    return NO_ERROR;
+}
+
+std::string Gralloc4Mapper::dumpBuffer(buffer_handle_t bufferHandle, bool less) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    BufferDump bufferDump;
+    Error error;
+    auto ret = mMapper->dumpBuffer(buffer, [&](const auto& tmpError, const auto& tmpBufferDump) {
+        error = tmpError;
+        bufferDump = tmpBufferDump;
+    });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("dumpBuffer() failed with %d", error);
+        return "";
+    }
+
+    std::ostringstream stream;
+    stream.precision(2);
+
+    status_t err = bufferDumpHelper(bufferDump, &stream, nullptr, less);
+    if (err != NO_ERROR) {
+        ALOGE("bufferDumpHelper() failed with %d", err);
+        return "";
+    }
+
+    return stream.str();
+}
+
+std::string Gralloc4Mapper::dumpBuffers(bool less) const {
+    hidl_vec<BufferDump> bufferDumps;
+    Error error;
+    auto ret = mMapper->dumpBuffers([&](const auto& tmpError, const auto& tmpBufferDump) {
+        error = tmpError;
+        bufferDumps = tmpBufferDump;
+    });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("dumpBuffer() failed with %d", error);
+        return "";
+    }
+
+    uint64_t totalAllocationSize = 0;
+    std::ostringstream stream;
+    stream.precision(2);
+
+    stream << "Imported gralloc buffers:\n";
+
+    for (const auto& bufferDump : bufferDumps) {
+        uint64_t allocationSize = 0;
+        status_t err = bufferDumpHelper(bufferDump, &stream, &allocationSize, less);
+        if (err != NO_ERROR) {
+            ALOGE("bufferDumpHelper() failed with %d", err);
+            return "";
+        }
+        totalAllocationSize += allocationSize;
+    }
+
+    double totalAllocationSizeKiB = static_cast<double>(totalAllocationSize) / 1024;
+    stream << "Total imported by gralloc: " << totalAllocationSizeKiB << "KiB\n";
+    return stream.str();
+}
+
 Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(mapper) {
     mAllocator = IAllocator::getService();
     if (mAllocator == nullptr) {
@@ -317,20 +1060,16 @@
     return mAllocator != nullptr;
 }
 
-std::string Gralloc4Allocator::dumpDebugInfo() const {
-    std::string debugInfo;
-
-    mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); });
-
-    return debugInfo;
+std::string Gralloc4Allocator::dumpDebugInfo(bool less) const {
+    return mMapper.dumpBuffers(less);
 }
 
-status_t Gralloc4Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format,
-                                     uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
-                                     uint32_t* outStride, buffer_handle_t* outBufferHandles,
-                                     bool importBuffers) const {
+status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height,
+                                     android::PixelFormat format, uint32_t layerCount,
+                                     uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+                                     buffer_handle_t* outBufferHandles, bool importBuffers) const {
     IMapper::BufferDescriptorInfo descriptorInfo;
-    sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+    sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage, &descriptorInfo);
 
     BufferDescriptor descriptor;
     status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 3a90a98..943d13e 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -78,11 +78,11 @@
     return total;
 }
 
-void GraphicBufferAllocator::dump(std::string& result) const {
+void GraphicBufferAllocator::dump(std::string& result, bool less) const {
     Mutex::Autolock _l(sLock);
     KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
     uint64_t total = 0;
-    result.append("Allocated buffers:\n");
+    result.append("GraphicBufferAllocator buffers:\n");
     const size_t c = list.size();
     for (size_t i=0 ; i<c ; i++) {
         const alloc_rec_t& rec(list.valueAt(i));
@@ -99,15 +99,15 @@
         }
         total += rec.size;
     }
-    StringAppendF(&result, "Total allocated (estimate): %.2f KB\n", static_cast<double>(total) / 1024.0);
+    StringAppendF(&result, "Total allocated by GraphicBufferAllocator (estimate): %.2f KB\n",
+                  static_cast<double>(total) / 1024.0);
 
-    result.append(mAllocator->dumpDebugInfo());
+    result.append(mAllocator->dumpDebugInfo(less));
 }
 
-void GraphicBufferAllocator::dumpToSystemLog()
-{
+void GraphicBufferAllocator::dumpToSystemLog(bool less) {
     std::string s;
-    GraphicBufferAllocator::getInstance().dump(s);
+    GraphicBufferAllocator::getInstance().dump(s, less);
     ALOGD("%s", s.c_str());
 }
 
@@ -137,8 +137,8 @@
     // TODO(b/72323293, b/72703005): Remove these invalid bits from callers
     usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
 
-    status_t error = mAllocator->allocate(width, height, format, layerCount, usage, 1, stride,
-                                          handle, importBuffer);
+    status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage,
+                                          1, stride, handle, importBuffer);
     if (error != NO_ERROR) {
         ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
               "usage %" PRIx64 ": %d",
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 4d087d1..d20bd7a 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -71,6 +71,17 @@
     LOG_ALWAYS_FATAL("gralloc-mapper is missing");
 }
 
+void GraphicBufferMapper::dumpBuffer(buffer_handle_t bufferHandle, std::string& result,
+                                     bool less) const {
+    result.append(mMapper->dumpBuffer(bufferHandle, less));
+}
+
+void GraphicBufferMapper::dumpBufferToSystemLog(buffer_handle_t bufferHandle, bool less) {
+    std::string s;
+    GraphicBufferMapper::getInstance().dumpBuffer(bufferHandle, s, less);
+    ALOGD("%s", s.c_str());
+}
+
 status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
         uint32_t width, uint32_t height, uint32_t layerCount,
         PixelFormat format, uint64_t usage, uint32_t stride,
@@ -177,5 +188,197 @@
                                           uint64_t usage, bool* outSupported) {
     return mMapper->isSupported(width, height, format, layerCount, usage, outSupported);
 }
+
+status_t GraphicBufferMapper::getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) {
+    return mMapper->getBufferId(bufferHandle, outBufferId);
+}
+
+status_t GraphicBufferMapper::getName(buffer_handle_t bufferHandle, std::string* outName) {
+    return mMapper->getName(bufferHandle, outName);
+}
+
+status_t GraphicBufferMapper::getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) {
+    return mMapper->getWidth(bufferHandle, outWidth);
+}
+
+status_t GraphicBufferMapper::getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) {
+    return mMapper->getHeight(bufferHandle, outHeight);
+}
+
+status_t GraphicBufferMapper::getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount) {
+    return mMapper->getLayerCount(bufferHandle, outLayerCount);
+}
+
+status_t GraphicBufferMapper::getPixelFormatRequested(buffer_handle_t bufferHandle,
+                                                      ui::PixelFormat* outPixelFormatRequested) {
+    return mMapper->getPixelFormatRequested(bufferHandle, outPixelFormatRequested);
+}
+
+status_t GraphicBufferMapper::getPixelFormatFourCC(buffer_handle_t bufferHandle,
+                                                   uint32_t* outPixelFormatFourCC) {
+    return mMapper->getPixelFormatFourCC(bufferHandle, outPixelFormatFourCC);
+}
+
+status_t GraphicBufferMapper::getPixelFormatModifier(buffer_handle_t bufferHandle,
+                                                     uint64_t* outPixelFormatModifier) {
+    return mMapper->getPixelFormatModifier(bufferHandle, outPixelFormatModifier);
+}
+
+status_t GraphicBufferMapper::getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) {
+    return mMapper->getUsage(bufferHandle, outUsage);
+}
+
+status_t GraphicBufferMapper::getAllocationSize(buffer_handle_t bufferHandle,
+                                                uint64_t* outAllocationSize) {
+    return mMapper->getAllocationSize(bufferHandle, outAllocationSize);
+}
+
+status_t GraphicBufferMapper::getProtectedContent(buffer_handle_t bufferHandle,
+                                                  uint64_t* outProtectedContent) {
+    return mMapper->getProtectedContent(bufferHandle, outProtectedContent);
+}
+
+status_t GraphicBufferMapper::getCompression(
+        buffer_handle_t bufferHandle,
+        aidl::android::hardware::graphics::common::ExtendableType* outCompression) {
+    return mMapper->getCompression(bufferHandle, outCompression);
+}
+
+status_t GraphicBufferMapper::getCompression(buffer_handle_t bufferHandle,
+                                             ui::Compression* outCompression) {
+    return mMapper->getCompression(bufferHandle, outCompression);
+}
+
+status_t GraphicBufferMapper::getInterlaced(
+        buffer_handle_t bufferHandle,
+        aidl::android::hardware::graphics::common::ExtendableType* outInterlaced) {
+    return mMapper->getInterlaced(bufferHandle, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getInterlaced(buffer_handle_t bufferHandle,
+                                            ui::Interlaced* outInterlaced) {
+    return mMapper->getInterlaced(bufferHandle, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getChromaSiting(
+        buffer_handle_t bufferHandle,
+        aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting) {
+    return mMapper->getChromaSiting(bufferHandle, outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getChromaSiting(buffer_handle_t bufferHandle,
+                                              ui::ChromaSiting* outChromaSiting) {
+    return mMapper->getChromaSiting(bufferHandle, outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getPlaneLayouts(buffer_handle_t bufferHandle,
+                                              std::vector<ui::PlaneLayout>* outPlaneLayouts) {
+    return mMapper->getPlaneLayouts(bufferHandle, outPlaneLayouts);
+}
+
+status_t GraphicBufferMapper::getDataspace(buffer_handle_t bufferHandle,
+                                           ui::Dataspace* outDataspace) {
+    return mMapper->getDataspace(bufferHandle, outDataspace);
+}
+
+status_t GraphicBufferMapper::getBlendMode(buffer_handle_t bufferHandle,
+                                           ui::BlendMode* outBlendMode) {
+    return mMapper->getBlendMode(bufferHandle, outBlendMode);
+}
+
+status_t GraphicBufferMapper::getSmpte2086(buffer_handle_t bufferHandle,
+                                           std::optional<ui::Smpte2086>* outSmpte2086) {
+    return mMapper->getSmpte2086(bufferHandle, outSmpte2086);
+}
+
+status_t GraphicBufferMapper::getCta861_3(buffer_handle_t bufferHandle,
+                                          std::optional<ui::Cta861_3>* outCta861_3) {
+    return mMapper->getCta861_3(bufferHandle, outCta861_3);
+}
+
+status_t GraphicBufferMapper::getSmpte2094_40(
+        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_40) {
+    return mMapper->getSmpte2094_40(bufferHandle, outSmpte2094_40);
+}
+
+status_t GraphicBufferMapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
+                                                          PixelFormat format, uint32_t layerCount,
+                                                          uint64_t usage,
+                                                          uint32_t* outPixelFormatFourCC) {
+    return mMapper->getDefaultPixelFormatFourCC(width, height, format, layerCount, usage,
+                                                outPixelFormatFourCC);
+}
+
+status_t GraphicBufferMapper::getDefaultPixelFormatModifier(uint32_t width, uint32_t height,
+                                                            PixelFormat format, uint32_t layerCount,
+                                                            uint64_t usage,
+                                                            uint64_t* outPixelFormatModifier) {
+    return mMapper->getDefaultPixelFormatModifier(width, height, format, layerCount, usage,
+                                                  outPixelFormatModifier);
+}
+
+status_t GraphicBufferMapper::getDefaultAllocationSize(uint32_t width, uint32_t height,
+                                                       PixelFormat format, uint32_t layerCount,
+                                                       uint64_t usage,
+                                                       uint64_t* outAllocationSize) {
+    return mMapper->getDefaultAllocationSize(width, height, format, layerCount, usage,
+                                             outAllocationSize);
+}
+
+status_t GraphicBufferMapper::getDefaultProtectedContent(uint32_t width, uint32_t height,
+                                                         PixelFormat format, uint32_t layerCount,
+                                                         uint64_t usage,
+                                                         uint64_t* outProtectedContent) {
+    return mMapper->getDefaultProtectedContent(width, height, format, layerCount, usage,
+                                               outProtectedContent);
+}
+
+status_t GraphicBufferMapper::getDefaultCompression(
+        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+        aidl::android::hardware::graphics::common::ExtendableType* outCompression) {
+    return mMapper->getDefaultCompression(width, height, format, layerCount, usage, outCompression);
+}
+
+status_t GraphicBufferMapper::getDefaultCompression(uint32_t width, uint32_t height,
+                                                    PixelFormat format, uint32_t layerCount,
+                                                    uint64_t usage,
+                                                    ui::Compression* outCompression) {
+    return mMapper->getDefaultCompression(width, height, format, layerCount, usage, outCompression);
+}
+
+status_t GraphicBufferMapper::getDefaultInterlaced(
+        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+        aidl::android::hardware::graphics::common::ExtendableType* outInterlaced) {
+    return mMapper->getDefaultInterlaced(width, height, format, layerCount, usage, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getDefaultInterlaced(uint32_t width, uint32_t height,
+                                                   PixelFormat format, uint32_t layerCount,
+                                                   uint64_t usage, ui::Interlaced* outInterlaced) {
+    return mMapper->getDefaultInterlaced(width, height, format, layerCount, usage, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getDefaultChromaSiting(
+        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+        aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting) {
+    return mMapper->getDefaultChromaSiting(width, height, format, layerCount, usage,
+                                           outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getDefaultChromaSiting(uint32_t width, uint32_t height,
+                                                     PixelFormat format, uint32_t layerCount,
+                                                     uint64_t usage,
+                                                     ui::ChromaSiting* outChromaSiting) {
+    return mMapper->getDefaultChromaSiting(width, height, format, layerCount, usage,
+                                           outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getDefaultPlaneLayouts(
+        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+        std::vector<ui::PlaneLayout>* outPlaneLayouts) {
+    return mMapper->getDefaultPlaneLayouts(width, height, format, layerCount, usage,
+                                           outPlaneLayouts);
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index c28f7a5..e199648 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -17,13 +17,15 @@
 #ifndef ANDROID_UI_GRALLOC_H
 #define ANDROID_UI_GRALLOC_H
 
-#include <string>
-
+#include <gralloctypes/Gralloc4.h>
 #include <hidl/HidlSupport.h>
+#include <ui/GraphicTypes.h>
 #include <ui/PixelFormat.h>
 #include <ui/Rect.h>
 #include <utils/StrongPointer.h>
 
+#include <string>
+
 namespace android {
 
 // A wrapper to IMapper
@@ -33,6 +35,10 @@
 
     virtual bool isLoaded() const = 0;
 
+    virtual std::string dumpBuffer(buffer_handle_t /*bufferHandle*/, bool /*less*/) const {
+        return "";
+    }
+
     virtual status_t createDescriptor(void* bufferDescriptorInfo,
                                       void* outBufferDescriptor) const = 0;
 
@@ -74,8 +80,176 @@
     // allocated if resources are available.  If false, a buffer with the given specifications will
     // never successfully allocate on this device. Note that this function is not guaranteed to be
     // supported on all devices, in which case a status_t of INVALID_OPERATION will be returned.
-    virtual status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
-                                 uint32_t layerCount, uint64_t usage, bool* outSupported) const = 0;
+    virtual status_t isSupported(uint32_t /*width*/, uint32_t /*height*/,
+                                 android::PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                 uint64_t /*usage*/, bool* /*outSupported*/) const {
+        return INVALID_OPERATION;
+    }
+
+    virtual status_t getBufferId(buffer_handle_t /*bufferHandle*/,
+                                 uint64_t* /*outBufferId*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getName(buffer_handle_t /*bufferHandle*/, std::string* /*outName*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getWidth(buffer_handle_t /*bufferHandle*/, uint64_t* /*outWidth*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getHeight(buffer_handle_t /*bufferHandle*/, uint64_t* /*outHeight*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getLayerCount(buffer_handle_t /*bufferHandle*/,
+                                   uint64_t* /*outLayerCount*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getPixelFormatRequested(buffer_handle_t /*bufferHandle*/,
+                                             ui::PixelFormat* /*outPixelFormatRequested*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getPixelFormatFourCC(buffer_handle_t /*bufferHandle*/,
+                                          uint32_t* /*outPixelFormatFourCC*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getPixelFormatModifier(buffer_handle_t /*bufferHandle*/,
+                                            uint64_t* /*outPixelFormatModifier*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getUsage(buffer_handle_t /*bufferHandle*/, uint64_t* /*outUsage*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getAllocationSize(buffer_handle_t /*bufferHandle*/,
+                                       uint64_t* /*outAllocationSize*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getProtectedContent(buffer_handle_t /*bufferHandle*/,
+                                         uint64_t* /*outProtectedContent*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getCompression(
+            buffer_handle_t /*bufferHandle*/,
+            aidl::android::hardware::graphics::common::ExtendableType* /*outCompression*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getCompression(buffer_handle_t /*bufferHandle*/,
+                                    ui::Compression* /*outCompression*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getInterlaced(
+            buffer_handle_t /*bufferHandle*/,
+            aidl::android::hardware::graphics::common::ExtendableType* /*outInterlaced*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getInterlaced(buffer_handle_t /*bufferHandle*/,
+                                   ui::Interlaced* /*outInterlaced*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getChromaSiting(
+            buffer_handle_t /*bufferHandle*/,
+            aidl::android::hardware::graphics::common::ExtendableType* /*outChromaSiting*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getChromaSiting(buffer_handle_t /*bufferHandle*/,
+                                     ui::ChromaSiting* /*outChromaSiting*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getPlaneLayouts(buffer_handle_t /*bufferHandle*/,
+                                     std::vector<ui::PlaneLayout>* /*outPlaneLayouts*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDataspace(buffer_handle_t /*bufferHandle*/,
+                                  ui::Dataspace* /*outDataspace*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getBlendMode(buffer_handle_t /*bufferHandle*/,
+                                  ui::BlendMode* /*outBlendMode*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getSmpte2086(buffer_handle_t /*bufferHandle*/,
+                                  std::optional<ui::Smpte2086>* /*outSmpte2086*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getCta861_3(buffer_handle_t /*bufferHandle*/,
+                                 std::optional<ui::Cta861_3>* /*outCta861_3*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getSmpte2094_40(
+            buffer_handle_t /*bufferHandle*/,
+            std::optional<std::vector<uint8_t>>* /*outSmpte2094_40*/) const {
+        return INVALID_OPERATION;
+    }
+
+    virtual status_t getDefaultPixelFormatFourCC(uint32_t /*width*/, uint32_t /*height*/,
+                                                 PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                                 uint64_t /*usage*/,
+                                                 uint32_t* /*outPixelFormatFourCC*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultPixelFormatModifier(uint32_t /*width*/, uint32_t /*height*/,
+                                                   PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                                   uint64_t /*usage*/,
+                                                   uint64_t* /*outPixelFormatModifier*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultAllocationSize(uint32_t /*width*/, uint32_t /*height*/,
+                                              PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                              uint64_t /*usage*/,
+                                              uint64_t* /*outAllocationSize*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultProtectedContent(uint32_t /*width*/, uint32_t /*height*/,
+                                                PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                                uint64_t /*usage*/,
+                                                uint64_t* /*outProtectedContent*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultCompression(
+            uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+            uint32_t /*layerCount*/, uint64_t /*usage*/,
+            aidl::android::hardware::graphics::common::ExtendableType* /*outCompression*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultCompression(uint32_t /*width*/, uint32_t /*height*/,
+                                           PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                           uint64_t /*usage*/,
+                                           ui::Compression* /*outCompression*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultInterlaced(
+            uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+            uint32_t /*layerCount*/, uint64_t /*usage*/,
+            aidl::android::hardware::graphics::common::ExtendableType* /*outInterlaced*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultInterlaced(uint32_t /*width*/, uint32_t /*height*/,
+                                          PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                          uint64_t /*usage*/,
+                                          ui::Interlaced* /*outInterlaced*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultChromaSiting(
+            uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+            uint32_t /*layerCount*/, uint64_t /*usage*/,
+            aidl::android::hardware::graphics::common::ExtendableType* /*outChromaSiting*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultChromaSiting(uint32_t /*width*/, uint32_t /*height*/,
+                                            PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                            uint64_t /*usage*/,
+                                            ui::ChromaSiting* /*outChromaSiting*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultPlaneLayouts(
+            uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+            uint32_t /*layerCount*/, uint64_t /*usage*/,
+            std::vector<ui::PlaneLayout>* /*outPlaneLayouts*/) const {
+        return INVALID_OPERATION;
+    }
+
+    virtual std::vector<android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription>
+    listSupportedMetadataTypes() const {
+        return {};
+    }
 };
 
 // A wrapper to IAllocator
@@ -85,16 +259,17 @@
 
     virtual bool isLoaded() const = 0;
 
-    virtual std::string dumpDebugInfo() const = 0;
+    virtual std::string dumpDebugInfo(bool less = true) const = 0;
 
     /*
      * The returned buffers are already imported and must not be imported
      * again.  outBufferHandles must point to a space that can contain at
      * least "bufferCount" buffer_handle_t.
      */
-    virtual status_t allocate(uint32_t width, uint32_t height, PixelFormat format,
-                              uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
-                              uint32_t* outStride, buffer_handle_t* outBufferHandles,
+    virtual status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+                              PixelFormat format, uint32_t layerCount, uint64_t usage,
+                              uint32_t bufferCount, uint32_t* outStride,
+                              buffer_handle_t* outBufferHandles,
                               bool importBuffers = true) const = 0;
 };
 
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index 12c772a..f570c42 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -61,9 +61,6 @@
 
     int unlock(buffer_handle_t bufferHandle) const override;
 
-    status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
-                         uint32_t layerCount, uint64_t usage, bool* outSupported) const override;
-
 private:
     // Determines whether the passed info is compatible with the mapper.
     status_t validateBufferDescriptorInfo(
@@ -81,11 +78,12 @@
 
     bool isLoaded() const override;
 
-    std::string dumpDebugInfo() const override;
+    std::string dumpDebugInfo(bool less = true) const override;
 
-    status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
-                      uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
-                      buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
+    status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+                      PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                      uint32_t* outStride, buffer_handle_t* outBufferHandles,
+                      bool importBuffers = true) const override;
 
 private:
     const Gralloc2Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
index bfbc2aa..93a5077 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -79,11 +79,12 @@
 
     bool isLoaded() const override;
 
-    std::string dumpDebugInfo() const override;
+    std::string dumpDebugInfo(bool less = true) const override;
 
-    status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
-                      uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
-                      buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
+    status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+                      PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                      uint32_t* outStride, buffer_handle_t* outBufferHandles,
+                      bool importBuffers = true) const override;
 
 private:
     const Gralloc3Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index 60115f9..4729cba 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -17,16 +17,17 @@
 #ifndef ANDROID_UI_GRALLOC4_H
 #define ANDROID_UI_GRALLOC4_H
 
-#include <string>
-
 #include <android/hardware/graphics/allocator/4.0/IAllocator.h>
 #include <android/hardware/graphics/common/1.1/types.h>
 #include <android/hardware/graphics/mapper/4.0/IMapper.h>
+#include <gralloctypes/Gralloc4.h>
 #include <ui/Gralloc.h>
-#include <ui/PixelFormat.h>
+#include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <utils/StrongPointer.h>
 
+#include <string>
+
 namespace android {
 
 class Gralloc4Mapper : public GrallocMapper {
@@ -37,6 +38,9 @@
 
     bool isLoaded() const override;
 
+    std::string dumpBuffer(buffer_handle_t bufferHandle, bool less = true) const override;
+    std::string dumpBuffers(bool less = true) const;
+
     status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
 
     status_t importBuffer(const hardware::hidl_handle& rawHandle,
@@ -45,7 +49,7 @@
     void freeBuffer(buffer_handle_t bufferHandle) const override;
 
     status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height,
-                                android::PixelFormat format, uint32_t layerCount, uint64_t usage,
+                                PixelFormat format, uint32_t layerCount, uint64_t usage,
                                 uint32_t stride) const override;
 
     void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
@@ -60,14 +64,123 @@
 
     int unlock(buffer_handle_t bufferHandle) const override;
 
-    status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
-                         uint32_t layerCount, uint64_t usage, bool* outSupported) const override;
+    status_t isSupported(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+                         uint64_t usage, bool* outSupported) const override;
+
+    status_t getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) const override;
+    status_t getName(buffer_handle_t bufferHandle, std::string* outName) const override;
+    status_t getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) const override;
+    status_t getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) const override;
+    status_t getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount) const override;
+    status_t getPixelFormatRequested(buffer_handle_t bufferHandle,
+                                     ui::PixelFormat* outPixelFormatRequested) const override;
+    status_t getPixelFormatFourCC(buffer_handle_t bufferHandle,
+                                  uint32_t* outPixelFormatFourCC) const override;
+    status_t getPixelFormatModifier(buffer_handle_t bufferHandle,
+                                    uint64_t* outPixelFormatModifier) const override;
+    status_t getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) const override;
+    status_t getAllocationSize(buffer_handle_t bufferHandle,
+                               uint64_t* outAllocationSize) const override;
+    status_t getProtectedContent(buffer_handle_t bufferHandle,
+                                 uint64_t* outProtectedContent) const override;
+    status_t getCompression(buffer_handle_t bufferHandle,
+                            aidl::android::hardware::graphics::common::ExtendableType*
+                                    outCompression) const override;
+    status_t getCompression(buffer_handle_t bufferHandle,
+                            ui::Compression* outCompression) const override;
+    status_t getInterlaced(buffer_handle_t bufferHandle,
+                           aidl::android::hardware::graphics::common::ExtendableType* outInterlaced)
+            const override;
+    status_t getInterlaced(buffer_handle_t bufferHandle,
+                           ui::Interlaced* outInterlaced) const override;
+    status_t getChromaSiting(buffer_handle_t bufferHandle,
+                             aidl::android::hardware::graphics::common::ExtendableType*
+                                     outChromaSiting) const override;
+    status_t getChromaSiting(buffer_handle_t bufferHandle,
+                             ui::ChromaSiting* outChromaSiting) const override;
+    status_t getPlaneLayouts(buffer_handle_t bufferHandle,
+                             std::vector<ui::PlaneLayout>* outPlaneLayouts) const override;
+    status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace) const override;
+    status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode) const override;
+    status_t getSmpte2086(buffer_handle_t bufferHandle,
+                          std::optional<ui::Smpte2086>* outSmpte2086) const override;
+    status_t getCta861_3(buffer_handle_t bufferHandle,
+                         std::optional<ui::Cta861_3>* outCta861_3) const override;
+    status_t getSmpte2094_40(buffer_handle_t bufferHandle,
+                             std::optional<std::vector<uint8_t>>* outSmpte2094_40) const override;
+
+    status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
+                                         uint32_t layerCount, uint64_t usage,
+                                         uint32_t* outPixelFormatFourCC) const override;
+    status_t getDefaultPixelFormatModifier(uint32_t width, uint32_t height, PixelFormat format,
+                                           uint32_t layerCount, uint64_t usage,
+                                           uint64_t* outPixelFormatModifier) const override;
+    status_t getDefaultAllocationSize(uint32_t width, uint32_t height, PixelFormat format,
+                                      uint32_t layerCount, uint64_t usage,
+                                      uint64_t* outAllocationSize) const override;
+    status_t getDefaultProtectedContent(uint32_t width, uint32_t height, PixelFormat format,
+                                        uint32_t layerCount, uint64_t usage,
+                                        uint64_t* outProtectedContent) const override;
+    status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+                                   uint32_t layerCount, uint64_t usage,
+                                   aidl::android::hardware::graphics::common::ExtendableType*
+                                           outCompression) const override;
+    status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+                                   uint32_t layerCount, uint64_t usage,
+                                   ui::Compression* outCompression) const override;
+    status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+                                  uint32_t layerCount, uint64_t usage,
+                                  aidl::android::hardware::graphics::common::ExtendableType*
+                                          outInterlaced) const override;
+    status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+                                  uint32_t layerCount, uint64_t usage,
+                                  ui::Interlaced* outInterlaced) const override;
+    status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+                                    uint32_t layerCount, uint64_t usage,
+                                    aidl::android::hardware::graphics::common::ExtendableType*
+                                            outChromaSiting) const override;
+    status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+                                    uint32_t layerCount, uint64_t usage,
+                                    ui::ChromaSiting* outChromaSiting) const override;
+    status_t getDefaultPlaneLayouts(uint32_t width, uint32_t height, PixelFormat format,
+                                    uint32_t layerCount, uint64_t usage,
+                                    std::vector<ui::PlaneLayout>* outPlaneLayouts) const override;
+
+    std::vector<android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription>
+    listSupportedMetadataTypes() const;
 
 private:
+    friend class GraphicBufferAllocator;
+
     // Determines whether the passed info is compatible with the mapper.
     status_t validateBufferDescriptorInfo(
             hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo* descriptorInfo) const;
 
+    template <class T>
+    using DecodeFunction = status_t (*)(const hardware::hidl_vec<uint8_t>& input, T* output);
+
+    template <class T>
+    status_t get(
+            buffer_handle_t bufferHandle,
+            const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+            DecodeFunction<T> decodeFunction, T* outMetadata) const;
+
+    template <class T>
+    status_t getDefault(
+            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+            uint64_t usage,
+            const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+            DecodeFunction<T> decodeFunction, T* outMetadata) const;
+
+    template <class T>
+    status_t metadataDumpHelper(
+            const android::hardware::graphics::mapper::V4_0::IMapper::BufferDump& bufferDump,
+            aidl::android::hardware::graphics::common::StandardMetadataType metadataType,
+            DecodeFunction<T> decodeFunction, T* outT) const;
+    status_t bufferDumpHelper(
+            const android::hardware::graphics::mapper::V4_0::IMapper::BufferDump& bufferDump,
+            std::ostringstream* outDump, uint64_t* outAllocationSize, bool less) const;
+
     sp<hardware::graphics::mapper::V4_0::IMapper> mMapper;
 };
 
@@ -79,11 +192,12 @@
 
     bool isLoaded() const override;
 
-    std::string dumpDebugInfo() const override;
+    std::string dumpDebugInfo(bool less = true) const override;
 
-    status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
-                      uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
-                      buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
+    status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+                      PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                      uint32_t* outStride, buffer_handle_t* outBufferHandles,
+                      bool importBuffers = true) const override;
 
 private:
     const Gralloc4Mapper& mMapper;
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index a182104..3ed988c 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -1,19 +1,19 @@
 /*
-**
-** Copyright 2009, 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.
-*/
+ *
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 #ifndef ANDROID_BUFFER_ALLOCATOR_H
 #define ANDROID_BUFFER_ALLOCATOR_H
@@ -74,8 +74,8 @@
 
     uint64_t getTotalSize() const;
 
-    void dump(std::string& res) const;
-    static void dumpToSystemLog();
+    void dump(std::string& res, bool less = true) const;
+    static void dumpToSystemLog(bool less = true);
 
 protected:
     struct alloc_rec_t {
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 83fb144..837e3d8 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -22,11 +22,11 @@
 
 #include <memory>
 
+#include <ui/GraphicTypes.h>
 #include <ui/PixelFormat.h>
 #include <ui/Rect.h>
 #include <utils/Singleton.h>
 
-
 // Needed by code that still uses the GRALLOC_USAGE_* constants.
 // when/if we get rid of gralloc, we should provide aliases or fix call sites.
 #include <hardware/gralloc.h>
@@ -49,6 +49,9 @@
     static void preloadHal();
     static inline GraphicBufferMapper& get() { return getInstance(); }
 
+    void dumpBuffer(buffer_handle_t bufferHandle, std::string& result, bool less = true) const;
+    static void dumpBufferToSystemLog(buffer_handle_t bufferHandle, bool less = true);
+
     // The imported outHandle must be freed with freeBuffer when no longer
     // needed. rawHandle is owned by the caller.
     status_t importBuffer(buffer_handle_t rawHandle,
@@ -86,6 +89,86 @@
     status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
                          uint32_t layerCount, uint64_t usage, bool* outSupported);
 
+    /**
+     * Gets the gralloc metadata associated with the buffer.
+     *
+     * These functions are supported by gralloc 4.0+.
+     */
+    status_t getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId);
+    status_t getName(buffer_handle_t bufferHandle, std::string* outName);
+    status_t getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth);
+    status_t getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight);
+    status_t getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount);
+    status_t getPixelFormatRequested(buffer_handle_t bufferHandle,
+                                     ui::PixelFormat* outPixelFormatRequested);
+    status_t getPixelFormatFourCC(buffer_handle_t bufferHandle, uint32_t* outPixelFormatFourCC);
+    status_t getPixelFormatModifier(buffer_handle_t bufferHandle, uint64_t* outPixelFormatModifier);
+    status_t getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage);
+    status_t getAllocationSize(buffer_handle_t bufferHandle, uint64_t* outAllocationSize);
+    status_t getProtectedContent(buffer_handle_t bufferHandle, uint64_t* outProtectedContent);
+    status_t getCompression(
+            buffer_handle_t bufferHandle,
+            aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+    status_t getCompression(buffer_handle_t bufferHandle, ui::Compression* outCompression);
+    status_t getInterlaced(
+            buffer_handle_t bufferHandle,
+            aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+    status_t getInterlaced(buffer_handle_t bufferHandle, ui::Interlaced* outInterlaced);
+    status_t getChromaSiting(
+            buffer_handle_t bufferHandle,
+            aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+    status_t getChromaSiting(buffer_handle_t bufferHandle, ui::ChromaSiting* outChromaSiting);
+    status_t getPlaneLayouts(buffer_handle_t bufferHandle,
+                             std::vector<ui::PlaneLayout>* outPlaneLayouts);
+    status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace);
+    status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode);
+    status_t getSmpte2086(buffer_handle_t bufferHandle, std::optional<ui::Smpte2086>* outSmpte2086);
+    status_t getCta861_3(buffer_handle_t bufferHandle, std::optional<ui::Cta861_3>* outCta861_3);
+    status_t getSmpte2094_40(buffer_handle_t bufferHandle,
+                             std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+
+    /**
+     * Gets the default metadata for a gralloc buffer allocated with the given parameters.
+     *
+     * These functions are supported by gralloc 4.0+.
+     */
+    status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
+                                         uint32_t layerCount, uint64_t usage,
+                                         uint32_t* outPixelFormatFourCC);
+    status_t getDefaultPixelFormatModifier(uint32_t width, uint32_t height, PixelFormat format,
+                                           uint32_t layerCount, uint64_t usage,
+                                           uint64_t* outPixelFormatModifier);
+    status_t getDefaultAllocationSize(uint32_t width, uint32_t height, PixelFormat format,
+                                      uint32_t layerCount, uint64_t usage,
+                                      uint64_t* outAllocationSize);
+    status_t getDefaultProtectedContent(uint32_t width, uint32_t height, PixelFormat format,
+                                        uint32_t layerCount, uint64_t usage,
+                                        uint64_t* outProtectedContent);
+    status_t getDefaultCompression(
+            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+            uint64_t usage,
+            aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+    status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+                                   uint32_t layerCount, uint64_t usage,
+                                   ui::Compression* outCompression);
+    status_t getDefaultInterlaced(
+            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+            uint64_t usage,
+            aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+    status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+                                  uint32_t layerCount, uint64_t usage,
+                                  ui::Interlaced* outInterlaced);
+    status_t getDefaultChromaSiting(
+            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+            uint64_t usage,
+            aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+    status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+                                    uint32_t layerCount, uint64_t usage,
+                                    ui::ChromaSiting* outChromaSiting);
+    status_t getDefaultPlaneLayouts(uint32_t width, uint32_t height, PixelFormat format,
+                                    uint32_t layerCount, uint64_t usage,
+                                    std::vector<ui::PlaneLayout>* outPlaneLayouts);
+
     const GrallocMapper& getGrallocMapper() const {
         return reinterpret_cast<const GrallocMapper&>(*mMapper);
     }
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index d7411ea..4bdacb0 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -16,21 +16,51 @@
 
 #pragma once
 
+#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Cta861_3.h>
+#include <aidl/android/hardware/graphics/common/Interlaced.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <aidl/android/hardware/graphics/common/Smpte2086.h>
 #include <android/hardware/graphics/common/1.1/types.h>
 #include <android/hardware/graphics/common/1.2/types.h>
 #include <system/graphics.h>
 
 namespace android {
 
-// android::ui::* in this header file will alias different types as
-// the HIDL interface is updated.
+/**
+ * android::ui::* in this header file will alias different types as
+ * the HIDL and stable AIDL interfaces are updated.
+ */
 namespace ui {
 
+/**
+ * The following HIDL types should be moved to their stable AIDL
+ * equivalents when composer moves to stable AIDL.
+ */
 using android::hardware::graphics::common::V1_1::RenderIntent;
 using android::hardware::graphics::common::V1_2::ColorMode;
 using android::hardware::graphics::common::V1_2::Dataspace;
 using android::hardware::graphics::common::V1_2::Hdr;
 using android::hardware::graphics::common::V1_2::PixelFormat;
 
+/**
+ * Stable AIDL types
+ */
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::Smpte2086;
+
+/**
+ * The following stable AIDL types below have standard platform definitions
+ * that can be extended by vendors. The extensions are not defined here
+ * because they cannot be understood by the framework.
+ */
+using ChromaSiting = aidl::android::hardware::graphics::common::ChromaSiting;
+using Compression = aidl::android::hardware::graphics::common::Compression;
+using Interlaced = aidl::android::hardware::graphics::common::Interlaced;
+
 }  // namespace ui
 }  // namespace android
diff --git a/libs/ui/include_vndk/ui/Gralloc2.h b/libs/ui/include_vndk/ui/Gralloc2.h
deleted file mode 120000
index 66098c4..0000000
--- a/libs/ui/include_vndk/ui/Gralloc2.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/Gralloc2.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index d2ad242..ff55bc2 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -38,8 +38,9 @@
         "libgmock",
     ],
     shared_libs: [
+        "libhidlbase",
         "liblog",
-	"libui",
+        "libui",
     ],
     srcs: [
         "GraphicBufferAllocator_test.cpp",
diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp
index efca083..f4c0afa 100644
--- a/libs/ui/tests/GraphicBufferAllocator_test.cpp
+++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp
@@ -51,7 +51,7 @@
         std::cout << "Setting expected stride to " << stride << std::endl;
         EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())),
                     allocate)
-                .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err)));
+                .WillOnce(DoAll(SetArgPointee<7>(stride), Return(err)));
     }
     std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; }
 };
diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h
index 7660e9f..d62e3e2 100644
--- a/libs/ui/tests/mock/MockGrallocAllocator.h
+++ b/libs/ui/tests/mock/MockGrallocAllocator.h
@@ -32,10 +32,10 @@
     ~MockGrallocAllocator() override;
 
     MOCK_METHOD(bool, isLoaded, (), (const, override));
-    MOCK_METHOD(std::string, dumpDebugInfo, (), (const, override));
+    MOCK_METHOD(std::string, dumpDebugInfo, (bool less), (const, override));
     MOCK_METHOD(status_t, allocate,
-                (uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
-                 uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+                (std::string requestorName, uint32_t width, uint32_t height, PixelFormat format,
+                 uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
                  buffer_handle_t* outBufferHandles, bool less),
                 (const, override));
 };
diff --git a/libs/vr/OWNERS b/libs/vr/OWNERS
index ec2d712..098c80e 100644
--- a/libs/vr/OWNERS
+++ b/libs/vr/OWNERS
@@ -1,4 +1,4 @@
 hendrikw@google.com
 jwcai@google.com
 steventhomas@google.com
-
+patplunkett@google.com
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
index 1a9d727..23a4224 100644
--- a/libs/vr/libpdx/Android.bp
+++ b/libs/vr/libpdx/Android.bp
@@ -53,6 +53,8 @@
         "libpdx",
         "liblog",
         "libutils",
+    ],
+    shared_libs: [
         "libvndksupport",
     ],
 }
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 1fc7927..5162ba4 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -984,6 +984,8 @@
                     if (attr == EGL_CONTEXT_CLIENT_VERSION) {
                         if (value == 1) {
                             version = egl_connection_t::GLESv1_INDEX;
+                            android::GraphicsEnv::getInstance().setTargetStats(
+                                    android::GpuStatsInfo::Stats::GLES_1_IN_USE);
                         } else if (value == 2 || value == 3) {
                             version = egl_connection_t::GLESv2_INDEX;
                         }
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index da9bb49..41fcf1b 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -91,8 +91,8 @@
 rm src/*.class
 
 # Add UnsupportedAppUsage.java to known sources.
-mkdir -p out/android/annotation
-cp ../../../../base/core/java/android/annotation/UnsupportedAppUsage.java out/android/annotation
+mkdir -p out/android/compat/annotation
+cp ../../../../../tools/platform-compat/annotation/src/java/android/compat/annotation/UnsupportedAppUsage.java out/android/compat/annotation
 
 pushd out > /dev/null
 mkdir classes
@@ -114,7 +114,7 @@
                     android/opengl/GLES31.java \
                     android/opengl/GLES31Ext.java \
                     android/opengl/GLES32.java \
-                    android/annotation/UnsupportedAppUsage.java
+                    android/compat/annotation/UnsupportedAppUsage.java
 popd > /dev/null
 JAVA_RESULT=$?
 if [ $JAVA_RESULT -ne 0 ]; then
diff --git a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
index 12728f5..9932556 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
+++ b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
@@ -18,7 +18,7 @@
 
 package android.opengl;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.SurfaceTexture;
 import android.view.Surface;
 import android.view.SurfaceView;
diff --git a/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if b/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if
index c2711aa..7db1101 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if
+++ b/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if
@@ -19,7 +19,7 @@
 
 package android.opengl;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /** OpenGL ES 2.0
  */
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index 67babd4..7fff6ed 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -145,6 +145,9 @@
         case GpuStatsInfo::Stats::FALSE_PREROTATION:
             mAppStats[appStatsKey].falsePrerotation = true;
             break;
+        case GpuStatsInfo::Stats::GLES_1_IN_USE:
+            mAppStats[appStatsKey].gles1InUse = true;
+            break;
         default:
             break;
     }
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
new file mode 100644
index 0000000..385b981
--- /dev/null
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -0,0 +1,22 @@
+cc_benchmark {
+    name: "inputflinger_benchmarks",
+    srcs: [
+        "InputDispatcher_benchmarks.cpp",
+    ],
+    defaults: ["inputflinger_defaults"],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "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..246e735
--- /dev/null
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -0,0 +1,316 @@
+/*
+ * 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 const int32_t INJECT_EVENT_TIMEOUT = 5000;
+static const int32_t DISPATCHING_TIMEOUT = 100000;
+
+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;
+        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;
+        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(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                     AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0,
+                     /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+                     /* 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(/* sequenceNum */ 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({window}, ADISPLAY_ID_DEFAULT);
+
+    NotifyMotionArgs motionArgs = generateMotionArgs();
+
+    for (auto _ : state) {
+        // Send ACTION_DOWN
+        motionArgs.action = AMOTION_EVENT_ACTION_DOWN;
+        motionArgs.sequenceNum = 0;
+        motionArgs.downTime = now();
+        motionArgs.eventTime = motionArgs.downTime;
+        dispatcher->notifyMotion(&motionArgs);
+
+        // Send ACTION_UP
+        motionArgs.action = AMOTION_EVENT_ACTION_UP;
+        motionArgs.sequenceNum = 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({window}, ADISPLAY_ID_DEFAULT);
+
+    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/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index dcb3ebc..a3cb4f8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -240,6 +240,22 @@
     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;
+}
+
 // --- InputDispatcherThread ---
 
 class InputDispatcher::InputDispatcherThread : public Thread {
@@ -731,10 +747,6 @@
 #endif
 }
 
-bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
-    return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
-}
-
 bool InputDispatcher::haveCommandsLocked() const {
     return !mCommandQueue.empty();
 }
@@ -1099,7 +1111,7 @@
         sp<Connection> connection =
                 getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
         if (connection != nullptr) {
-            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
+            prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
         } else {
             if (DEBUG_FOCUS) {
                 ALOGD("Dropping event delivery to target with channel '%s' because it "
@@ -2033,7 +2045,7 @@
 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 ")",
@@ -2044,9 +2056,9 @@
     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);
+          connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset,
+          inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale,
+          inputTarget.windowYScale, inputTarget.pointerIds.value);
 #endif
 
     // Skip this event if the connection status is not normal.
@@ -2060,13 +2072,15 @@
     }
 
     // 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));
 
         const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
-        if (inputTarget->pointerIds.count() != originalMotionEntry.pointerCount) {
+        if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
             MotionEntry* splitMotionEntry =
-                    splitMotionEvent(originalMotionEntry, inputTarget->pointerIds);
+                    splitMotionEvent(originalMotionEntry, inputTarget.pointerIds);
             if (!splitMotionEntry) {
                 return; // split event was dropped
             }
@@ -2088,7 +2102,7 @@
 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
@@ -2121,7 +2135,7 @@
 
 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)",
@@ -2129,7 +2143,7 @@
                                            dispatchModeToString(dispatchMode).c_str());
         ATRACE_NAME(message.c_str());
     }
-    int32_t inputTargetFlags = inputTarget->flags;
+    int32_t inputTargetFlags = inputTarget.flags;
     if (!(inputTargetFlags & dispatchMode)) {
         return;
     }
@@ -2137,11 +2151,11 @@
 
     // 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 =
+            std::make_unique<DispatchEntry>(eventEntry, // increments ref
+                                            inputTargetFlags, inputTarget.xOffset,
+                                            inputTarget.yOffset, inputTarget.globalScaleFactor,
+                                            inputTarget.windowXScale, inputTarget.windowYScale);
 
     // Apply target flags and update the connection's input state.
     switch (eventEntry->type) {
@@ -2156,7 +2170,6 @@
                 ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
                       connection->getInputChannelName().c_str());
 #endif
-                delete dispatchEntry;
                 return; // skip the inconsistent event
             }
             break;
@@ -2203,12 +2216,11 @@
                       "event",
                       connection->getInputChannelName().c_str());
 #endif
-                delete dispatchEntry;
                 return; // skip the inconsistent event
             }
 
             dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,
-                                            inputTarget->inputChannel->getConnectionToken());
+                                            inputTarget.inputChannel->getConnectionToken());
 
             break;
         }
@@ -2226,7 +2238,7 @@
     }
 
     // Enqueue the dispatch entry.
-    connection->outboundQueue.push_back(dispatchEntry);
+    connection->outboundQueue.push_back(dispatchEntry.release());
     traceOutboundQueueLength(connection);
 }
 
@@ -2613,7 +2625,7 @@
             target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
 
             enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
-                                       &target, InputTarget::FLAG_DISPATCH_AS_IS);
+                                       target, InputTarget::FLAG_DISPATCH_AS_IS);
 
             cancelationEventEntry->release();
         }
@@ -3278,9 +3290,9 @@
     // 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<sp<IBinder>, sp<InputWindowHandle>, IBinderHash> oldHandlesByTokens;
+    std::unordered_map<int32_t /*id*/, sp<InputWindowHandle>> oldHandlesById;
     for (const sp<InputWindowHandle>& handle : oldHandles) {
-        oldHandlesByTokens[handle->getToken()] = handle;
+        oldHandlesById[handle->getId()] = handle;
     }
 
     std::vector<sp<InputWindowHandle>> newHandles;
@@ -3311,8 +3323,8 @@
             continue;
         }
 
-        if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) {
-            const sp<InputWindowHandle> oldHandle = oldHandlesByTokens.at(handle->getToken());
+        if (oldHandlesById.find(handle->getId()) != oldHandlesById.end()) {
+            const sp<InputWindowHandle>& oldHandle = oldHandlesById.at(handle->getId());
             oldHandle->updateFrom(handle);
             newHandles.push_back(oldHandle);
         } else {
@@ -3370,7 +3382,7 @@
         sp<InputWindowHandle> oldFocusedWindowHandle =
                 getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
 
-        if (oldFocusedWindowHandle != newFocusedWindowHandle) {
+        if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
             if (oldFocusedWindowHandle != nullptr) {
                 if (DEBUG_FOCUS) {
                     ALOGD("Focus left window: %s in display %" PRId32,
@@ -3781,12 +3793,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),
@@ -3795,11 +3805,10 @@
                                          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",
@@ -4115,7 +4124,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();
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 96a09e3..50b5250 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -168,9 +168,6 @@
     bool isAppSwitchPendingLocked() REQUIRES(mLock);
     void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
 
-    // Stale event latency optimization.
-    static bool isStaleEvent(nsecs_t currentTime, const 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);
@@ -381,13 +378,13 @@
     // 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);
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 2e9bca2..1ba5eff 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -108,7 +108,7 @@
 
     // The subset of pointer ids to include in motion events dispatched to this input target
     // if FLAG_SPLIT is set.
-    BitSet32 pointerIds{};
+    BitSet32 pointerIds;
 };
 
 std::string dispatchModeToString(int32_t dispatchMode);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index f51d4a0..348a7ad 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -183,6 +183,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) {
@@ -331,10 +334,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 && getDevice()->isExternal() && !mParameters.doNotWakeByDefault &&
+        !isMediaKey(keyCode)) {
         policyFlags |= POLICY_FLAG_WAKE;
     }
 
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index de2a377..badbcb2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -73,6 +73,7 @@
     struct Parameters {
         bool orientationAware;
         bool handlesKeyRepeat;
+        bool doNotWakeByDefault;
     } mParameters;
 
     void configureParameters();
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index ada2266..09ecb13 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -4,6 +4,7 @@
     name: "inputflinger_tests",
     srcs: [
         "BlockingQueue_test.cpp",
+        "EventHub_test.cpp",
         "TestInputListener.cpp",
         "InputClassifier_test.cpp",
         "InputClassifierConverter_test.cpp",
@@ -36,4 +37,5 @@
     header_libs: [
         "libinputreader_headers",
     ],
+    require_root: true,
 }
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
new file mode 100644
index 0000000..6504738
--- /dev/null
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -0,0 +1,238 @@
+/*
+ * 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 <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+#include <chrono>
+
+#define TAG "EventHub_test"
+
+using android::EventHub;
+using android::EventHubInterface;
+using android::InputDeviceIdentifier;
+using android::RawEvent;
+using android::sp;
+using android::base::StringPrintf;
+using std::chrono_literals::operator""ms;
+
+static constexpr bool DEBUG = false;
+static const char* DEVICE_NAME = "EventHub Test Device";
+
+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.
+    android::base::unique_fd mDeviceFd;
+    int32_t mDeviceId;
+    virtual void SetUp() override {
+        mEventHub = std::make_unique<EventHub>();
+        consumeInitialDeviceAddedEvents();
+        createDevice();
+        mDeviceId = waitForDeviceCreation();
+    }
+    virtual void TearDown() override {
+        mDeviceFd.reset();
+        waitForDeviceClose(mDeviceId);
+    }
+
+    void createDevice();
+    /**
+     * Return the device id of the created device.
+     */
+    int32_t waitForDeviceCreation();
+    void waitForDeviceClose(int32_t deviceId);
+    void consumeInitialDeviceAddedEvents();
+    void sendEvent(uint16_t type, uint16_t code, int32_t value);
+    std::vector<RawEvent> getEvents(std::chrono::milliseconds timeout = 5ms);
+};
+
+std::vector<RawEvent> EventHubTest::getEvents(std::chrono::milliseconds timeout) {
+    static constexpr size_t EVENT_BUFFER_SIZE = 256;
+    std::array<RawEvent, EVENT_BUFFER_SIZE> eventBuffer;
+    std::vector<RawEvent> events;
+
+    while (true) {
+        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 (DEBUG) {
+        dumpEvents(events);
+    }
+    return events;
+}
+
+void EventHubTest::createDevice() {
+    mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
+    if (mDeviceFd < 0) {
+        FAIL() << "Can't open /dev/uinput :" << strerror(errno);
+    }
+
+    /**
+     * Signal which type of events this input device supports.
+     * We will emulate a keyboard here.
+     */
+    // enable key press/release event
+    if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_KEY)) {
+        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno);
+    }
+
+    // enable set of KEY events
+    if (ioctl(mDeviceFd, UI_SET_KEYBIT, KEY_HOME)) {
+        ADD_FAILURE() << "Error in ioctl : UI_SET_KEYBIT : KEY_HOME: " << strerror(errno);
+    }
+
+    // enable synchronization event
+    if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_SYN)) {
+        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno);
+    }
+
+    struct uinput_user_dev keyboard = {};
+    strlcpy(keyboard.name, DEVICE_NAME, UINPUT_MAX_NAME_SIZE);
+    keyboard.id.bustype = BUS_USB;
+    keyboard.id.vendor = 0x01;
+    keyboard.id.product = 0x01;
+    keyboard.id.version = 1;
+
+    if (write(mDeviceFd, &keyboard, sizeof(keyboard)) < 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);
+    }
+}
+
+/**
+ * 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(0ms);
+    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(20ms);
+    EXPECT_EQ(2U, events.size()); // Using "expect" because the function is non-void.
+    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, DEVICE_NAME);
+    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(20ms);
+    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::sendEvent(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 = StringPrintf("Could not write event %" PRIu16 " %" PRIu16
+                                       " with value %" PRId32 " : %s",
+                                       type, code, value, strerror(errno));
+        ALOGE("%s", msg.c_str());
+        ADD_FAILURE() << msg.c_str();
+    }
+}
+
+/**
+ * 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);
+    // key press
+    sendEvent(EV_KEY, KEY_HOME, 1);
+    sendEvent(EV_SYN, SYN_REPORT, 0);
+
+    // key release
+    sendEvent(EV_KEY, KEY_HOME, 0);
+    sendEvent(EV_SYN, SYN_REPORT, 0);
+
+    std::vector<RawEvent> events = getEvents();
+    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/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index d861a5f..5ffc89d 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -21,6 +21,7 @@
 
 #include <gtest/gtest.h>
 #include <linux/input.h>
+#include <vector>
 
 namespace android::inputdispatcher {
 
@@ -37,6 +38,10 @@
 static const int32_t INJECTOR_PID = 999;
 static const int32_t INJECTOR_UID = 1001;
 
+struct PointF {
+    float x;
+    float y;
+};
 
 // --- FakeInputDispatcherPolicy ---
 
@@ -411,6 +416,11 @@
 
 class FakeInputReceiver {
 public:
+    explicit FakeInputReceiver(const sp<InputChannel>& clientChannel, const std::string name)
+          : mName(name) {
+        mConsumer = std::make_unique<InputConsumer>(clientChannel);
+    }
+
     InputEvent* consume() {
         uint32_t consumeSeq;
         InputEvent* event;
@@ -418,7 +428,7 @@
         std::chrono::time_point start = std::chrono::steady_clock::now();
         status_t status = WOULD_BLOCK;
         while (status == WOULD_BLOCK) {
-            status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, &consumeSeq,
+            status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq,
                                         &event);
             std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
             if (elapsed > 100ms) {
@@ -440,7 +450,7 @@
             return nullptr;
         }
 
-        status = mConsumer->sendFinishedSignal(consumeSeq, handled());
+        status = mConsumer->sendFinishedSignal(consumeSeq, true);
         if (status != OK) {
             ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
         }
@@ -478,6 +488,89 @@
         }
     }
 
+    void assertNoEvents() {
+        InputEvent* event = consume();
+        ASSERT_EQ(nullptr, event)
+                << mName.c_str()
+                << ": should not have received any events, so consume() should return NULL";
+    }
+
+    sp<IBinder> getToken() { return mConsumer->getChannel()->getConnectionToken(); }
+
+protected:
+    std::unique_ptr<InputConsumer> mConsumer;
+    PreallocatedInputEventFactory mEventFactory;
+
+    std::string mName;
+};
+
+class FakeWindowHandle : public InputWindowHandle {
+public:
+    static const int32_t WIDTH = 600;
+    static const int32_t HEIGHT = 800;
+    const std::string mName;
+
+    FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+                     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();
+
+        mInfo.token = token;
+        mInfo.id = 0;
+        mInfo.name = name;
+        mInfo.layoutParamsFlags = 0;
+        mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+        mInfo.frameLeft = 0;
+        mInfo.frameTop = 0;
+        mInfo.frameRight = WIDTH;
+        mInfo.frameBottom = HEIGHT;
+        mInfo.globalScaleFactor = 1.0;
+        mInfo.touchableRegion.clear();
+        mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+        mInfo.visible = true;
+        mInfo.canReceiveKeys = true;
+        mInfo.hasFocus = false;
+        mInfo.hasWallpaper = false;
+        mInfo.paused = false;
+        mInfo.ownerPid = INJECTOR_PID;
+        mInfo.ownerUid = INJECTOR_UID;
+        mInfo.inputFeatures = 0;
+        mInfo.displayId = displayId;
+    }
+
+    virtual bool updateInfo() { return true; }
+
+    void setFocus() { mInfo.hasFocus = true; }
+
+    void setFrame(const Rect& 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) { mInfo.layoutParamsFlags = flags; }
+
+    void setId(int32_t id) { mInfo.id = id; }
+
+    void setWindowScale(float xScale, float yScale) {
+        mInfo.windowXScale = xScale;
+        mInfo.windowYScale = yScale;
+    }
+
     void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
         consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
                      expectedFlags);
@@ -488,102 +581,35 @@
                      expectedFlags);
     }
 
+    void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId,
+                     expectedFlags);
+    }
+
+    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);
+    }
+
+    InputEvent* consume() {
+        if (mInputReceiver == nullptr) {
+            return nullptr;
+        }
+        return mInputReceiver->consume();
+    }
+
     void assertNoEvents() {
-        InputEvent* event = consume();
-        ASSERT_EQ(nullptr, event)
-                << mName.c_str()
-                << ": should not have received any events, so consume() should return NULL";
+        ASSERT_NE(mInputReceiver, nullptr)
+                << "Call 'assertNoEvents' on a window with an InputReceiver";
+        mInputReceiver->assertNoEvents();
     }
 
-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);
-        }
+    sp<IBinder> getToken() { return mInfo.token; }
 
-        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;
-};
-
-class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
-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) {
-            mDispatcher->registerInputChannel(mServerChannel);
-
-            inputApplicationHandle->updateInfo();
-            mInfo.applicationInfo = *inputApplicationHandle->getInfo();
-    }
-
-    virtual bool updateInfo() {
-        mInfo.token = mServerChannel ? mServerChannel->getConnectionToken() : nullptr;
-        mInfo.name = mName;
-        mInfo.layoutParamsFlags = mLayoutParamFlags;
-        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.globalScaleFactor = 1.0;
-        mInfo.touchableRegion.clear();
-        mInfo.addTouchableRegion(mFrame);
-        mInfo.visible = true;
-        mInfo.canReceiveKeys = true;
-        mInfo.hasFocus = mFocused;
-        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;
-    }
-
-    void setFocus() {
-        mFocused = true;
-    }
-
-    void setFrame(const Rect& frame) {
-        mFrame.set(frame);
-    }
-
-    void setLayoutParamFlags(int32_t flags) {
-        mLayoutParamFlags = flags;
-    }
-
-    void releaseChannel() {
-        mServerChannel.clear();
-        InputWindowHandle::releaseChannel();
-    }
-protected:
-    virtual bool handled() override { return true; }
-
-    bool mFocused;
-    Rect mFrame;
-    int32_t mLayoutParamFlags;
+private:
+    std::unique_ptr<FakeInputReceiver> mInputReceiver;
 };
 
 static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
@@ -639,6 +665,11 @@
     return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, x, y);
 }
 
+static int32_t injectMotionUp(const sp<InputDispatcher>& dispatcher, int32_t source,
+                              int32_t displayId, int32_t x = 100, int32_t y = 200) {
+    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, x, y);
+}
+
 static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid key event.
@@ -649,31 +680,43 @@
     return args;
 }
 
-static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
-    PointerProperties pointerProperties[1];
-    PointerCoords pointerCoords[1];
+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[0].clear();
-    pointerProperties[0].id = 0;
-    pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+    PointerProperties pointerProperties[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
 
-    pointerCoords[0].clear();
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+    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(/* 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, 1, pointerProperties, pointerCoords,
-                          /* xPrecision */ 0, /* yPrecision */ 0,
+                          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) {
+    return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
+}
+
 TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
     sp<FakeApplicationHandle> application = new FakeApplicationHandle();
     sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
@@ -847,6 +890,126 @@
                          0 /*expectedFlags*/);
 }
 
+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);
+    }
+
+    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({window}, ADISPLAY_ID_DEFAULT);
+
+    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();
+
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    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({window}, ADISPLAY_ID_DEFAULT);
+
+    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, TestMoveEvent) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    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.sequenceNum += 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*/);
+}
+
 /* Test InputDispatcher for MultiDisplay */
 class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
 public:
@@ -934,39 +1097,30 @@
     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) {
-        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->consumeMotionDown(ADISPLAY_ID_DEFAULT);
-    monitorInPrimary->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();
+    monitorInPrimary.assertNoEvents();
     windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
-    monitorInSecondary->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,
@@ -975,26 +1129,26 @@
         AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     windowInPrimary->assertNoEvents();
-    monitorInPrimary->assertNoEvents();
+    monitorInPrimary.assertNoEvents();
     windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
-    monitorInSecondary->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();
+    monitorInPrimary.assertNoEvents();
     windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
-    monitorInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
+    monitorInSecondary.consumeKeyDown(ADISPLAY_ID_NONE);
 }
 
 class InputFilterTest : public InputDispatcherTest {
@@ -1157,4 +1311,132 @@
     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->setId(0);
+        mWindow1->setFrame(Rect(0, 0, 100, 100));
+
+        mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2",
+                                        ADISPLAY_ID_DEFAULT, mWindow1->getToken());
+        mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
+                                      InputWindowInfo::FLAG_SPLIT_TOUCH);
+        mWindow2->setId(1);
+        mWindow2->setFrame(Rect(100, 100, 200, 200));
+
+        mDispatcher->setInputWindows({mWindow1, mWindow2}, ADISPLAY_ID_DEFAULT);
+    }
+
+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) {
+        std::string name = window->mName;
+        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});
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index d6624c9..1fc8217 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2580,6 +2580,84 @@
                                                 AKEYCODE_DPAD_LEFT, newDisplayId));
 }
 
+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.
+    mDevice->setExternal(true);
+
+    mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, 0);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
+
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD,
+                                                          AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    addMapperAndConfigure(mapper);
+
+    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.
+    mDevice->setExternal(true);
+
+    mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE);
+
+    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD,
+                                                          AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    addConfigurationProperty("keyboard.doNotWakeByDefault", "1");
+    addMapperAndConfigure(mapper);
+
+    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 ---
 
 class CursorInputMapperTest : public InputMapperTest {
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 054acc5..bdecdb7 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -770,17 +770,20 @@
 
     // 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.
-    // Therefore, temporarily store the relatives so they can be set in the cloned drawingState
-    // again.
+    // 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;
-    // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple
-    // InputWindows per client token yet.
-    mDrawingState.inputInfo.token = nullptr;
+
+    mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
     mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
     mDrawingState.zOrderRelatives = tmpZOrderRelatives;
+    mDrawingState.inputInfo = tmpInputInfo;
 }
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 16855d2..5f5532e 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -190,7 +190,7 @@
 
     static bool getOpacityForFormat(uint32_t format);
 
-    // from GLES
+    // from graphics API
     const uint32_t mTextureName;
 
     bool mRefreshPending{false};
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 945abd7..e4e4bc7 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -455,6 +455,10 @@
         mLastFrameNumberReceived = item.mFrameNumber;
         mQueueItemCondition.broadcast();
     }
+
+    const int32_t layerId = getSequence();
+    mFlinger->mFrameTracer->traceTimestamp(layerId, item.mGraphicBuffer->getId(), item.mFrameNumber,
+                                           systemTime(), FrameTracer::FrameEvent::QUEUE);
     mConsumer->onBufferAvailable(item);
 }
 
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/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 0a70165..e432b1c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <optional>
+#include <ostream>
 #include <unordered_set>
 
 #include <renderengine/LayerSettings.h>
@@ -115,5 +116,29 @@
 
 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);
+}
+
+// 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}";
+}
+
 } // namespace compositionengine
 } // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 2ba781d..a64fdbf 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -79,6 +79,9 @@
     // The bounds of the layer in layer local coordinates
     FloatRect geomLayerBounds;
 
+    // length of the shadow in screen space
+    float shadowRadius;
+
     /*
      * Geometry state
      */
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 11cfccc..00baa89 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -52,6 +52,9 @@
     // 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};
 
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 1ca03dc..e740529 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -51,6 +51,9 @@
     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);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 7e5a720..28d2653 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -408,6 +408,11 @@
      */
     Region transparentRegion;
 
+    /*
+     * shadowRegion: Region cast by the layer's shadow.
+     */
+    Region shadowRegion;
+
     // handle hidden surfaces by setting the visible region to empty
     if (CC_UNLIKELY(!layerFEState.isVisible)) {
         return;
@@ -418,7 +423,18 @@
     // Get the visible region
     // TODO(b/121291683): Is it worth creating helper methods on LayerFEState
     // for computations like this?
-    visibleRegion.set(Rect(tr.transform(layerFEState.geomLayerBounds)));
+    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 int32_t inset = layerFEState.shadowRadius * -1.0f;
+        Rect visibleRectWithShadows(visibleRect);
+        visibleRectWithShadows.inset(inset, inset, inset, inset);
+        visibleRegion.set(visibleRectWithShadows);
+        shadowRegion = visibleRegion.subtract(visibleRect);
+    }
 
     if (visibleRegion.isEmpty()) {
         return;
@@ -444,7 +460,7 @@
         // 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 = visibleRegion;
+        opaqueRegion.set(visibleRect);
     }
 
     // Clip the covered region to the visible region
@@ -510,7 +526,7 @@
     // Compute the visible non-transparent region
     Region visibleNonTransparentRegion = visibleRegion.subtract(transparentRegion);
 
-    // Peform the final check to see if this layer is visible on this output
+    // 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));
@@ -519,6 +535,8 @@
         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, layer, layerFE);
@@ -529,8 +547,9 @@
     outputLayerState.visibleRegion = visibleRegion;
     outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
     outputLayerState.coveredRegion = coveredRegion;
-    outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform(
-            outputLayerState.visibleRegion.intersect(outputState.viewport));
+    outputLayerState.outputSpaceVisibleRegion =
+            outputState.transform.transform(visibleNonShadowRegion.intersect(outputState.viewport));
+    outputLayerState.shadowRegion = shadowRegion;
 }
 
 void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
@@ -884,7 +903,7 @@
             continue;
         }
 
-        bool clientComposition = layer->requiresClientComposition();
+        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
@@ -921,8 +940,15 @@
                     }
                 }
 
-                layer->editState().clientCompositionTimestamp = systemTime();
-                clientCompositionLayers.push_back(*result);
+                // 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 skipNonShadowContentComposition = clientComposition &&
+                        layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty();
+
+                if (!skipNonShadowContentComposition) {
+                    layer->editState().clientCompositionTimestamp = systemTime();
+                    clientCompositionLayers.push_back(*result);
+                }
             }
         }
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index ce0222c..82d2422 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -231,7 +231,7 @@
      * (NOTE: the matrices are multiplied in reverse order)
      */
     const ui::Transform& layerTransform = layerState.geomLayerTransform;
-    const ui::Transform displayTransform{outputState.orientation};
+    const ui::Transform displayTransform{outputState.transform};
     const ui::Transform bufferTransform{layerState.geomBufferTransform};
     ui::Transform transform(displayTransform * layerTransform * bufferTransform);
 
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index ad668b6..cc3c54c 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -51,6 +51,9 @@
     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);
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 5ed21fc..949feb4 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -117,12 +117,12 @@
     if (usesClientComposition && usesDeviceComposition) {
         compositionType = DisplaySurface::COMPOSITION_MIXED;
     } else if (usesClientComposition) {
-        compositionType = DisplaySurface::COMPOSITION_GLES;
+        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;
     }
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 364661b..9971791 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -86,6 +86,9 @@
     MOCK_METHOD4(setActiveConfigWithConstraints,
                  status_t(DisplayId, size_t, const HWC2::VsyncPeriodChangeConstraints&,
                           HWC2::VsyncPeriodChangeTimeline*));
+    MOCK_METHOD2(setAutoLowLatencyMode, status_t(DisplayId, bool));
+    MOCK_METHOD2(getSupportedContentTypes, status_t(DisplayId, std::vector<HWC2::ContentType>*));
+    MOCK_METHOD2(setContentType, status_t(DisplayId, HWC2::ContentType));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 2e030a1..0e579fa 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -364,6 +364,7 @@
         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();
         EXPECT_EQ(entry.expected, actual) << "entry " << i;
@@ -422,6 +423,7 @@
         mLayerFEState.geomLayerTransform = ui::Transform{entry.layer};
         mLayerFEState.geomBufferTransform = entry.buffer;
         mOutputState.orientation = entry.display;
+        mOutputState.transform = ui::Transform{entry.display};
 
         auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
         EXPECT_EQ(entry.expected, actual) << "entry " << i;
@@ -764,6 +766,21 @@
     mOutputLayer.writeStateToHWC(true);
 }
 
+TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
+    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).
+    mOutputState.orientation = TR_IDENT;
+    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.
+    mLayerFEState.geomBufferTransform = TR_ROT_270;
+
+    EXPECT_EQ(TR_IDENT, mOutputLayer.calculateOutputRelativeBufferTransform());
+}
+
 TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSolidColor) {
     mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 08b8818..cb6c50c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -41,9 +41,13 @@
 
 using testing::_;
 using testing::ByMove;
+using testing::ByRef;
 using testing::DoAll;
+using testing::ElementsAreArray;
 using testing::Eq;
 using testing::InSequence;
+using testing::Invoke;
+using testing::IsEmpty;
 using testing::Mock;
 using testing::Property;
 using testing::Ref;
@@ -1435,6 +1439,87 @@
     EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kExpectedLayerVisibleRegion));
 }
 
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesWithShadowsTest) {
+    ui::Transform translate;
+    translate.set(50, 50);
+    mLayerFEState.geomLayerTransform = translate;
+    mLayerFEState.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), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    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(mOutputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+    EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+                RegionEq(kExpectedLayerVisibleNonTransparentRegion));
+    EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion));
+    EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion,
+                RegionEq(kExpectedoutputSpaceLayerVisibleRegion));
+    EXPECT_THAT(mOutputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
+    EXPECT_FALSE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty());
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, shadowRegionOnlyTest) {
+    ui::Transform translate;
+    translate.set(50, 50);
+    mLayerFEState.geomLayerTransform = translate;
+    mLayerFEState.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), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    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(mOutputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+    EXPECT_THAT(mOutputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
+    EXPECT_TRUE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty());
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifLayerWithShadowIsCovered) {
+    ui::Transform translate;
+    translate.set(50, 50);
+    mLayerFEState.geomLayerTransform = translate;
+    mLayerFEState.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));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
 /*
  * Output::present()
  */
@@ -2438,8 +2523,6 @@
     mOutput.devOptRepaintFlash(mRefreshArgs);
 }
 
-// TODO(b/144060211) - Add coverage
-
 /*
  * Output::finishFrame()
  */
@@ -2667,13 +2750,7 @@
  */
 
 struct OutputComposeSurfacesTest : public testing::Test {
-    static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT;
-    static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::DISPLAY_P3;
-
-    static const Rect kDefaultOutputFrame;
-    static const Rect kDefaultOutputViewport;
-    static const Rect kDefaultOutputScissor;
-    static const mat4 kDefaultColorTransformMat;
+    using TestType = OutputComposeSurfacesTest;
 
     struct OutputPartialMock : public OutputPartialMockBase {
         // Sets up the helper functions called by the function under test to use
@@ -2691,84 +2768,404 @@
                 std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
         mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
 
-        mOutput.editState().frame = kDefaultOutputFrame;
-        mOutput.editState().viewport = kDefaultOutputViewport;
-        mOutput.editState().scissor = kDefaultOutputScissor;
-        mOutput.editState().transform = ui::Transform{kDefaultOutputOrientation};
-        mOutput.editState().orientation = kDefaultOutputOrientation;
-        mOutput.editState().dataspace = kDefaultOutputDataspace;
-        mOutput.editState().colorTransformMatrix = kDefaultColorTransformMat;
-        mOutput.editState().isSecure = true;
-        mOutput.editState().needsFiltering = false;
-        mOutput.editState().usesClientComposition = true;
-        mOutput.editState().usesDeviceComposition = false;
+        mOutput.mState.frame = kDefaultOutputFrame;
+        mOutput.mState.viewport = kDefaultOutputViewport;
+        mOutput.mState.scissor = kDefaultOutputScissor;
+        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;
 
-        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
-        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
-                .WillRepeatedly(Return(&mOutputLayer1));
-        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
-                .WillRepeatedly(Return(&mOutputLayer2));
         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);
+            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 kDefaultOutputScissor;
+    static const mat4 kDefaultColorTransformMat;
+
+    static const Region kDebugRegion;
+    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<mock::OutputLayer> mOutputLayer1;
-    StrictMock<mock::OutputLayer> mOutputLayer2;
     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::kDefaultOutputScissor{1009, 1010, 1011, 1012};
 const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5};
-
-// TODO(b/121291683): Expand unit test coverage for composeSurfaces beyond these
-// basic tests.
+const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
+const HdrCapabilities OutputComposeSurfacesTest::
+        kHdrCapabilities{{},
+                         OutputComposeSurfacesTest::kDefaultMaxLuminance,
+                         OutputComposeSurfacesTest::kDefaultAvgLuminance,
+                         OutputComposeSurfacesTest::kDefaultMinLuminance};
 
 TEST_F(OutputComposeSurfacesTest, doesNothingIfNoClientComposition) {
-    mOutput.editState().usesClientComposition = false;
+    mOutput.mState.usesClientComposition = false;
 
-    Region debugRegion;
-    std::optional<base::unique_fd> readyFence = mOutput.composeSurfaces(debugRegion);
-    EXPECT_TRUE(readyFence);
+    verify().execute().expectAFenceWasReturned();
 }
 
-TEST_F(OutputComposeSurfacesTest, worksIfNoClientLayersQueued) {
-    const Region kDebugRegion{Rect{100, 101, 102, 103}};
+TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFails) {
+    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<renderengine::LayerSettings>{}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(Return());
 
-    constexpr float kDefaultMaxLuminance = 1.0f;
-    constexpr float kDefaultAvgLuminance = 0.7f;
-    constexpr float kDefaultMinLuminance = 0.1f;
-    HdrCapabilities HdrCapabilities{{},
-                                    kDefaultMaxLuminance,
-                                    kDefaultAvgLuminance,
-                                    kDefaultMinLuminance};
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
 
-    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillOnce(Return(false));
-    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).Times(1);
+    verify().execute().expectNoFenceWasReturned();
+}
 
-    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillOnce(Return(true));
-    EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities()).WillOnce(ReturnRef(HdrCapabilities));
+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<renderengine::LayerSettings>{}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(Return());
 
-    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer));
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, true, _, _))
+            .WillRepeatedly(Return(NO_ERROR));
 
-    EXPECT_CALL(mOutput, getSkipColorTransform()).WillOnce(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(false, _, _)).Times(1);
-    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)).Times(1);
-    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(1);
-    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)).Times(1);
+    verify().execute().expectAFenceWasReturned();
+}
 
-    std::optional<base::unique_fd> readyFence = mOutput.composeSurfaces(kDebugRegion);
-    EXPECT_TRUE(readyFence);
+TEST_F(OutputComposeSurfacesTest, buildsAndRendersRequestList) {
+    renderengine::LayerSettings r1;
+    renderengine::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<renderengine::LayerSettings>{r1}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(
+                    Invoke([&](const Region&,
+                               std::vector<renderengine::LayerSettings>& clientCompositionLayers) {
+                        clientCompositionLayers.emplace_back(r2);
+                    }));
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAreArray({r1, r2}), _, true, _, _))
+            .WillRepeatedly(Return(NO_ERROR));
+
+    verify().execute().expectAFenceWasReturned();
+}
+
+struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComposeSurfacesTest {
+    OutputComposeSurfacesTest_UsesExpectedDisplaySettings() {
+        EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+                .WillRepeatedly(Return(std::vector<renderengine::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({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+                                            kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
+                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComposition) {
+    verify().ifMixedCompositionIs(true)
+            .andIfUsesHdr(false)
+            .andIfSkipColorTransform(false)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+                                            kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
+                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientComposition) {
+    verify().ifMixedCompositionIs(false)
+            .andIfUsesHdr(true)
+            .andIfSkipColorTransform(false)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+                                            kDefaultMaxLuminance, kDefaultOutputDataspace,
+                                            kDefaultColorTransformMat, Region::INVALID_REGION,
+                                            kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClientComposition) {
+    verify().ifMixedCompositionIs(false)
+            .andIfUsesHdr(false)
+            .andIfSkipColorTransform(false)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+                                            kDefaultMaxLuminance, kDefaultOutputDataspace,
+                                            kDefaultColorTransformMat, Region::INVALID_REGION,
+                                            kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings,
+       usesExpectedDisplaySettingsForHdrOnlyClientCompositionWithSkipClientTransform) {
+    verify().ifMixedCompositionIs(false)
+            .andIfUsesHdr(true)
+            .andIfSkipColorTransform(true)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+                                            kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
+                                            Region::INVALID_REGION, kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeSurfacesTest {
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(mLayer));
+            EXPECT_CALL(mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+        }
+
+        StrictMock<mock::OutputLayer> mOutputLayer;
+        StrictMock<mock::Layer> mLayer;
+        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<renderengine::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);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+
+    mOutput.composeSurfaces(kDebugRegion);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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<renderengine::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));
+    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
+
+    mOutput.composeSurfaces(kDebugRegion);
 }
 
 /*
@@ -2786,7 +3183,26 @@
         }
     };
 
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+            EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+            EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(mLayer));
+            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
+            EXPECT_CALL(mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+        }
+
+        StrictMock<mock::OutputLayer> mOutputLayer;
+        StrictMock<mock::Layer> mLayer;
+        StrictMock<mock::LayerFE> mLayerFE;
+        LayerFECompositionState mLayerFEState;
+        impl::OutputLayerCompositionState mOutputLayerState;
+        renderengine::LayerSettings mRELayerSettings;
+    };
+
     GenerateClientCompositionRequestsTest() {
+        mOutput.mState.needsFiltering = false;
+
         mOutput.setDisplayColorProfileForTest(
                 std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
         mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
@@ -2797,222 +3213,569 @@
     StrictMock<OutputPartialMock> mOutput;
 };
 
-// TODO(b/121291683): Add more unit test coverage for generateClientCompositionRequests
+struct GenerateClientCompositionRequestsTest_ThreeLayers
+      : public GenerateClientCompositionRequestsTest {
+    GenerateClientCompositionRequestsTest_ThreeLayers() {
+        mOutput.mState.frame = kDisplayFrame;
+        mOutput.mState.viewport = kDisplayViewport;
+        mOutput.mState.scissor = kDisplayScissor;
+        mOutput.mState.transform = ui::Transform{kDisplayOrientation};
+        mOutput.mState.orientation = kDisplayOrientation;
+        mOutput.mState.needsFiltering = false;
+        mOutput.mState.isSecure = false;
 
-TEST_F(GenerateClientCompositionRequestsTest, worksForLandscapeModeSplitScreen) {
-    // 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.
+        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].mRELayerSettings.geometry.boundaries =
+                    FloatRect{static_cast<float>(i + 1), 0.f, 0.f, 0.f};
+            mLayers[i].mRELayerSettings.source.solidColor = {1.0f, 1.0f, 1.0f};
+            mLayers[i].mRELayerSettings.alpha = 1.0f;
+            mLayers[i].mRELayerSettings.disableBlending = false;
 
-    StrictMock<mock::OutputLayer> leftOutputLayer;
-    StrictMock<mock::OutputLayer> rightOutputLayer;
+            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));
+        }
 
-    StrictMock<mock::Layer> leftLayer;
-    StrictMock<mock::LayerFE> leftLayerFE;
-    StrictMock<mock::Layer> rightLayer;
-    StrictMock<mock::LayerFE> rightLayerFE;
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(mLayers.size()));
+    }
 
-    impl::OutputLayerCompositionState leftOutputLayerState;
-    leftOutputLayerState.clearClientTarget = false;
-    leftOutputLayerState.visibleRegion = Region{Rect{0, 0, 1000, 1000}};
+    static constexpr uint32_t kDisplayOrientation = TR_IDENT;
+    static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN;
 
-    LayerFECompositionState leftLayerFEState;
-    leftLayerFEState.isOpaque = true;
+    static const Rect kDisplayFrame;
+    static const Rect kDisplayViewport;
+    static const Rect kDisplayScissor;
 
-    const half3 leftLayerColor{1.f, 0.f, 0.f};
-    renderengine::LayerSettings leftLayerRESettings;
-    leftLayerRESettings.source.solidColor = leftLayerColor;
+    std::array<Layer, 3> mLayers;
+};
 
-    impl::OutputLayerCompositionState rightOutputLayerState;
-    rightOutputLayerState.clearClientTarget = false;
-    rightOutputLayerState.visibleRegion = Region{Rect{1000, 0, 2000, 1000}};
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayFrame(0, 0, 100, 200);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayViewport(0, 0, 101, 201);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayScissor(0, 0, 102, 202);
 
-    LayerFECompositionState rightLayerFEState;
-    rightLayerFEState.isOpaque = true;
+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));
 
-    const half3 rightLayerColor{0.f, 1.f, 0.f};
-    renderengine::LayerSettings rightLayerRESettings;
-    rightLayerRESettings.source.solidColor = rightLayerColor;
-
-    EXPECT_CALL(leftOutputLayer, getState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
-    EXPECT_CALL(leftOutputLayer, getLayer()).WillRepeatedly(ReturnRef(leftLayer));
-    EXPECT_CALL(leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE));
-    EXPECT_CALL(leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
-    EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
-    EXPECT_CALL(leftLayerFE, prepareClientComposition(_)).WillOnce(Return(leftLayerRESettings));
-    EXPECT_CALL(leftLayerFE, prepareShadowClientComposition(_, _, _))
-            .WillOnce(Return(std::optional<renderengine::LayerSettings>()));
-    EXPECT_CALL(leftOutputLayer, editState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
-
-    EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
-    EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
-    EXPECT_CALL(rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE));
-    EXPECT_CALL(rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
-    EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
-    EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
-    EXPECT_CALL(rightLayerFE, prepareShadowClientComposition(_, _, _))
-            .WillOnce(Return(std::optional<renderengine::LayerSettings>()));
-    EXPECT_CALL(rightOutputLayer, editState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
-
-    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
-    EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
-            .WillRepeatedly(Return(&leftOutputLayer));
-    EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
-            .WillRepeatedly(Return(&rightOutputLayer));
-
-    const Rect kPortraitFrame(0, 0, 1000, 2000);
-    const Rect kPortraitViewport(0, 0, 2000, 1000);
-    const Rect kPortraitScissor(0, 0, 1000, 2000);
-    const uint32_t kPortraitOrientation = TR_ROT_90;
-
-    mOutput.editState().frame = kPortraitFrame;
-    mOutput.editState().viewport = kPortraitViewport;
-    mOutput.editState().scissor = kPortraitScissor;
-    mOutput.editState().transform = ui::Transform{kPortraitOrientation};
-    mOutput.editState().orientation = kPortraitOrientation;
-    mOutput.editState().needsFiltering = true;
-    mOutput.editState().isSecure = false;
-
-    constexpr bool supportsProtectedContent = false;
-    Region clearRegion;
-    auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
-                                                              mOutput.getState().targetDataspace);
-
-    ASSERT_EQ(2u, requests.size());
-    EXPECT_EQ(leftLayerColor, requests[0].source.solidColor);
-    EXPECT_EQ(rightLayerColor, requests[1].source.solidColor);
-}
-
-TEST_F(GenerateClientCompositionRequestsTest, ignoresLayersThatDoNotIntersectWithViewport) {
-    // Layers whose visible region does not intersect with the viewport will be
-    // skipped when generating client composition request state.
-
-    StrictMock<mock::OutputLayer> outputLayer;
-    StrictMock<mock::Layer> layer;
-    StrictMock<mock::LayerFE> layerFE;
-
-    impl::OutputLayerCompositionState outputLayerState;
-    outputLayerState.clearClientTarget = false;
-    outputLayerState.visibleRegion = Region{Rect{3000, 0, 4000, 1000}};
-
-    LayerFECompositionState layerFEState;
-    layerFEState.isOpaque = true;
-
-    EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
-    EXPECT_CALL(outputLayer, getLayer()).WillRepeatedly(ReturnRef(layer));
-    EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
-    EXPECT_CALL(outputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
-    EXPECT_CALL(outputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(layer, getFEState()).WillRepeatedly(ReturnRef(layerFEState));
-    EXPECT_CALL(layerFE, prepareClientComposition(_)).Times(0);
-    EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
-
-    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
-    EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)).WillRepeatedly(Return(&outputLayer));
-
-    const Rect kPortraitFrame(0, 0, 1000, 2000);
-    const Rect kPortraitViewport(0, 0, 2000, 1000);
-    const Rect kPortraitScissor(0, 0, 1000, 2000);
-    const uint32_t kPortraitOrientation = TR_ROT_90;
-
-    mOutput.editState().frame = kPortraitFrame;
-    mOutput.editState().viewport = kPortraitViewport;
-    mOutput.editState().scissor = kPortraitScissor;
-    mOutput.editState().transform = ui::Transform{kPortraitOrientation};
-    mOutput.editState().orientation = kPortraitOrientation;
-    mOutput.editState().needsFiltering = true;
-    mOutput.editState().isSecure = false;
-
-    constexpr bool supportsProtectedContent = false;
-    Region clearRegion;
-    auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
-                                                              mOutput.getState().targetDataspace);
-
+    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, clearsDeviceLayesAfterFirst) {
+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) {
+    renderengine::LayerSettings mREShadowSettings;
+    mREShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(_)).WillOnce(Return(std::nullopt));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(mLayers[1].mRELayerSettings));
+    EXPECT_CALL(mLayers[1].mLayerFE,
+                prepareShadowClientComposition(mLayers[1].mRELayerSettings, kDisplayViewport,
+                                               kDisplayDataspace))
+            .WillOnce(Return(std::nullopt));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(mLayers[2].mRELayerSettings));
+    EXPECT_CALL(mLayers[2].mLayerFE,
+                prepareShadowClientComposition(mLayers[2].mRELayerSettings, kDisplayViewport,
+                                               kDisplayDataspace))
+            .WillOnce(Return(mREShadowSettings));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(3u, requests.size());
+    EXPECT_EQ(mLayers[1].mRELayerSettings, requests[0]);
+    EXPECT_EQ(mREShadowSettings, requests[1]);
+    EXPECT_EQ(mLayers[2].mRELayerSettings, 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, prepareClientComposition(_))
+            .WillOnce(Return(mLayers[2].mRELayerSettings));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareShadowClientComposition(_, _, _))
+            .WillOnce(Return(std::nullopt));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(1u, requests.size());
+    EXPECT_EQ(mLayers[2].mRELayerSettings, 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, prepareClientComposition(_))
+            .WillOnce(Return(mLayers[2].mRELayerSettings));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareShadowClientComposition(_, _, _))
+            .WillOnce(Return(std::nullopt));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(1u, requests.size());
+    EXPECT_EQ(mLayers[2].mRELayerSettings, 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.
 
-    StrictMock<mock::OutputLayer> leftOutputLayer;
-    StrictMock<mock::OutputLayer> rightOutputLayer;
+    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));
 
-    StrictMock<mock::Layer> leftLayer;
-    StrictMock<mock::LayerFE> leftLayerFE;
-    StrictMock<mock::Layer> rightLayer;
-    StrictMock<mock::LayerFE> rightLayerFE;
+    mLayers[0].mOutputLayerState.clearClientTarget = true;
+    mLayers[1].mOutputLayerState.clearClientTarget = true;
+    mLayers[2].mOutputLayerState.clearClientTarget = true;
 
-    impl::OutputLayerCompositionState leftOutputLayerState;
-    leftOutputLayerState.clearClientTarget = true;
-    leftOutputLayerState.visibleRegion = Region{Rect{0, 0, 1000, 1000}};
+    mLayers[0].mLayerFEState.isOpaque = true;
+    mLayers[1].mLayerFEState.isOpaque = true;
+    mLayers[2].mLayerFEState.isOpaque = true;
 
-    LayerFECompositionState leftLayerFEState;
-    leftLayerFEState.isOpaque = true;
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(mLayers[1].mRELayerSettings));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+            .WillOnce(Return(mLayers[2].mRELayerSettings));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareShadowClientComposition(_, _, _))
+            .WillOnce(Return(std::nullopt));
 
-    impl::OutputLayerCompositionState rightOutputLayerState;
-    rightOutputLayerState.clearClientTarget = true;
-    rightOutputLayerState.visibleRegion = Region{Rect{1000, 0, 2000, 1000}};
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(2u, requests.size());
 
-    LayerFECompositionState rightLayerFEState;
-    rightLayerFEState.isOpaque = true;
+    // The second layer is expected to be rendered as alpha=0 black with no blending
+    EXPECT_EQ(mLayers[1].mRELayerSettings.geometry.boundaries, requests[0].geometry.boundaries);
+    EXPECT_FALSE(requests[0].source.buffer.buffer);
+    EXPECT_EQ((half3{0.f, 0.f, 0.f}), requests[0].source.solidColor);
+    EXPECT_EQ(half{0.f}, requests[0].alpha);
+    EXPECT_EQ(true, requests[0].disableBlending);
 
-    const half3 rightLayerColor{0.f, 1.f, 0.f};
-    renderengine::LayerSettings rightLayerRESettings;
-    rightLayerRESettings.geometry.boundaries = FloatRect{456, 0, 0, 0};
-    rightLayerRESettings.source.solidColor = rightLayerColor;
+    EXPECT_EQ(mLayers[2].mRELayerSettings, requests[1]);
 
-    EXPECT_CALL(leftOutputLayer, getState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
-    EXPECT_CALL(leftOutputLayer, getLayer()).WillRepeatedly(ReturnRef(leftLayer));
-    EXPECT_CALL(leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE));
-    EXPECT_CALL(leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
-    EXPECT_CALL(leftOutputLayer, editState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
 
-    EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
-    EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
-    EXPECT_CALL(rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE));
-    EXPECT_CALL(rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
-    EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
-    EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
-    EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
-    EXPECT_CALL(rightOutputLayer, editState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
+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));
 
-    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
-    EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
-            .WillRepeatedly(Return(&leftOutputLayer));
-    EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
-            .WillRepeatedly(Return(&rightOutputLayer));
+    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,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(Rect(0, 0, 30, 30)),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(Rect(0, 0, 40, 201)),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+
+    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,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+
+    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,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            true,  /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            true,  /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+
+    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,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            true,  /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            true,  /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+
+    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,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            true,  /* supports protected content */
+            accumClearRegion,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            true,  /* supports protected content */
+            accumClearRegion,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::nullopt));
+
+    static_cast<void>(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */,
+                                                                accumClearRegion,
+                                                                kDisplayDataspace));
+}
+
+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 kPortraitScissor(0, 0, 1000, 2000);
     const uint32_t kPortraitOrientation = TR_ROT_90;
+    constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
 
-    mOutput.editState().frame = kPortraitFrame;
-    mOutput.editState().viewport = kPortraitViewport;
-    mOutput.editState().scissor = kPortraitScissor;
-    mOutput.editState().transform = ui::Transform{kPortraitOrientation};
-    mOutput.editState().orientation = kPortraitOrientation;
-    mOutput.editState().needsFiltering = true;
-    mOutput.editState().isSecure = false;
+    mOutput.mState.frame = kPortraitFrame;
+    mOutput.mState.viewport = kPortraitViewport;
+    mOutput.mState.scissor = kPortraitScissor;
+    mOutput.mState.transform = ui::Transform{kPortraitOrientation};
+    mOutput.mState.orientation = kPortraitOrientation;
+    mOutput.mState.needsFiltering = false;
+    mOutput.mState.isSecure = true;
 
-    constexpr bool supportsProtectedContent = false;
-    Region clearRegion;
-    auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
-                                                              mOutput.getState().targetDataspace);
+    Layer leftLayer;
+    Layer rightLayer;
 
-    const half3 clearColor{0.f, 0.f, 0.f};
+    leftLayer.mOutputLayerState.clearClientTarget = false;
+    leftLayer.mOutputLayerState.visibleRegion = Region(Rect(0, 0, 1000, 1000));
+    leftLayer.mLayerFEState.isOpaque = true;
+    leftLayer.mRELayerSettings.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.mRELayerSettings.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,
+    };
+
+    EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+    EXPECT_CALL(leftLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+    EXPECT_CALL(leftLayer.mLayerFE, prepareClientComposition(Eq(ByRef(leftLayerSettings))))
+            .WillOnce(Return(leftLayer.mRELayerSettings));
+    EXPECT_CALL(leftLayer.mLayerFE,
+                prepareShadowClientComposition(leftLayer.mRELayerSettings, kPortraitViewport,
+                                               kOutputDataspace))
+            .WillOnce(Return(std::nullopt));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{
+            Region(Rect(1000, 0, 2000, 1000)),
+            false, /* identity transform */
+            false, /* needs filtering */
+            true,  /* secure */
+            true,  /* supports protected content */
+            accumClearRegion,
+    };
+
+    EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+    EXPECT_CALL(rightLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+    EXPECT_CALL(rightLayer.mLayerFE, prepareClientComposition(Eq(ByRef(rightLayerSettings))))
+            .WillOnce(Return(rightLayer.mRELayerSettings));
+    EXPECT_CALL(rightLayer.mLayerFE,
+                prepareShadowClientComposition(rightLayer.mRELayerSettings, kPortraitViewport,
+                                               kOutputDataspace))
+            .WillOnce(Return(std::nullopt));
+
+    constexpr bool supportsProtectedContent = true;
+    auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent,
+                                                              accumClearRegion, kOutputDataspace);
+    ASSERT_EQ(2u, requests.size());
+    EXPECT_EQ(leftLayer.mRELayerSettings, requests[0]);
+    EXPECT_EQ(rightLayer.mRELayerSettings, 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));
+
+    renderengine::LayerSettings mREShadowSettings;
+    mREShadowSettings.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, prepareClientComposition(_))
+            .WillOnce(Return(mLayers[2].mRELayerSettings));
+    EXPECT_CALL(mLayers[2].mLayerFE,
+                prepareShadowClientComposition(mLayers[2].mRELayerSettings, kDisplayViewport,
+                                               kDisplayDataspace))
+            .WillOnce(Return(mREShadowSettings));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
     ASSERT_EQ(1u, requests.size());
-    EXPECT_EQ(456.f, requests[0].geometry.boundaries.left);
-    EXPECT_EQ(clearColor, requests[0].source.solidColor);
+
+    EXPECT_EQ(mREShadowSettings, 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));
+
+    renderengine::LayerSettings mREShadowSettings;
+    mREShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+    mLayers[2].mOutputLayerState.visibleRegion = kPartialContentWithPartialShadowRegion;
+    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, prepareClientComposition(_))
+            .WillOnce(Return(mLayers[2].mRELayerSettings));
+    EXPECT_CALL(mLayers[2].mLayerFE,
+                prepareShadowClientComposition(mLayers[2].mRELayerSettings, kDisplayViewport,
+                                               kDisplayDataspace))
+            .WillOnce(Return(mREShadowSettings));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(2u, requests.size());
+
+    EXPECT_EQ(mREShadowSettings, requests[0]);
+    EXPECT_EQ(mLayers[2].mRELayerSettings, requests[1]);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index da3f4fb..fd47e45 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -192,8 +192,8 @@
     mSurface.prepareFrame(true, true);
 }
 
-TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGlesComposition) {
-    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GLES))
+TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGpuComposition) {
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GPU))
             .WillOnce(Return(NO_ERROR));
 
     mSurface.prepareFrame(true, false);
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index dc71128..eb032f3 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -1281,6 +1281,45 @@
     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);
+}
+
 CommandReader::~CommandReader()
 {
     resetData();
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 336fdd8..301f54f 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -221,6 +221,13 @@
             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;
 };
 
 namespace impl {
@@ -442,6 +449,12 @@
             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;
 
 private:
 #if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 34254e0..12b0ddd 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -42,8 +42,6 @@
 using android::Rect;
 using android::Region;
 using android::sp;
-using android::hardware::Return;
-using android::hardware::Void;
 
 namespace HWC2 {
 
@@ -60,175 +58,8 @@
     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, std::nullopt);
-        return Void();
-    }
-
-    Return<void> onVsync_2_4(Hwc2::Display display, int64_t timestamp,
-                             Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override {
-        // TODO(b/140201379): use vsyncPeriodNanos in the new DispSync
-        mCallback->onVsyncReceived(mSequenceId, display, timestamp,
-                                   std::make_optional(vsyncPeriodNanos));
-        return Void();
-    }
-
-    Return<void> onVsyncPeriodTimingChanged(
-            Hwc2::Display display,
-            const Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override {
-        hwc_vsync_period_change_timeline_t timeline;
-        timeline.newVsyncAppliedTimeNanos = updatedTimeline.newVsyncAppliedTimeNanos;
-        timeline.refreshRequired = updatedTimeline.refreshRequired;
-        timeline.refreshTimeNanos = updatedTimeline.refreshTimeNanos;
-        mCallback->onVsyncPeriodTimingChangedReceived(mSequenceId, display, timeline);
-        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);
-
-        auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities,
-                                                          displayId, DisplayType::Physical);
-        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;
 
@@ -799,6 +630,26 @@
     return static_cast<Error>(intError);
 }
 
+Error Display::setAutoLowLatencyMode(bool on) const {
+    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) const {
+    using Hwc2_ContentType = Hwc2::IComposerClient::ContentType;
+    auto intError = mComposer.setContentType(mId, static_cast<Hwc2_ContentType>(contentType));
+    return static_cast<Error>(intError);
+}
+
 // For use by Device
 
 void Display::setConnected(bool connected) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 81ae3b6..8b532e3 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -38,8 +38,6 @@
 #include <unordered_set>
 #include <vector>
 
-#include "../Scheduler/StrongTyping.h"
-
 namespace android {
     struct DisplayedFrameStats;
     class Fence;
@@ -54,7 +52,6 @@
 
 namespace HWC2 {
 
-class Display;
 class Layer;
 using VsyncPeriodChangeConstraints = hwc_vsync_period_change_constraints_t;
 using VsyncPeriodChangeTimeline = hwc_vsync_period_change_timeline_t;
@@ -83,56 +80,6 @@
     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.
 class Display {
 public:
@@ -285,6 +232,10 @@
             const std::shared_ptr<const HWC2::Display::Config>& config,
             const VsyncPeriodChangeConstraints& constraints,
             VsyncPeriodChangeTimeline* outTimeline) = 0;
+    [[clang::warn_unused_result]] virtual Error setAutoLowLatencyMode(bool on) const = 0;
+    [[clang::warn_unused_result]] virtual Error getSupportedContentTypes(
+            std::vector<HWC2::ContentType>*) const = 0;
+    [[clang::warn_unused_result]] virtual Error setContentType(HWC2::ContentType) const = 0;
 };
 
 namespace impl {
@@ -348,7 +299,10 @@
     Error setActiveConfigWithConstraints(const std::shared_ptr<const HWC2::Display::Config>& config,
                                          const VsyncPeriodChangeConstraints& constraints,
                                          VsyncPeriodChangeTimeline* outTimeline) override;
-
+    Error setAutoLowLatencyMode(bool on) const override;
+    Error getSupportedContentTypes(
+            std::vector<HWC2::ContentType>* outSupportedContentTypes) const override;
+    Error setContentType(HWC2::ContentType contentType) const override;
     // Other Display methods
     hwc2_display_t getId() const override { return mId; }
     bool isConnected() const override { return mIsConnected; }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index d8dad0b..0a7009b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -65,14 +65,88 @@
 #define RETURN_IF_HWC_ERROR(error, displayId, ...) \
     RETURN_IF_HWC_ERROR_FOR(__FUNCTION__, error, displayId, __VA_ARGS__)
 
+namespace {
+
+using android::hardware::Return;
+using android::hardware::Void;
+
+class ComposerCallbackBridge : public android::Hwc2::IComposerCallback {
+public:
+    ComposerCallbackBridge(HWC2::ComposerCallback* callback, int32_t sequenceId,
+                           bool vsyncSwitchingSupported)
+          : mCallback(callback),
+            mSequenceId(sequenceId),
+            mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
+
+    android::hardware::Return<void> onHotplug(
+            android::Hwc2::Display display,
+            android::Hwc2::IComposerCallback::Connection conn) override {
+        HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
+        mCallback->onHotplugReceived(mSequenceId, display, connection);
+        return android::hardware::Void();
+    }
+
+    android::hardware::Return<void> onRefresh(android::Hwc2::Display display) override {
+        mCallback->onRefreshReceived(mSequenceId, display);
+        return android::hardware::Void();
+    }
+
+    android::hardware::Return<void> onVsync(android::Hwc2::Display 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(
+            android::Hwc2::Display display, int64_t timestamp,
+            android::Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override {
+        if (mVsyncSwitchingSupported) {
+            // TODO(b/140201379): use vsyncPeriodNanos in the new DispSync
+            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(
+            android::Hwc2::Display display,
+            const android::Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override {
+        hwc_vsync_period_change_timeline_t timeline;
+        timeline.newVsyncAppliedTimeNanos = updatedTimeline.newVsyncAppliedTimeNanos;
+        timeline.refreshRequired = updatedTimeline.refreshRequired;
+        timeline.refreshTimeNanos = updatedTimeline.refreshTimeNanos;
+        mCallback->onVsyncPeriodTimingChangedReceived(mSequenceId, display, timeline);
+        return android::hardware::Void();
+    }
+
+private:
+    HWC2::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)) {
+    loadCapabilities();
+}
+
+HWComposer::HWComposer(const std::string& composerServiceName)
+      : mComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {
+    loadCapabilities();
+}
 
 HWComposer::~HWComposer() {
     mDisplayData.clear();
@@ -80,12 +154,21 @@
 
 void HWComposer::registerCallback(HWC2::ComposerCallback* callback,
                                   int32_t sequenceId) {
-    mHwcDevice->registerCallback(callback, sequenceId);
+    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,
                                               DisplayIdentificationData* outData) const {
-    const auto error = mHwcDevice->getDisplayIdentificationData(hwcDisplayId, outPort, outData);
+    const auto error = static_cast<HWC2::Error>(
+            mComposer->getDisplayIdentificationData(hwcDisplayId, outPort, outData));
     if (error != HWC2::Error::None) {
         if (error != HWC2::Error::Unsupported) {
             LOG_HWC_DISPLAY_ERROR(hwcDisplayId, to_string(error).c_str());
@@ -95,9 +178,8 @@
     return true;
 }
 
-bool HWComposer::hasCapability(HWC2::Capability capability) const
-{
-    return mHwcDevice->getCapabilities().count(capability) > 0;
+bool HWComposer::hasCapability(HWC2::Capability capability) const {
+    return mCapabilities.count(capability) > 0;
 }
 
 bool HWComposer::hasDisplayCapability(const std::optional<DisplayId>& displayId,
@@ -133,13 +215,33 @@
           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);
+        auto& displayData = mDisplayData[info->id];
+        // 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.
+        if (displayData.hwcDisplay != nullptr && displayData.hwcDisplay->isConnected()) {
+            ALOGI("Hotplug connecting an already connected display."
+                  " Clearing old display state.");
+        }
+        displayData.hwcDisplay.reset();
+        auto newDisplay =
+                std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
+                                                      HWC2::DisplayType::Physical);
+        newDisplay->setConnected(true);
+        displayData.hwcDisplay = std::move(newDisplay);
         mPhysicalDisplayIdMap[hwcDisplayId] = info->id;
+    } else if (connection == HWC2::Connection::Disconnected) {
+        // The display will later be destroyed by a call to
+        // destroyDisplay(). For now we just mark it disconnected.
+        auto& displayData = mDisplayData[info->id];
+        if (displayData.hwcDisplay) {
+            displayData.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 info;
@@ -197,14 +299,18 @@
               height, SurfaceFlinger::maxVirtualDisplaySize);
         return {};
     }
-    HWC2::Display* display;
-    auto error = mHwcDevice->createVirtualDisplay(width, height, format,
-            &display);
+    hwc2_display_t hwcDisplayId = 0;
+    const auto error = static_cast<HWC2::Error>(
+            mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId));
     if (error != HWC2::Error::None) {
         ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__);
         return {};
     }
 
+    auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities,
+                                                         hwcDisplayId, HWC2::DisplayType::Virtual);
+    display->setConnected(true);
+
     DisplayId displayId;
     if (mFreeVirtualDisplayIds.empty()) {
         displayId = getVirtualDisplayId(mNextVirtualDisplayId++);
@@ -214,7 +320,7 @@
     }
 
     auto& displayData = mDisplayData[displayId];
-    displayData.hwcDisplay = display;
+    displayData.hwcDisplay = std::move(display);
     displayData.isVirtual = true;
 
     --mRemainingHwcVirtualDisplays;
@@ -224,9 +330,8 @@
 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;
 }
@@ -234,8 +339,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);
 }
 
@@ -484,8 +588,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<HWC2::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;
     }
@@ -600,8 +704,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) {
@@ -609,8 +711,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,
@@ -722,11 +824,46 @@
     return getComposer()->isUsingVrComposer();
 }
 
+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 == 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;
+}
+
+status_t HWComposer::getSupportedContentTypes(
+        DisplayId displayId, std::vector<HWC2::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, HWC2::ContentType contentType) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error = mDisplayData[displayId].hwcDisplay->setContentType(contentType);
+    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;
+}
+
 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());
+    result.append(mComposer->dumpDebugInfo());
 }
 
 std::optional<DisplayId> HWComposer::toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const {
@@ -792,5 +929,17 @@
                                                                            : "External display"};
 }
 
+void HWComposer::loadCapabilities() {
+    static_assert(sizeof(HWC2::Capability) == sizeof(int32_t), "Capability size has changed");
+    auto capabilities = mComposer->getCapabilities();
+    for (auto capability : capabilities) {
+        mCapabilities.emplace(static_cast<HWC2::Capability>(capability));
+    }
+}
+
+uint32_t HWComposer::getMaxVirtualDisplayCount() const {
+    return mComposer->getMaxVirtualDisplayCount();
+}
+
 } // namespace impl
 } // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 077e452..a0dabb4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -184,6 +184,10 @@
             DisplayId displayId, size_t configId,
             const HWC2::VsyncPeriodChangeConstraints& constraints,
             HWC2::VsyncPeriodChangeTimeline* outTimeline) = 0;
+    virtual status_t setAutoLowLatencyMode(DisplayId displayId, bool on) = 0;
+    virtual status_t getSupportedContentTypes(
+            DisplayId displayId, std::vector<HWC2::ContentType>* outSupportedContentTypes) = 0;
+    virtual status_t setContentType(DisplayId displayId, HWC2::ContentType contentType) = 0;
 
     // for debugging ----------------------------------------------------------
     virtual void dump(std::string& out) const = 0;
@@ -203,6 +207,7 @@
 class HWComposer final : public android::HWComposer {
 public:
     explicit HWComposer(std::unique_ptr<Hwc2::Composer> composer);
+    explicit HWComposer(const std::string& composerServiceName);
 
     ~HWComposer() override;
 
@@ -313,11 +318,15 @@
     status_t setActiveConfigWithConstraints(DisplayId displayId, size_t configId,
                                             const HWC2::VsyncPeriodChangeConstraints& constraints,
                                             HWC2::VsyncPeriodChangeTimeline* outTimeline) override;
+    status_t setAutoLowLatencyMode(DisplayId displayId, bool) override;
+    status_t getSupportedContentTypes(DisplayId displayId,
+                                      std::vector<HWC2::ContentType>*) override;
+    status_t setContentType(DisplayId displayId, HWC2::ContentType) 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 {
@@ -335,11 +344,12 @@
     friend TestableSurfaceFlinger;
 
     std::optional<DisplayIdentificationInfo> onHotplugConnect(hwc2_display_t hwcDisplayId);
+    void loadCapabilities();
+    uint32_t getMaxVirtualDisplayCount() const;
 
     struct DisplayData {
         bool isVirtual = false;
-
-        HWC2::Display* hwcDisplay = nullptr;
+        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;
@@ -361,9 +371,9 @@
 
     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<HWC2::Capability> mCapabilities;
+    bool mRegisteredCallback = false;
 
     std::unordered_map<hwc2_display_t, DisplayId> mPhysicalDisplayIdMap;
     std::optional<hwc2_display_t> mInternalHwcDisplayId;
@@ -372,7 +382,7 @@
 
     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/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 4e0e4df..56b0ea6 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -42,13 +42,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 +93,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 +144,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 +162,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 +192,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 +301,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 +372,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 +383,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 +456,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 +485,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 +514,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 +637,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 +668,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";
     }
 }
 
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/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 35fc4be..6fd1629 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -430,6 +430,7 @@
     compositionState.internalOnly = getPrimaryDisplayOnly();
     compositionState.isVisible = isVisible();
     compositionState.isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
+    compositionState.shadowRadius = mEffectiveShadowRadius;
 
     compositionState.contentDirty = contentDirty;
     contentDirty = false;
@@ -585,6 +586,7 @@
 
     renderengine::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.
@@ -1928,6 +1930,11 @@
                                        [&]() { 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) {
@@ -1935,6 +1942,9 @@
                                        [&]() { 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);
     }
 }
 
@@ -2025,6 +2035,7 @@
 
 InputWindowInfo Layer::fillInputInfo() {
     InputWindowInfo info = mDrawingState.inputInfo;
+    info.id = sequence;
 
     if (info.displayId == ADISPLAY_ID_NONE) {
         info.displayId = getLayerStack();
@@ -2081,9 +2092,29 @@
         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;
 }
 
+sp<Layer> Layer::getClonedRoot() {
+    if (mClonedChild != nullptr) {
+        return this;
+    }
+    if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) {
+        return nullptr;
+    }
+    return mDrawingParent.promote()->getClonedRoot();
+}
+
 bool Layer::hasInput() const {
     return mDrawingState.inputInfo.token != nullptr;
 }
@@ -2119,10 +2150,6 @@
     // copy drawing state from cloned layer
     mDrawingState = clonedFrom->mDrawingState;
     mClonedFrom = clonedFrom;
-
-    // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple
-    // InputWindows per client token yet.
-    mDrawingState.inputInfo.token = nullptr;
 }
 
 void Layer::updateMirrorInfo() {
@@ -2157,9 +2184,6 @@
     if (isClonedFromAlive()) {
         sp<Layer> clonedFrom = getClonedFrom();
         mDrawingState = clonedFrom->mDrawingState;
-        // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple
-        // InputWindows per client token yet.
-        mDrawingState.inputInfo.token = nullptr;
         clonedLayersMap.emplace(clonedFrom, this);
     }
 
@@ -2198,7 +2222,24 @@
     }
 }
 
-void Layer::updateClonedRelatives(std::map<sp<Layer>, sp<Layer>> 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();
 
@@ -2206,11 +2247,11 @@
         return;
     }
 
-    sp<Layer> clonedFrom = getClonedFrom();
+    const sp<Layer>& clonedFrom = getClonedFrom();
     for (wp<Layer>& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) {
-        sp<Layer> relative = relativeWeak.promote();
-        auto clonedRelative = clonedLayersMap[relative];
-        if (clonedRelative != nullptr) {
+        const sp<Layer>& relative = relativeWeak.promote();
+        if (clonedLayersMap.count(relative) > 0) {
+            auto& clonedRelative = clonedLayersMap.at(relative);
             mDrawingState.zOrderRelatives.add(clonedRelative);
         }
     }
@@ -2220,12 +2261,14 @@
     // 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.
-    sp<Layer> relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote();
-    sp<Layer> clonedRelativeOf = clonedLayersMap[relativeOf];
-    if (clonedRelativeOf != nullptr) {
+    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);
     }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 843d3ae..d697a6a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -497,8 +497,9 @@
     void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
     void updateClonedChildren(const sp<Layer>& mirrorRoot,
                               std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
-    void updateClonedRelatives(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);
 
 public:
     /*
@@ -972,6 +973,10 @@
 
     // 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();
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index c94e439..b402270 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -155,5 +155,13 @@
     }
 }
 
+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
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/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 38a80a7..c69859e 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -18,26 +18,139 @@
 #include "Client.h"
 #include "Layer.h"
 
+#include <gui/IProducerListener.h>
+
+#undef LOG_TAG
+#define LOG_TAG "RefreshRateOverlay"
+
 namespace android {
 
+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, "RefreshRateOverlayBuffer");
+    uint8_t* pixels;
+    buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&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));
 
     // setting Layer's Z requires resorting layersSortedByZ
     ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer);
@@ -49,9 +162,51 @@
     return true;
 }
 
+void RefreshRateOverlay::primeCache() {
+    auto allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates();
+    if (allRefreshRates.size() == 1) {
+        auto fps = allRefreshRates.begin()->second.fps;
+        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.fps);
+    }
+
+    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::changeRefreshRate(const RefreshRate& refreshRate) {
-    const half3& color = (refreshRate.fps > 65.0f) ? GREEN : RED;
-    mLayer->setColor(color);
+    const auto display = mFlinger.getDefaultDisplayDeviceLocked();
+    if (!display) {
+        return;
+    }
+
+    const int32_t left = display->getWidth() / 32;
+    const int32_t top = display->getHeight() / 32;
+    const int32_t right = left + display->getWidth() / 8;
+    const int32_t buttom = top + display->getHeight() / 32;
+
+    auto buffer = mBufferCache[refreshRate.fps];
+    mLayer->setBuffer(buffer, 0, 0, {});
+
+    mLayer->setFrame(Rect(left, top, right, buttom));
+
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
 
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 414bc47..6d34df2 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -28,7 +28,32 @@
     void changeRefreshRate(const RefreshRate& 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;
@@ -36,8 +61,11 @@
     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
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 571c9ca..bd4b0ec 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -27,16 +27,14 @@
 
 namespace android {
 
-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)),
         mDispSync(dispSync),
-        mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset),
-        mOffsetThresholdForNextVsync(offsetThresholdForNextVsync) {}
+        mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset) {}
 
 void DispSyncSource::setVSyncEnabled(bool enable) {
     std::lock_guard lock(mVsyncMutex);
@@ -67,10 +65,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;
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 536464e..328b8c1 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -26,8 +26,7 @@
 
 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;
 
@@ -55,7 +54,6 @@
 
     std::mutex mVsyncMutex;
     TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
-    const nsecs_t mOffsetThresholdForNextVsync;
     bool mEnabled GUARDED_BY(mVsyncMutex) = false;
 };
 
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index ff800c3..5bdef58 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -103,10 +103,11 @@
 }
 
 DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId,
-                                              HwcConfigIndexType configId) {
+                                              HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
     DisplayEventReceiver::Event event;
     event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()};
     event.config.configId = configId.value();
+    event.config.vsyncPeriod = vsyncPeriod;
     return event;
 }
 
@@ -116,7 +117,7 @@
                                              ResyncCallback resyncCallback,
                                              ISurfaceComposer::ConfigChanged configChanged)
       : resyncCallback(std::move(resyncCallback)),
-        configChanged(configChanged),
+        mConfigChanged(configChanged),
         mEventThread(eventThread),
         mChannel(gui::BitTube::DefaultSize) {}
 
@@ -145,6 +146,18 @@
     mEventThread->requestNextVsync(this);
 }
 
+void EventThreadConnection::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) {
+    ATRACE_NAME("enableConfigEvents");
+    mConfigChanged = configChangeFlag;
+
+    // In principle it's possible for rapidly toggling config events to drop an
+    // event here, but it's unlikely in practice.
+    if (configChangeFlag == ISurfaceComposer::eConfigChangedDispatch) {
+        mForcedConfigChangeDispatch = true;
+        mEventThread->requestLatestConfig();
+    }
+}
+
 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);
@@ -257,6 +270,24 @@
     }
 }
 
+void EventThread::requestLatestConfig() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    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) {
@@ -292,10 +323,11 @@
     mCondition.notify_all();
 }
 
-void EventThread::onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType 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();
 }
 
@@ -325,6 +357,9 @@
                         mInterceptVSyncsCallback(event->header.timestamp);
                     }
                     break;
+                case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+                    mLastConfigChangeEvent = *event;
+                    break;
             }
         }
 
@@ -398,8 +433,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 forcedDispatch = connection->mForcedConfigChangeDispatch.exchange(false);
+            return forcedDispatch ||
+                    connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch;
+        }
 
         case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
             switch (connection->vsyncRequest) {
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index a42546c..641b2a5 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -81,12 +81,19 @@
     status_t stealReceiveChannel(gui::BitTube* outChannel) override;
     status_t setVsyncRate(uint32_t rate) override;
     void requestNextVsync() override; // asynchronous
+    void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override;
 
     // Called in response to requestNextVsync.
     const ResyncCallback resyncCallback;
 
     VSyncRequest vsyncRequest = VSyncRequest::None;
-    const ISurfaceComposer::ConfigChanged configChanged;
+    std::atomic<ISurfaceComposer::ConfigChanged> mConfigChanged =
+            ISurfaceComposer::ConfigChanged::eConfigChangedSuppress;
+    // Store whether we need to force dispatching a config change separately -
+    // if mConfigChanged ever changes before the config change is dispatched
+    // then we still need to propagate an initial config to the app if we
+    // haven't already.
+    std::atomic<bool> mForcedConfigChangeDispatch = false;
 
 private:
     virtual void onFirstRef();
@@ -110,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, HwcConfigIndexType configId) = 0;
+    virtual void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
+                                 nsecs_t vsyncPeriod) = 0;
 
     virtual void dump(std::string& result) const = 0;
 
@@ -121,6 +129,11 @@
     virtual void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) = 0;
     // Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
     virtual void requestNextVsync(const sp<EventThreadConnection>& connection) = 0;
+
+    // Dispatches the most recent configuration
+    // Usage of this method assumes that only the primary internal display
+    // supports multiple display configurations.
+    virtual void requestLatestConfig() = 0;
 };
 
 namespace impl {
@@ -138,6 +151,7 @@
     status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
     void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
     void requestNextVsync(const sp<EventThreadConnection>& connection) override;
+    void requestLatestConfig() override;
 
     // called before the screen is turned off from main thread
     void onScreenReleased() override;
@@ -147,7 +161,8 @@
 
     void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
 
-    void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId) override;
+    void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
+                         nsecs_t vsyncPeriod) override;
 
     void dump(std::string& result) const override;
 
@@ -182,6 +197,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/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index 6c502e6..fa46e6f 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -45,7 +45,6 @@
     const char* getName() const override { return "inject"; }
     void setVSyncEnabled(bool) override {}
     void setPhaseOffset(nsecs_t) override {}
-    void pauseVsyncCallback(bool) {}
 
 private:
     std::mutex mCallbackMutex;
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 12832a6..13014c7 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -31,49 +31,56 @@
     return std::nullopt;
 }
 
+bool fpsEqualsWithMargin(float fpsA, float fpsB) {
+    static constexpr float MARGIN = 0.01f;
+    return std::abs(fpsA - fpsB) <= MARGIN;
+}
+
 } // namespace
 
 namespace android::scheduler {
 
-PhaseOffsets::~PhaseOffsets() = default;
+PhaseConfiguration::~PhaseConfiguration() = default;
 
 namespace impl {
 
-PhaseOffsets::PhaseOffsets() {
-    // 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.
-    const nsecs_t thresholdForNextVsync =
-            getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
-                    .value_or(std::numeric_limits<nsecs_t>::max());
-
-    mDefaultOffsets = getDefaultOffsets(thresholdForNextVsync);
-    mHighFpsOffsets = getHighFpsOffsets(thresholdForNextVsync);
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
-    // TODO(145561086): Once offsets are common for all refresh rates we can remove the magic
-    // number for refresh rate
-    if (fps > 65.0f) {
-        return mHighFpsOffsets;
-    } else {
-        return mDefaultOffsets;
-    }
-}
+PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+      : // 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.
+        mThresholdForNextVsync(getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+                                       .value_or(std::numeric_limits<nsecs_t>::max())),
+        mOffsets(initializeOffsets(refreshRateConfigs)),
+        mRefreshRateFps(refreshRateConfigs.getCurrentRefreshRate().fps) {}
 
 void PhaseOffsets::dump(std::string& result) const {
-    const auto [early, earlyGl, late, threshold] = getCurrentOffsets();
+    const auto [early, earlyGl, late] = getCurrentOffsets();
     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, threshold);
+                  late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf,
+                  mThresholdForNextVsync);
 }
 
-PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t thresholdForNextVsync) {
+std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
+        const scheduler::RefreshRateConfigs& refreshRateConfigs) const {
+    std::unordered_map<float, Offsets> offsets;
+
+    for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) {
+        if (refreshRate.fps > 65.0f) {
+            offsets.emplace(refreshRate.fps, getHighFpsOffsets(refreshRate.vsyncPeriod));
+        } else {
+            offsets.emplace(refreshRate.fps, getDefaultOffsets(refreshRate.vsyncPeriod));
+        }
+    }
+    return offsets;
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
     const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000);
     const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000);
 
@@ -82,19 +89,32 @@
     const auto earlyAppOffsetNs = getProperty("debug.sf.early_app_phase_offset_ns");
     const auto earlyGlAppOffsetNs = getProperty("debug.sf.early_gl_app_phase_offset_ns");
 
-    return {{earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
-             earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
+    return {
+            {
+                    earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync
+                            ? earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs)
+                            : earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration,
 
-            {earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
-             earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
+                    earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs),
+            },
+            {
+                    earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync
+                            ? earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs)
+                            : earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration,
 
-            {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs},
+                    earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs),
+            },
+            {
+                    sfVsyncPhaseOffsetNs < mThresholdForNextVsync
+                            ? sfVsyncPhaseOffsetNs
+                            : sfVsyncPhaseOffsetNs - vsyncDuration,
 
-            thresholdForNextVsync};
+                    vsyncPhaseOffsetNs,
+            },
+    };
 }
 
-PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t thresholdForNextVsync) {
-    // TODO(b/122905996): Define these in device.mk.
+PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
     const int highFpsLateAppOffsetNs =
             getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
     const int highFpsLateSfOffsetNs =
@@ -106,15 +126,206 @@
     const auto highFpsEarlyGlAppOffsetNs =
             getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
 
-    return {{highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs),
-             highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
+    return {
+            {
+                    highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync
+                            ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs)
+                            : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) -
+                                    vsyncDuration,
 
-            {highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs),
-             highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
+                    highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs),
+            },
+            {
+                    highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) <
+                                    mThresholdForNextVsync
+                            ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs)
+                            : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) -
+                                    vsyncDuration,
 
-            {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs},
+                    highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs),
+            },
+            {
+                    highFpsLateSfOffsetNs < mThresholdForNextVsync
+                            ? highFpsLateSfOffsetNs
+                            : highFpsLateSfOffsetNs - vsyncDuration,
 
-            thresholdForNextVsync};
+                    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);
+                                   });
+    LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
+    return iter->second;
+}
+
+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;
+}
+
+static 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.fps);
+    }
+
+    return refreshRates;
+}
+
+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) {
+        const nsecs_t vsyncDuration = static_cast<nsecs_t>(1e9f / fps);
+        offsets.emplace(fps,
+                        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),
+                                },
+                        });
+    }
+    return offsets;
+}
+
+PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+      : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs),
+                       refreshRateConfigs.getCurrentRefreshRate().fps,
+                       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);
+    });
+    LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
+    return iter->second;
+}
+
+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
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index 7747f0c..7b1bdfd 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -29,17 +29,11 @@
  * 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:
     using Offsets = VSyncModulator::OffsetsConfig;
 
-    virtual ~PhaseOffsets();
-
-    nsecs_t getCurrentAppOffset() const { return getCurrentOffsets().late.app; }
-    nsecs_t getCurrentSfOffset() const { return getCurrentOffsets().late.sf; }
-    nsecs_t getOffsetThresholdForNextVsync() const {
-        return getCurrentOffsets().thresholdForNextVsync;
-    }
+    virtual ~PhaseConfiguration();
 
     virtual Offsets getCurrentOffsets() const = 0;
     virtual Offsets getOffsetsForRefreshRate(float fps) const = 0;
@@ -51,9 +45,13 @@
 
 namespace impl {
 
-class PhaseOffsets : public scheduler::PhaseOffsets {
+/*
+ * This is the old implementation of phase offsets and considered as deprecated.
+ * PhaseDurations is the new implementation.
+ */
+class PhaseOffsets : public scheduler::PhaseConfiguration {
 public:
-    PhaseOffsets();
+    PhaseOffsets(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;
@@ -69,13 +67,60 @@
     void dump(std::string& result) const override;
 
 private:
-    static Offsets getDefaultOffsets(nsecs_t thresholdForNextVsync);
-    static Offsets getHighFpsOffsets(nsecs_t thresholdForNextVsync);
+    std::unordered_map<float, Offsets> initializeOffsets(
+            const scheduler::RefreshRateConfigs&) const;
+    Offsets getDefaultOffsets(nsecs_t vsyncDuration) const;
+    Offsets getHighFpsOffsets(nsecs_t vsyncDuration) const;
 
-    std::atomic<float> mRefreshRateFps = 0;
+    const nsecs_t mThresholdForNextVsync;
+    const std::unordered_map<float, Offsets> mOffsets;
 
-    Offsets mDefaultOffsets;
-    Offsets mHighFpsOffsets;
+    std::atomic<float> mRefreshRateFps;
+};
+
+/*
+ * 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;
+
+    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
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 23fb96a..692ded9 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -110,13 +110,48 @@
     init(inputConfigs, currentConfigId);
 }
 
-void RefreshRateConfigs::setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
-                                   float maxRefreshRate) {
+status_t RefreshRateConfigs::setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
+                                       float maxRefreshRate, bool* outPolicyChanged) {
     std::lock_guard lock(mLock);
-    mCurrentGroupId = mRefreshRates.at(defaultConfigId).configGroup;
+    bool policyChanged = defaultConfigId != mDefaultConfig ||
+            minRefreshRate != mMinRefreshRateFps || maxRefreshRate != mMaxRefreshRateFps;
+    if (outPolicyChanged) {
+        *outPolicyChanged = policyChanged;
+    }
+    if (!policyChanged) {
+        return NO_ERROR;
+    }
+    // defaultConfigId must be a valid config ID, and within the given refresh rate range.
+    if (mRefreshRates.count(defaultConfigId) == 0) {
+        return BAD_VALUE;
+    }
+    const RefreshRate& refreshRate = mRefreshRates.at(defaultConfigId);
+    if (!refreshRate.inPolicy(minRefreshRate, maxRefreshRate)) {
+        return BAD_VALUE;
+    }
+    mDefaultConfig = defaultConfigId;
     mMinRefreshRateFps = minRefreshRate;
     mMaxRefreshRateFps = maxRefreshRate;
     constructAvailableRefreshRates();
+    return NO_ERROR;
+}
+
+void RefreshRateConfigs::getPolicy(HwcConfigIndexType* defaultConfigId, float* minRefreshRate,
+                                   float* maxRefreshRate) const {
+    std::lock_guard lock(mLock);
+    *defaultConfigId = mDefaultConfig;
+    *minRefreshRate = mMinRefreshRateFps;
+    *maxRefreshRate = mMaxRefreshRateFps;
+}
+
+bool RefreshRateConfigs::isConfigAllowed(HwcConfigIndexType config) const {
+    std::lock_guard lock(mLock);
+    for (const RefreshRate* refreshRate : mAvailableRefreshRates) {
+        if (refreshRate->configId == config) {
+            return true;
+        }
+    }
+    return false;
 }
 
 void RefreshRateConfigs::getSortedRefreshRateList(
@@ -140,15 +175,18 @@
 
 void RefreshRateConfigs::constructAvailableRefreshRates() {
     // Filter configs based on current policy and sort based on vsync period
-    ALOGV("constructRefreshRateMap: group %d min %.2f max %.2f", mCurrentGroupId.value(),
-          mMinRefreshRateFps, mMaxRefreshRateFps);
+    HwcConfigGroupType group = mRefreshRates.at(mDefaultConfig).configGroup;
+    ALOGV("constructRefreshRateMap: default %d group %d min %.2f max %.2f", mDefaultConfig.value(),
+          group.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
     getSortedRefreshRateList(
-            [this](const RefreshRate& refreshRate) REQUIRES(mLock) {
-                return refreshRate.configGroup == mCurrentGroupId &&
-                        refreshRate.fps >= mMinRefreshRateFps &&
-                        refreshRate.fps <= mMaxRefreshRateFps;
+            [&](const RefreshRate& refreshRate) REQUIRES(mLock) {
+                return refreshRate.configGroup == group &&
+                        refreshRate.inPolicy(mMinRefreshRateFps, mMaxRefreshRateFps);
             },
             &mAvailableRefreshRates);
+    LOG_ALWAYS_FATAL_IF(mAvailableRefreshRates.empty(),
+                        "No compatible display configs for default=%d min=%.0f max=%.0f",
+                        mDefaultConfig.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
 }
 
 // NO_THREAD_SAFETY_ANALYSIS since this is called from the constructor
@@ -167,12 +205,12 @@
         mRefreshRates.emplace(config.configId, buildRefreshRate(config));
         if (config.configId == currentHwcConfig) {
             mCurrentRefreshRate = &mRefreshRates.at(config.configId);
-            mCurrentGroupId = config.configGroup;
         }
     }
 
     std::vector<const RefreshRate*> sortedConfigs;
     getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
+    mDefaultConfig = currentHwcConfig;
     mMinSupportedRefreshRate = sortedConfigs.front();
     mMaxSupportedRefreshRate = sortedConfigs.back();
     constructAvailableRefreshRates();
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index fb14dc7..0c3369a 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -44,6 +44,9 @@
 class RefreshRateConfigs {
 public:
     struct RefreshRate {
+        // The tolerance within which we consider FPS approximately equals.
+        static constexpr float FPS_EPSILON = 0.001f;
+
         RefreshRate(HwcConfigIndexType configId, nsecs_t vsyncPeriod,
                     HwcConfigGroupType configGroup, std::string name, float fps)
               : configId(configId),
@@ -63,6 +66,12 @@
         // Refresh rate in frames per second
         const float fps = 0;
 
+        // 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 || vsyncPeriod != other.vsyncPeriod ||
                     configGroup != other.configGroup;
@@ -73,9 +82,17 @@
 
     using AllRefreshRatesMapType = std::unordered_map<HwcConfigIndexType, const RefreshRate>;
 
-    // Sets the current policy to choose refresh rates.
-    void setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate, float maxRefreshRate)
-            EXCLUDES(mLock);
+    // Sets the current policy to choose refresh rates. Returns NO_ERROR if the requested policy is
+    // valid, or a negative error value otherwise. policyChanged, if non-null, will be set to true
+    // if the new policy is different from the old policy.
+    status_t setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
+                       float maxRefreshRate, bool* policyChanged) EXCLUDES(mLock);
+    // Gets the current policy.
+    void getPolicy(HwcConfigIndexType* defaultConfigId, float* minRefreshRate,
+                   float* maxRefreshRate) const EXCLUDES(mLock);
+
+    // Returns true if config is allowed by the current policy.
+    bool isConfigAllowed(HwcConfigIndexType config) const EXCLUDES(mLock);
 
     // Returns true if this device is doing refresh rate switching. This won't change at runtime.
     bool refreshRateSwitchingSupported() const { return mRefreshRateSwitching; }
@@ -143,9 +160,9 @@
     // the main thread, and read by the Scheduler (and other objects) on other threads.
     const RefreshRate* mCurrentRefreshRate GUARDED_BY(mLock);
 
-    // The current config group. This will change at runtime. This is set by SurfaceFlinger on
+    // The default 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.
-    HwcConfigGroupType mCurrentGroupId GUARDED_BY(mLock);
+    HwcConfigIndexType mDefaultConfig GUARDED_BY(mLock);
 
     // The min and max FPS allowed by the policy. This will change at runtime and set by
     // SurfaceFlinger on the main thread.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 71a6a2f..ff9cf86 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -47,6 +47,10 @@
 #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 {                                                             \
@@ -58,11 +62,45 @@
 
 namespace android {
 
+std::unique_ptr<DispSync> createDispSync() {
+    // TODO (140302863) remove this and use the vsync_reactor system.
+    if (property_get_bool("debug.sf.vsync_reactor", false)) {
+        // TODO (144707443) tune Predictor tunables.
+        static constexpr int default_rate = 60;
+        static constexpr auto initial_period =
+                std::chrono::duration<nsecs_t, std::ratio<1, default_rate>>(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>(
+                                                   initial_period)
+                                                   .count(),
+                                           vsyncTimestampHistorySize, minimumSamplesForPrediction,
+                                           discardOutlierPercent);
+
+        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());
+
+        static constexpr size_t pendingFenceLimit = 20;
+        return std::make_unique<scheduler::VSyncReactor>(std::make_unique<scheduler::SystemClock>(),
+                                                         std::move(dispatch), std::move(tracker),
+                                                         pendingFenceLimit);
+    } else {
+        return std::make_unique<impl::DispSync>("SchedulerDispSync",
+                                                sysprop::running_without_sync_framework(true));
+    }
+}
+
 Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
                      const scheduler::RefreshRateConfigs& refreshRateConfig,
                      ISchedulerCallback& schedulerCallback)
-      : mPrimaryDispSync(new impl::DispSync("SchedulerDispSync",
-                                            sysprop::running_without_sync_framework(true))),
+      : mPrimaryDispSync(createDispSync()),
         mEventControlThread(new impl::EventControlThread(std::move(function))),
         mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
         mSchedulerCallback(schedulerCallback),
@@ -125,18 +163,16 @@
     return *mPrimaryDispSync;
 }
 
-std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
-        const char* name, 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,
-                                            offsetThresholdForNextVsync, true /* traceVsync */,
-                                            name);
+                                            true /* traceVsync */, name);
 }
 
 Scheduler::ConnectionHandle Scheduler::createConnection(
-        const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
+        const char* connectionName, nsecs_t phaseOffsetNs,
         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
-    auto vsyncSource =
-            makePrimaryDispSyncSource(connectionName, phaseOffsetNs, offsetThresholdForNextVsync);
+    auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs);
     auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
                                                            std::move(interceptCallback));
     return createConnection(std::move(eventThread));
@@ -186,9 +222,9 @@
 }
 
 void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
-                                HwcConfigIndexType configId) {
+                                HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
     RETURN_IF_INVALID_HANDLE(handle);
-    mConnections[handle].thread->onConfigChanged(displayId, configId);
+    mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod);
 }
 
 void Scheduler::dump(ConnectionHandle handle, std::string& result) const {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 15277ce..2cdb757 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -66,7 +66,6 @@
 
     using ConnectionHandle = scheduler::ConnectionHandle;
     ConnectionHandle createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
-                                      nsecs_t offsetThresholdForNextVsync,
                                       impl::EventThread::InterceptVSyncsCallback);
 
     sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
@@ -75,7 +74,8 @@
     sp<EventThreadConnection> getEventConnection(ConnectionHandle);
 
     void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
-    void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId);
+    void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId,
+                         nsecs_t vsyncPeriod);
 
     void onScreenAcquired(ConnectionHandle);
     void onScreenReleased(ConnectionHandle);
@@ -149,8 +149,7 @@
     Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
               const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback);
 
-    std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs,
-                                                           nsecs_t offsetThresholdForNextVsync);
+    std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
 
     // Create a connection on the given EventThread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread>);
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index e001080..56b3252 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -26,7 +26,7 @@
 class TimeKeeper;
 class VSyncTracker;
 
-enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error };
+enum class ScheduleResult { Scheduled, CannotSchedule, Error };
 enum class CancelResult { Cancelled, TooLate, Error };
 
 /*
@@ -83,7 +83,6 @@
      * \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::ReScheduled if callback was rescheduled.
      *                             A ScheduleResult::CannotSchedule
      *                             if (workDuration - earliestVsync) is in the past, or
      *                             if a callback was dispatched for the predictedVsync already.
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index a79fe98..48f2abb 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -29,8 +29,13 @@
 TimeKeeper::~TimeKeeper() = default;
 
 VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name,
-                                                           std::function<void(nsecs_t)> const& cb)
-      : mName(name), mCallback(cb), mWorkDuration(0), mEarliestVsync(0) {}
+                                                           std::function<void(nsecs_t)> const& cb,
+                                                           nsecs_t minVsyncDistance)
+      : mName(name),
+        mCallback(cb),
+        mWorkDuration(0),
+        mEarliestVsync(0),
+        mMinVsyncDistance(minVsyncDistance) {}
 
 std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
     return mLastDispatchTime;
@@ -49,18 +54,28 @@
 
 ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
                                                       VSyncTracker& tracker, nsecs_t now) {
-    auto const nextVsyncTime =
+    auto nextVsyncTime =
             tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
-    if (mLastDispatchTime >= nextVsyncTime) { // already dispatched a callback for this vsync
-        return ScheduleResult::CannotSchedule;
+
+    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;
-    auto result = mArmedInfo ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
     mWorkDuration = workDuration;
     mEarliestVsync = earliestVsync;
     mArmedInfo = {nextWakeupTime, nextVsyncTime};
-    return result;
+    return ScheduleResult::Scheduled;
 }
 
 void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
@@ -101,8 +116,12 @@
 }
 
 VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
-                                                 VSyncTracker& tracker, nsecs_t timerSlack)
-      : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {}
+                                                 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);
@@ -187,7 +206,8 @@
             mCallbacks
                     .emplace(++mCallbackToken,
                              std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
-                                                                            callbackFn))
+                                                                            callbackFn,
+                                                                            mMinVsyncDistance))
                     .first->first};
 }
 
@@ -277,12 +297,16 @@
 }
 
 ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
-    if (!mValidToken) return ScheduleResult::Error;
+    if (!mValidToken) {
+        return ScheduleResult::Error;
+    }
     return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
 }
 
 CancelResult VSyncCallbackRegistration::cancel() {
-    if (!mValidToken) return CancelResult::Error;
+    if (!mValidToken) {
+        return CancelResult::Error;
+    }
     return mDispatch.get().cancel(mToken);
 }
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index fc78da3..0c9b4fe 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -36,7 +36,8 @@
     // 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, std::function<void(nsecs_t)> const& fn);
+    VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn,
+                                 nsecs_t minVsyncDistance);
     std::string_view name() const;
 
     // Start: functions that are not threadsafe.
@@ -72,6 +73,7 @@
 
     nsecs_t mWorkDuration;
     nsecs_t mEarliestVsync;
+    nsecs_t const mMinVsyncDistance;
 
     struct ArmingInfo {
         nsecs_t mActualWakeupTime;
@@ -91,8 +93,15 @@
  */
 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 timerSlack, nsecs_t minVsyncDistance);
     ~VSyncDispatchTimerQueue();
 
     CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
@@ -119,6 +128,7 @@
     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;
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index 63c0feb..704a5d5 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -43,6 +43,10 @@
     struct Offsets {
         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); }
     };
 
     struct OffsetsConfig {
@@ -50,7 +54,11 @@
         Offsets earlyGl; // As above but while compositing with GL.
         Offsets late;    // Default.
 
-        nsecs_t thresholdForNextVsync;
+        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); }
     };
 
     VSyncModulator(Scheduler&, ConnectionHandle appConnectionHandle,
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index f2a7791..a652053 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
+#undef LOG_TAG
+#define LOG_TAG "VSyncReactor"
+//#define LOG_NDEBUG 0
 #include "VSyncReactor.h"
+#include <log/log.h>
 #include "TimeKeeper.h"
 #include "VSyncDispatch.h"
 #include "VSyncTracker.h"
@@ -22,14 +26,98 @@
 namespace android::scheduler {
 
 Clock::~Clock() = default;
+nsecs_t SystemClock::now() const {
+    return systemTime(SYSTEM_TIME_MONOTONIC);
+}
 
 VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
                            std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit)
       : mClock(std::move(clock)),
-        mDispatch(std::move(dispatch)),
         mTracker(std::move(tracker)),
+        mDispatch(std::move(dispatch)),
         mPendingLimit(pendingFenceLimit) {}
 
+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)
+          : mCallback(cb),
+            mRegistration(dispatch,
+                          std::bind(&CallbackRepeater::callback, this, std::placeholders::_1),
+                          std::string(name)),
+            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();
+    }
+
+private:
+    void callback(nsecs_t vsynctime) {
+        nsecs_t period = 0;
+        {
+            std::lock_guard<std::mutex> lk(mMutex);
+            period = mPeriod;
+            mLastCallTime = vsynctime;
+        }
+
+        mCallback->onDispSyncEvent(vsynctime - period);
+
+        {
+            std::lock_guard<std::mutex> lk(mMutex);
+            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; }
+
+    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;
@@ -66,7 +154,7 @@
         mTracker->addVsyncTimestamp(signalTime);
     }
 
-    return false; // TODO(b/144707443): add policy for turning on HWVsync.
+    return mMoreSamplesNeeded;
 }
 
 void VSyncReactor::setIgnorePresentFences(bool ignoration) {
@@ -88,11 +176,9 @@
 }
 
 void VSyncReactor::setPeriod(nsecs_t period) {
-    mTracker->setPeriod(period);
-    {
-        std::lock_guard<std::mutex> lk(mMutex);
-        mPeriodChangeInProgress = true;
-    }
+    std::lock_guard lk(mMutex);
+    mLastHwVsync.reset();
+    mPeriodTransitioningTo = period;
 }
 
 nsecs_t VSyncReactor::getPeriod() {
@@ -103,15 +189,89 @@
 
 void VSyncReactor::endResync() {}
 
+bool VSyncReactor::periodChangeDetected(nsecs_t vsync_timestamp) {
+    if (!mLastHwVsync || !mPeriodTransitioningTo) {
+        return false;
+    }
+    auto const distance = vsync_timestamp - *mLastHwVsync;
+    return std::abs(distance - *mPeriodTransitioningTo) < std::abs(distance - getPeriod());
+}
+
 bool VSyncReactor::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
     assert(periodFlushed);
-    mTracker->addVsyncTimestamp(timestamp);
-    {
-        std::lock_guard<std::mutex> lk(mMutex);
-        *periodFlushed = mPeriodChangeInProgress;
-        mPeriodChangeInProgress = false;
+
+    std::lock_guard<std::mutex> lk(mMutex);
+    if (periodChangeDetected(timestamp)) {
+        mMoreSamplesNeeded = false;
+        *periodFlushed = true;
+
+        mTracker->setPeriod(*mPeriodTransitioningTo);
+        for (auto& entry : mCallbacks) {
+            entry.second->setPeriod(*mPeriodTransitioningTo);
+        }
+
+        mPeriodTransitioningTo.reset();
+        mLastHwVsync.reset();
+    } else if (mPeriodTransitioningTo) {
+        mLastHwVsync = timestamp;
+        mMoreSamplesNeeded = true;
+        *periodFlushed = false;
+    } else {
+        mMoreSamplesNeeded = false;
+        *periodFlushed = false;
     }
-    return 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 = 3;
+        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 {
+    result += "VsyncReactor in use\n"; // TODO (b/144927823): add more information!
+}
+
+void VSyncReactor::reset() {}
+
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 786ee98..73a5e37 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -20,44 +20,68 @@
 #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;
 
 // TODO (b/145217110): consider renaming.
-class VSyncReactor /* TODO (b/140201379): : public android::DispSync */ {
+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);
+    ~VSyncReactor();
 
-    bool addPresentFence(const std::shared_ptr<FenceTime>& fence);
-    void setIgnorePresentFences(bool ignoration);
+    bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final;
+    void setIgnorePresentFences(bool ignoration) final;
 
-    nsecs_t computeNextRefresh(int periodOffset) const;
-    nsecs_t expectedPresentTime();
+    nsecs_t computeNextRefresh(int periodOffset) const final;
+    nsecs_t expectedPresentTime() final;
 
-    void setPeriod(nsecs_t period);
-    nsecs_t getPeriod();
+    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();
-    bool addResyncSample(nsecs_t timestamp, bool* periodFlushed);
-    void endResync();
+    void beginResync() final;
+    bool addResyncSample(nsecs_t timestamp, 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:
+    bool periodChangeDetected(nsecs_t vsync_timestamp) REQUIRES(mMutex);
+
     std::unique_ptr<Clock> const mClock;
-    std::unique_ptr<VSyncDispatch> const mDispatch;
     std::unique_ptr<VSyncTracker> const mTracker;
+    std::unique_ptr<VSyncDispatch> const mDispatch;
     size_t const mPendingLimit;
 
     std::mutex mMutex;
     bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
     std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
-    bool mPeriodChangeInProgress GUARDED_BY(mMutex) = false;
+
+    bool mMoreSamplesNeeded 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);
+};
+
+class SystemClock : public Clock {
+    nsecs_t now() const final;
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 8e94529..8c1d168 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -260,7 +260,6 @@
         mFrameTracer(std::make_unique<FrameTracer>()),
         mEventQueue(mFactory.createMessageQueue()),
         mCompositionEngine(mFactory.createCompositionEngine()),
-        mPhaseOffsets(mFactory.createPhaseOffsets()),
         mPendingSyncInputWindows(false) {}
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
@@ -582,8 +581,6 @@
 void SurfaceFlinger::init() {
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
-    ALOGI("Phase offset: %" PRId64 " ns", mPhaseOffsets->getCurrentAppOffset());
-
     Mutex::Autolock _l(mStateLock);
 
     // Get a RenderEngine for the given display / config (can't fail)
@@ -817,7 +814,7 @@
         info.ydpi = ydpi;
         info.fps = 1e9 / hwConfig->getVsyncPeriod();
 
-        const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(info.fps);
+        const auto offset = mPhaseConfiguration->getOffsetsForRefreshRate(info.fps);
         info.appVsyncOffset = offset.late.app;
 
         // This is how far in advance a buffer must be queued for
@@ -897,8 +894,8 @@
         // DispSync model is locked.
         mVSyncModulator->onRefreshRateChangeInitiated();
 
-        mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
-        mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+        mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+        mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
     }
     mDesiredActiveConfigChanged = true;
 
@@ -910,10 +907,30 @@
 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);
+    status_t result = 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());
+            result = BAD_VALUE;
+        } else if (display->isVirtual()) {
+            ALOGW("Attempt to set allowed display configs for virtual display");
+            result = BAD_VALUE;
+        } else {
+            HwcConfigIndexType config(mode);
+            const auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(config);
+            result = setDesiredDisplayConfigSpecsInternal(display, config, refreshRate.fps,
+                                                          refreshRate.fps);
+        }
+    }));
+
+    return result;
 }
 
 void SurfaceFlinger::setActiveConfigInternal() {
@@ -931,13 +948,16 @@
 
     auto refreshRate =
             mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
-    mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
-    mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+    mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+    mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
     ATRACE_INT("ActiveConfigFPS", refreshRate.fps);
 
     if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
+        const nsecs_t vsyncPeriod =
+                mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId)
+                        .vsyncPeriod;
         mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
-                                    mUpcomingActiveConfig.configId);
+                                    mUpcomingActiveConfig.configId, vsyncPeriod);
     }
 }
 
@@ -949,8 +969,8 @@
     mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
     auto refreshRate =
             mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
-    mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
-    mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+    mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+    mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
 }
 
 bool SurfaceFlinger::performSetActiveConfig() {
@@ -1111,6 +1131,86 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
+                                                      bool* outSupport) const {
+    Mutex::Autolock _l(mStateLock);
+
+    if (!displayToken) {
+        ALOGE("getAutoLowLatencyModeSupport() failed. Missing display token.");
+        return BAD_VALUE;
+    }
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        ALOGE("getAutoLowLatencyModeSupport() failed. Display id for display token %p not found.",
+              displayToken.get());
+        return NAME_NOT_FOUND;
+    }
+    *outSupport = getHwComposer().hasDisplayCapability(displayId,
+                                                       HWC2::DisplayCapability::AutoLowLatencyMode);
+    return NO_ERROR;
+}
+
+void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
+    postMessageAsync(new LambdaMessage([=] { setAutoLowLatencyModeInternal(displayToken, on); }));
+}
+
+void SurfaceFlinger::setAutoLowLatencyModeInternal(const sp<IBinder>& displayToken, bool on) {
+    if (!displayToken) {
+        ALOGE("setAutoLowLatencyMode() failed. Missing display token.");
+        return;
+    }
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        ALOGE("setAutoLowLatencyMode() failed. Display id for display token %p not found.",
+              displayToken.get());
+        return;
+    }
+
+    getHwComposer().setAutoLowLatencyMode(*displayId, on);
+}
+
+status_t SurfaceFlinger::getGameContentTypeSupport(const sp<IBinder>& displayToken,
+                                                   bool* outSupport) const {
+    Mutex::Autolock _l(mStateLock);
+
+    if (!displayToken) {
+        ALOGE("getGameContentTypeSupport() failed. Missing display token.");
+        return BAD_VALUE;
+    }
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        ALOGE("getGameContentTypeSupport() failed. Display id for display token %p not found.",
+              displayToken.get());
+        return NAME_NOT_FOUND;
+    }
+
+    std::vector<HWC2::ContentType> outSupportedContentTypes;
+    getHwComposer().getSupportedContentTypes(*displayId, &outSupportedContentTypes);
+    *outSupport = std::find(outSupportedContentTypes.begin(), outSupportedContentTypes.end(),
+                            HWC2::ContentType::Game) != outSupportedContentTypes.end();
+    return NO_ERROR;
+}
+
+void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
+    postMessageAsync(new LambdaMessage([=] { setGameContentTypeInternal(displayToken, on); }));
+}
+
+void SurfaceFlinger::setGameContentTypeInternal(const sp<IBinder>& displayToken, bool on) {
+    if (!displayToken) {
+        ALOGE("setGameContentType() failed. Missing display token.");
+        return;
+    }
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        ALOGE("setGameContentType() failed. Display id for display token %p not found.",
+              displayToken.get());
+        return;
+    }
+
+    const HWC2::ContentType type = on ? HWC2::ContentType::Game : HWC2::ContentType::None;
+    getHwComposer().setContentType(*displayId, type);
+}
+
 status_t SurfaceFlinger::clearAnimationFrameStats() {
     Mutex::Autolock _l(mStateLock);
     mAnimFrameTracker.clearStats();
@@ -1413,7 +1513,7 @@
 }
 
 bool SurfaceFlinger::isDisplayConfigAllowed(HwcConfigIndexType configId) const {
-    return mAllowedDisplayConfigs.empty() || mAllowedDisplayConfigs.count(configId);
+    return mRefreshRateConfigs->isConfigAllowed(configId);
 }
 
 void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate,
@@ -1598,10 +1698,8 @@
     // 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];
+    const sp<Fence>& fence = mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
+                                                                  : mPreviousPresentFences[1];
 
     if (fence == Fence::NO_FENCE) {
         return false;
@@ -1619,10 +1717,8 @@
     mScheduler->getDisplayStatInfo(&stats);
     const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime();
     // Inflate the expected present time if we're targetting the next vsync.
-    mExpectedPresentTime.store(mVSyncModulator->getOffsets().sf <
-                                               mPhaseOffsets->getOffsetThresholdForNextVsync()
-                                       ? presentTime
-                                       : presentTime + stats.vsyncPeriod);
+    mExpectedPresentTime.store(
+            mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod);
 }
 
 void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
@@ -1866,11 +1962,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;
     }
@@ -1879,8 +1976,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 =
@@ -2029,6 +2126,7 @@
     // 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());
     }
 }
@@ -2571,28 +2669,39 @@
                                                           currentConfig, HWC_POWER_MODE_OFF);
     mRefreshRateStats->setConfigMode(currentConfig);
 
+    mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs);
+
     // start the EventThread
     mScheduler =
             getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
                                          *mRefreshRateConfigs, *this);
     mAppConnectionHandle =
-            mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
-                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
+            mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
                                          impl::EventThread::InterceptVSyncsCallback());
     mSfConnectionHandle =
-            mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
-                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
+            mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
                                          [this](nsecs_t timestamp) {
                                              mInterceptor->saveVSyncEvent(timestamp);
                                          });
 
     mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
     mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
-                            mPhaseOffsets->getCurrentOffsets());
+                            mPhaseConfiguration->getCurrentOffsets());
 
     mRegionSamplingThread =
             new RegionSamplingThread(*this, *mScheduler,
                                      RegionSamplingThread::EnvironmentTimingTunables());
+    // 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).vsyncPeriod;
+    mScheduler->onConfigChanged(mAppConnectionHandle, primaryDisplayId.value, currentConfig,
+                                vsyncPeriod);
 }
 
 void SurfaceFlinger::commitTransaction()
@@ -3989,23 +4098,18 @@
     mRefreshRateStats->dump(result);
     result.append("\n");
 
-    mPhaseOffsets->dump(result);
+    mPhaseConfiguration->dump(result);
     StringAppendF(&result,
                   "      present offset: %9" PRId64 " ns\t     VSYNC period: %9" PRId64 " ns\n\n",
                   dispSyncPresentTimeOffset, getVsyncPeriod());
 
-    StringAppendF(&result, "Allowed Display Configs: ");
-    for (auto configId : mAllowedDisplayConfigs) {
-        StringAppendF(&result, "%" PRIu32 " Hz, ",
-                      static_cast<int32_t>(
-                              mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps));
-    }
+    HwcConfigIndexType defaultConfig;
+    float minFps, maxFps;
+    mRefreshRateConfigs->getPolicy(&defaultConfig, &minFps, &maxFps);
     StringAppendF(&result,
-                  "DesiredDisplayConfigSpecs: default config ID: %" PRIu32
+                  "DesiredDisplayConfigSpecs: default config ID: %d"
                   ", min: %.2f Hz, max: %.2f Hz",
-                  mDesiredDisplayConfigSpecs.defaultModeId,
-                  mDesiredDisplayConfigSpecs.minRefreshRate,
-                  mDesiredDisplayConfigSpecs.maxRefreshRate);
+                  defaultConfig.value(), minFps, maxFps);
     StringAppendF(&result, "(config override by backdoor: %s)\n\n",
                   mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
 
@@ -4433,11 +4537,13 @@
         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:
@@ -4843,15 +4949,15 @@
                 return NO_ERROR;
             }
             case 1034: {
-                // TODO(b/129297325): expose this via developer menu option
                 n = data.readInt32();
-                if (n && !mRefreshRateOverlay &&
-                    mRefreshRateConfigs->refreshRateSwitchingSupported()) {
+                if (n == 1 && !mRefreshRateOverlay) {
                     mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
                     auto current = mRefreshRateConfigs->getCurrentRefreshRate();
                     mRefreshRateOverlay->changeRefreshRate(current);
-                } else if (!n) {
+                } else if (n == 0) {
                     mRefreshRateOverlay.reset();
+                } else {
+                    reply->writeBool(mRefreshRateOverlay != nullptr);
                 }
                 return NO_ERROR;
             }
@@ -4860,7 +4966,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;
                     }
@@ -5456,8 +5562,12 @@
     }
 }
 
-void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
-                                                      const std::vector<int32_t>& allowedConfigs) {
+status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal(const sp<DisplayDevice>& display,
+                                                              HwcConfigIndexType defaultConfig,
+                                                              float minRefreshRate,
+                                                              float maxRefreshRate) {
+    Mutex::Autolock lock(mStateLock);
+
     if (!display->isPrimary()) {
         // 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
@@ -5471,51 +5581,47 @@
         constraints.seamlessRequired = false;
 
         HWC2::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
-        getHwComposer().setActiveConfigWithConstraints(*displayId, allowedConfigs[0], constraints,
-                                                       &timeline);
+        if (getHwComposer().setActiveConfigWithConstraints(*displayId, defaultConfig.value(),
+                                                           constraints, &timeline) < 0) {
+            return BAD_VALUE;
+        }
         if (timeline.refreshRequired) {
             repaintEverythingForHWC();
         }
 
-        auto configId = HwcConfigIndexType(allowedConfigs[0]);
+        auto configId = HwcConfigIndexType(defaultConfig);
         display->setActiveConfig(configId);
-        mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, configId);
-        return;
+        const nsecs_t vsyncPeriod =
+                mRefreshRateConfigs->getRefreshRateFromConfigId(configId).vsyncPeriod;
+        mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, configId,
+                                    vsyncPeriod);
+        return NO_ERROR;
     }
 
-    const auto allowedDisplayConfigs = DisplayConfigs(allowedConfigs.begin(),
-                                                      allowedConfigs.end());
-    if (allowedDisplayConfigs == mAllowedDisplayConfigs) {
-        return;
+    if (mDebugDisplayConfigSetByBackdoor) {
+        // ignore this request as config is overridden by backdoor
+        return NO_ERROR;
     }
 
-    ALOGV("Updating allowed configs");
-    mAllowedDisplayConfigs = std::move(allowedDisplayConfigs);
+    bool policyChanged;
+    if (mRefreshRateConfigs->setPolicy(defaultConfig, minRefreshRate, maxRefreshRate,
+                                       &policyChanged) < 0) {
+        return BAD_VALUE;
+    }
+    if (!policyChanged) {
+        return NO_ERROR;
+    }
+
+    ALOGV("Setting desired display config specs: defaultConfig: %d min: %.f max: %.f",
+          defaultConfig.value(), minRefreshRate, maxRefreshRate);
 
     // 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
+    const nsecs_t vsyncPeriod =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig()).vsyncPeriod;
     mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
-                                display->getActiveConfig());
-
-    // Prepare the parameters needed for RefreshRateConfigs::setPolicy. This will change to just
-    // passthrough once DisplayManager provide these parameters directly.
-    const auto refreshRate =
-            mRefreshRateConfigs->getRefreshRateFromConfigId(HwcConfigIndexType(allowedConfigs[0]));
-    const auto defaultModeId = refreshRate.configId;
-    auto minRefreshRateFps = refreshRate.fps;
-    auto maxRefreshRateFps = minRefreshRateFps;
-
-    for (auto config : allowedConfigs) {
-        const auto configRefreshRate =
-                mRefreshRateConfigs->getRefreshRateFromConfigId(HwcConfigIndexType(config));
-        if (configRefreshRate.fps < minRefreshRateFps) {
-            minRefreshRateFps = configRefreshRate.fps;
-        } else if (configRefreshRate.fps > maxRefreshRateFps) {
-            maxRefreshRateFps = configRefreshRate.fps;
-        }
-    }
-    mRefreshRateConfigs->setPolicy(defaultModeId, minRefreshRateFps, maxRefreshRateFps);
+                                display->getActiveConfig(), vsyncPeriod);
 
     if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
         auto configId = mScheduler->getPreferredConfigId();
@@ -5535,70 +5641,15 @@
                                     Scheduler::ConfigEvent::Changed});
         }
     } else {
-        if (!allowedConfigs.empty()) {
-            ALOGV("switching to config %d", allowedConfigs[0]);
-            auto configId = HwcConfigIndexType(allowedConfigs[0]);
-            setDesiredActiveConfig({configId, Scheduler::ConfigEvent::Changed});
-        }
-    }
-}
-
-status_t SurfaceFlinger::setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                                  const std::vector<int32_t>& allowedConfigs) {
-    ATRACE_CALL();
-
-    if (!displayToken || allowedConfigs.empty()) {
-        return BAD_VALUE;
-    }
-
-    if (mDebugDisplayConfigSetByBackdoor) {
-        // ignore this request as config is overridden by backdoor
-        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);
-        }
-    }));
-
-    return NO_ERROR;
-}
-
-status_t SurfaceFlinger::getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                                  std::vector<int32_t>* outAllowedConfigs) {
-    ATRACE_CALL();
-
-    if (!displayToken || !outAllowedConfigs) {
-        return BAD_VALUE;
-    }
-
-    Mutex::Autolock lock(mStateLock);
-
-    const auto display = getDisplayDeviceLocked(displayToken);
-    if (!display) {
-        return NAME_NOT_FOUND;
-    }
-
-    if (display->isPrimary()) {
-        outAllowedConfigs->reserve(mAllowedDisplayConfigs.size());
-        for (auto configId : mAllowedDisplayConfigs) {
-            outAllowedConfigs->push_back(configId.value());
-        }
+        ALOGV("switching to config %d", defaultConfig.value());
+        setDesiredActiveConfig({defaultConfig, Scheduler::ConfigEvent::Changed});
     }
 
     return NO_ERROR;
 }
 
 status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                      int32_t defaultModeId, float minRefreshRate,
+                                                      int32_t defaultConfig, float minRefreshRate,
                                                       float maxRefreshRate) {
     ATRACE_CALL();
 
@@ -5606,39 +5657,34 @@
         return BAD_VALUE;
     }
 
+    status_t result = NO_ERROR;
+
     postMessageSync(new LambdaMessage([&]() {
         const auto display = getDisplayDeviceLocked(displayToken);
         if (!display) {
+            result = BAD_VALUE;
             ALOGE("Attempt to set desired display configs for invalid display token %p",
                   displayToken.get());
         } else if (display->isVirtual()) {
+            result = BAD_VALUE;
             ALOGW("Attempt to set desired display configs for virtual display");
         } else {
-            // TODO(b/142507213): Plug through to HWC once the interface is ready.
-            Mutex::Autolock lock(mStateLock);
-            const DesiredDisplayConfigSpecs desiredDisplayConfigSpecs = {defaultModeId,
-                                                                         minRefreshRate,
-                                                                         maxRefreshRate};
-            if (desiredDisplayConfigSpecs == mDesiredDisplayConfigSpecs) {
-                return;
-            }
-            ALOGV("Updating desired display configs");
-            ALOGD("desiredDisplayConfigSpecs: defaultId: %d min: %.f max: %.f decisions: ",
-                  desiredDisplayConfigSpecs.defaultModeId, desiredDisplayConfigSpecs.minRefreshRate,
-                  desiredDisplayConfigSpecs.maxRefreshRate);
-            mDesiredDisplayConfigSpecs = desiredDisplayConfigSpecs;
+            result =
+                    setDesiredDisplayConfigSpecsInternal(display, HwcConfigIndexType(defaultConfig),
+                                                         minRefreshRate, maxRefreshRate);
         }
     }));
-    return NO_ERROR;
+
+    return result;
 }
 
 status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                                      int32_t* outDefaultModeId,
+                                                      int32_t* outDefaultConfig,
                                                       float* outMinRefreshRate,
                                                       float* outMaxRefreshRate) {
     ATRACE_CALL();
 
-    if (!displayToken || !outDefaultModeId || !outMinRefreshRate || !outMaxRefreshRate) {
+    if (!displayToken || !outDefaultConfig || !outMinRefreshRate || !outMaxRefreshRate) {
         return BAD_VALUE;
     }
 
@@ -5649,12 +5695,23 @@
     }
 
     if (display->isPrimary()) {
-        *outDefaultModeId = mDesiredDisplayConfigSpecs.defaultModeId;
-        *outMinRefreshRate = mDesiredDisplayConfigSpecs.minRefreshRate;
-        *outMaxRefreshRate = mDesiredDisplayConfigSpecs.maxRefreshRate;
+        HwcConfigIndexType defaultConfig;
+        mRefreshRateConfigs->getPolicy(&defaultConfig, outMinRefreshRate, outMaxRefreshRate);
+        *outDefaultConfig = defaultConfig.value();
+        return NO_ERROR;
+    } else if (display->isVirtual()) {
+        return BAD_VALUE;
+    } else {
+        const auto displayId = display->getId();
+        if (!displayId) {
+            return BAD_VALUE;
+        }
+        *outDefaultConfig = getHwComposer().getActiveConfigIndex(*displayId);
+        auto vsyncPeriod = getHwComposer().getActiveConfig(*displayId)->getVsyncPeriod();
+        *outMinRefreshRate = 1e9f / vsyncPeriod;
+        *outMaxRefreshRate = 1e9f / vsyncPeriod;
+        return NO_ERROR;
     }
-
-    return NO_ERROR;
 }
 
 void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() {
@@ -5662,7 +5719,10 @@
 }
 
 sp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
-    BBinder *b = handle->localBinder();
+    BBinder* b = nullptr;
+    if (handle) {
+        b = handle->localBinder();
+    }
     if (b == nullptr) {
         return nullptr;
     }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 53b8908..2f84b13 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -384,17 +384,6 @@
         void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
     };
 
-    struct DesiredDisplayConfigSpecs {
-        int32_t defaultModeId;
-        float minRefreshRate;
-        float maxRefreshRate;
-
-        bool operator==(const DesiredDisplayConfigSpecs& other) const {
-            return defaultModeId == other.defaultModeId && minRefreshRate == other.minRefreshRate &&
-                    maxRefreshRate == other.maxRefreshRate;
-        }
-    };
-
     /* ------------------------------------------------------------------------
      * IBinder interface
      */
@@ -449,6 +438,12 @@
                                        ui::DisplayPrimaries &primaries);
     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;
@@ -478,14 +473,10 @@
     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 minRefreshRate, float maxRefreshRate) override;
     status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
-                                          int32_t* outDefaultModeId, float* outMinRefreshRate,
+                                          int32_t* outDefaultConfig, float* outMinRefreshRate,
                                           float* outMaxRefreshRate) override;
     status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
                                          bool* outSupport) const override;
@@ -559,10 +550,16 @@
     // 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)
-            REQUIRES(mStateLock);
+    // Sets the desired display configs.
+    status_t setDesiredDisplayConfigSpecsInternal(const sp<DisplayDevice>& display,
+                                                  HwcConfigIndexType defaultConfig,
+                                                  float minRefreshRate, float maxRefreshRate)
+            EXCLUDES(mStateLock);
+
+    // called on the main thread in response to setAutoLowLatencyMode()
+    void setAutoLowLatencyModeInternal(const sp<IBinder>& displayToken, bool on);
+    // called on the main thread in response to setGameContentType()
+    void setGameContentTypeInternal(const sp<IBinder>& displayToken, bool on);
 
     // Returns whether the transaction actually modified any state
     bool handleMessageTransaction();
@@ -1130,7 +1127,7 @@
     scheduler::ConnectionHandle mSfConnectionHandle;
 
     // Stores phase offsets configured per refresh rate.
-    const std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
+    std::unique_ptr<scheduler::PhaseConfiguration> mPhaseConfiguration;
 
     // Optional to defer construction until scheduler connections are created.
     std::optional<scheduler::VSyncModulator> mVSyncModulator;
@@ -1140,11 +1137,6 @@
 
     std::atomic<nsecs_t> mExpectedPresentTime = 0;
 
-    // All configs are allowed if the set is empty.
-    using DisplayConfigs = std::set<HwcConfigIndexType>;
-    DisplayConfigs mAllowedDisplayConfigs GUARDED_BY(mStateLock);
-    DesiredDisplayConfigSpecs mDesiredDisplayConfigSpecs GUARDED_BY(mStateLock);
-
     std::mutex mActiveConfigLock;
     // This bit is set once we start setting the config. We read from this bit during the
     // process. If at the end, this bit is different than mDesiredActiveConfig, we restart
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index bd4cdba..d5c2306 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <compositionengine/impl/CompositionEngine.h>
+#include <cutils/properties.h>
 #include <ui/GraphicBuffer.h>
 
 #include "BufferLayerConsumer.h"
@@ -51,16 +52,20 @@
 }
 
 std::unique_ptr<HWComposer> DefaultFactory::createHWComposer(const std::string& serviceName) {
-    return std::make_unique<android::impl::HWComposer>(
-            std::make_unique<Hwc2::impl::Composer>(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::PhaseOffsets> DefaultFactory::createPhaseOffsets() {
-    return std::make_unique<scheduler::impl::PhaseOffsets>();
+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(
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 1a24448..36fae21 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -30,7 +30,8 @@
     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::PhaseOffsets> createPhaseOffsets() override;
+    std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+            const scheduler::RefreshRateConfigs&) override;
     std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
                                                const scheduler::RefreshRateConfigs&,
                                                ISchedulerCallback&) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 0db941d..951bd09 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -56,7 +56,7 @@
 } // namespace compositionengine
 
 namespace scheduler {
-class PhaseOffsets;
+class PhaseConfiguration;
 class RefreshRateConfigs;
 } // namespace scheduler
 
@@ -74,7 +74,8 @@
     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::PhaseConfiguration> createPhaseConfiguration(
+            const scheduler::RefreshRateConfigs&) = 0;
     virtual std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
                                                        const scheduler::RefreshRateConfigs&,
                                                        ISchedulerCallback&) = 0;
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 7e6c472..5e8910a 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -124,6 +124,7 @@
     addRelativeParentLocked(transaction, layerId,
                             getLayerIdFromWeakRef(layer->mCurrentState.zOrderRelativeOf),
                             layer->mCurrentState.z);
+    addShadowRadiusLocked(transaction, layerId, layer->mCurrentState.shadowRadius);
 }
 
 void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment,
@@ -368,6 +369,13 @@
     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)
 {
@@ -441,6 +449,9 @@
         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,
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 72b734b..c6f9e8a 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -165,6 +165,7 @@
     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);
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 8db03db..4c1baaf 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -311,8 +311,16 @@
                     interface_cast<ITransactionCompletedListener>(listenerStats.listener)
                             ->onTransactionCompleted(listenerStats);
                     listener->unlinkToDeath(mDeathRecipient);
+                    if (transactionStatsDeque.empty()) {
+                        completedTransactionsItr =
+                                mCompletedTransactions.erase(completedTransactionsItr);
+                    } else {
+                        completedTransactionsItr++;
+                    }
+                } else {
+                    completedTransactionsItr =
+                            mCompletedTransactions.erase(completedTransactionsItr);
                 }
-                completedTransactionsItr = mCompletedTransactions.erase(completedTransactionsItr);
             } else {
                 completedTransactionsItr++;
             }
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index ef488bd..ef27847 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -112,7 +112,8 @@
         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;
 }
 
@@ -307,8 +308,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 54e02ca..774b0e1 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -111,6 +111,8 @@
         bool isProtected;
         float cornerRadius;
         LayerMetadata metadata;
+        LayerProtoParser::FloatRect cornerRadiusCrop;
+        float shadowRadius;
 
         std::string to_string() const;
     };
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index c7fbff3..23df1bb 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -94,6 +94,14 @@
   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;
 }
 
 message PositionProto {
@@ -174,3 +182,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;
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc
index aea602b..d3942e8 100644
--- a/services/surfaceflinger/surfaceflinger.rc
+++ b/services/surfaceflinger/surfaceflinger.rc
@@ -2,6 +2,7 @@
     class core animation
     user system
     group graphics drmrpc readproc
+    capabilities SYS_NICE
     onrestart restart zygote
     writepid /dev/stune/foreground/tasks
     socket pdx/system/vr/display/client     stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index ca7e18d..0403237 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -21,7 +21,6 @@
         "CommonTypes_test.cpp",
         "Credentials_test.cpp",
         "DereferenceSurfaceControl_test.cpp",
-        "DisplayActiveConfig_test.cpp",
         "DisplayConfigs_test.cpp",
         "InvalidHandles_test.cpp",
         "LayerCallback_test.cpp",
@@ -43,6 +42,7 @@
         "libtrace_proto",
     ],
     shared_libs: [
+        "android.hardware.graphics.common-ndk_platform",
         "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.composer@2.1",
         "libandroid",
@@ -53,12 +53,15 @@
         "libgui",
         "liblayers_proto",
         "liblog",
+        "libnativewindow",
         "libprotobuf-cpp-full",
         "libtimestats_proto",
         "libui",
         "libutils",
-        "vintf-graphics-common-ndk_platform",
-    ]
+    ],
+    header_libs: [
+        "libnativewindow_headers",
+    ],
 }
 
 cc_defaults {
diff --git a/services/surfaceflinger/tests/CommonTypes_test.cpp b/services/surfaceflinger/tests/CommonTypes_test.cpp
index a3e16f9..25b4615 100644
--- a/services/surfaceflinger/tests/CommonTypes_test.cpp
+++ b/services/surfaceflinger/tests/CommonTypes_test.cpp
@@ -16,6 +16,7 @@
 #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>
 
@@ -34,6 +35,51 @@
 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) ==
@@ -100,21 +146,21 @@
               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::V0_SRGB_LINEAR) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::SRGB_LINEAR) ==
               static_cast<uint32_t>(HidlDataspace::V0_SRGB_LINEAR));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_SCRGB_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::V0_SRGB) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::SRGB) ==
               static_cast<uint32_t>(HidlDataspace::V0_SRGB));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_SCRGB) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::SCRGB) ==
               static_cast<uint32_t>(HidlDataspace::V0_SCRGB));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_JFIF) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::JFIF) ==
               static_cast<uint32_t>(HidlDataspace::V0_JFIF));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT601_625) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::BT601_625) ==
               static_cast<uint32_t>(HidlDataspace::V0_BT601_625));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT601_525) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::BT601_525) ==
               static_cast<uint32_t>(HidlDataspace::V0_BT601_525));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT709) ==
+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));
@@ -152,19 +198,3 @@
               static_cast<uint32_t>(HidlDataspace::JPEG_APP_SEGMENTS));
 static_assert(static_cast<uint32_t>(AidlDataspace::HEIF) ==
               static_cast<uint32_t>(HidlDataspace::HEIF));
-
-// Below are the dataspaces that have been deprecated for sometime. They are required to behave
-// the same as their V0_* counterparts. We redefined them in AIDL to be the same as the
-// their V0_* counterparts.
-static_assert(static_cast<uint32_t>(AidlDataspace::SRGB_LINEAR) ==
-              static_cast<uint32_t>(AidlDataspace::V0_SRGB_LINEAR));
-static_assert(static_cast<uint32_t>(AidlDataspace::SRGB) ==
-              static_cast<uint32_t>(AidlDataspace::V0_SRGB));
-static_assert(static_cast<uint32_t>(AidlDataspace::JFIF) ==
-              static_cast<uint32_t>(AidlDataspace::V0_JFIF));
-static_assert(static_cast<uint32_t>(AidlDataspace::BT601_625) ==
-              static_cast<uint32_t>(AidlDataspace::V0_BT601_625));
-static_assert(static_cast<uint32_t>(AidlDataspace::BT601_525) ==
-              static_cast<uint32_t>(AidlDataspace::V0_BT601_525));
-static_assert(static_cast<uint32_t>(AidlDataspace::BT709) ==
-              static_cast<uint32_t>(AidlDataspace::V0_BT709));
diff --git a/services/surfaceflinger/tests/DisplayActiveConfig_test.cpp b/services/surfaceflinger/tests/DisplayActiveConfig_test.cpp
deleted file mode 100644
index 2e3b760..0000000
--- a/services/surfaceflinger/tests/DisplayActiveConfig_test.cpp
+++ /dev/null
@@ -1,85 +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 <thread>
-#include "LayerTransactionTest.h"
-namespace android {
-
-using android::hardware::graphics::common::V1_1::BufferUsage;
-
-::testing::Environment* const binderEnv =
-        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
-
-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());
-}
-} // namespace android
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
index 420fb29..d51b9a1 100644
--- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -31,42 +31,40 @@
     void SetUp() override { mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); }
 
     sp<IBinder> mDisplayToken;
-    int32_t defaultConfigId;
-    float minRefreshRate;
-    float maxRefreshRate;
 };
 
-TEST_F(RefreshRateRangeTest, simpleSetAndGet) {
-    status_t res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 1, 45, 75);
-    EXPECT_EQ(res, NO_ERROR);
+TEST_F(RefreshRateRangeTest, setAllConfigs) {
+    int32_t initialDefaultConfig;
+    float initialMin;
+    float initialMax;
+    status_t res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken,
+                                                                       &initialDefaultConfig,
+                                                                       &initialMin, &initialMax);
+    ASSERT_EQ(res, NO_ERROR);
 
-    res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfigId,
-                                                              &minRefreshRate, &maxRefreshRate);
-    EXPECT_EQ(res, NO_ERROR);
-    EXPECT_EQ(defaultConfigId, 1);
-    EXPECT_EQ(minRefreshRate, 45);
-    EXPECT_EQ(maxRefreshRate, 75);
+    Vector<DisplayInfo> configs;
+    res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs);
+    ASSERT_EQ(res, NO_ERROR);
+
+    for (size_t i = 0; i < configs.size(); i++) {
+        res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, i, configs[i].fps,
+                                                                  configs[i].fps);
+        ASSERT_EQ(res, NO_ERROR);
+
+        int defaultConfig;
+        float minFps;
+        float maxFps;
+        res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
+                                                                  &minFps, &maxFps);
+        ASSERT_EQ(res, NO_ERROR);
+        ASSERT_EQ(defaultConfig, i);
+        ASSERT_EQ(minFps, configs[i].fps);
+        ASSERT_EQ(maxFps, configs[i].fps);
+    }
+
+    res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, initialDefaultConfig,
+                                                              initialMin, initialMax);
+    ASSERT_EQ(res, NO_ERROR);
 }
 
-TEST_F(RefreshRateRangeTest, complexSetAndGet) {
-    status_t res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 1, 45, 75);
-    EXPECT_EQ(res, NO_ERROR);
-
-    res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfigId,
-                                                              &minRefreshRate, &maxRefreshRate);
-    EXPECT_EQ(res, NO_ERROR);
-    EXPECT_EQ(defaultConfigId, 1);
-    EXPECT_EQ(minRefreshRate, 45);
-    EXPECT_EQ(maxRefreshRate, 75);
-
-    // Second call overrides the first one.
-    res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 10, 145, 875);
-    EXPECT_EQ(res, NO_ERROR);
-    res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfigId,
-                                                              &minRefreshRate, &maxRefreshRate);
-    EXPECT_EQ(res, NO_ERROR);
-    EXPECT_EQ(defaultConfigId, 10);
-    EXPECT_EQ(minRefreshRate, 145);
-    EXPECT_EQ(maxRefreshRate, 875);
-}
 } // namespace android
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 999e82d..627de7a 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -72,8 +72,8 @@
     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
+    // 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();
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index b196684..2bedd7d 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.*:SetGeometryTest.*"
+            "filter": "*:-LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/3:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/3"
         }
 }
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 59e9c00..1fa426d 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -51,6 +51,7 @@
 constexpr float CORNER_RADIUS_UPDATE = 0.2f;
 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_BG_SURFACE_NAME = "BG Interceptor Test Surface";
@@ -190,6 +191,7 @@
     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
@@ -226,6 +228,7 @@
     void relativeParentUpdate(Transaction&);
     void detachChildrenUpdate(Transaction&);
     void reparentChildrenUpdate(Transaction&);
+    void shadowRadiusUpdate(Transaction&);
     void surfaceCreation(Transaction&);
     void displayCreation(Transaction&);
     void displayDeletion(Transaction&);
@@ -406,6 +409,10 @@
     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);
@@ -435,6 +442,7 @@
     runInTransaction(&SurfaceInterceptorTest::reparentChildrenUpdate);
     runInTransaction(&SurfaceInterceptorTest::detachChildrenUpdate);
     runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate);
+    runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate);
 }
 
 void SurfaceInterceptorTest::surfaceCreation(Transaction&) {
@@ -655,6 +663,17 @@
     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;
@@ -718,6 +737,9 @@
                         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;
                     }
@@ -920,6 +942,11 @@
                 SurfaceChange::SurfaceChangeCase::kDetachChildren);
 }
 
+TEST_F(SurfaceInterceptorTest, InterceptShadowRadiusUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::shadowRadiusUpdate,
+                SurfaceChange::SurfaceChangeCase::kShadowRadius);
+}
+
 TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
     captureTest(&SurfaceInterceptorTest::runAllUpdates,
                 &SurfaceInterceptorTest::assertAllUpdatesFound);
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index 6d79615..0c370a6 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -760,6 +760,22 @@
     return V2_4::Error::UNSUPPORTED;
 }
 
+V2_4::Error FakeComposerClient::setAutoLowLatencyMode(Display, bool) {
+    ALOGV("setAutoLowLatencyMode");
+    return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error FakeComposerClient::getSupportedContentTypes(
+        Display, std::vector<IComposerClient::ContentType>*) {
+    ALOGV("getSupportedContentTypes");
+    return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error FakeComposerClient::setContentType(Display, IComposerClient::ContentType) {
+    ALOGV("setContentType");
+    return V2_4::Error::UNSUPPORTED;
+}
+
 //////////////////////////////////////////////////////////////////
 
 void FakeComposerClient::requestVSync(uint64_t vsyncTime) {
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
index 2a08b9b..f9ff2bf 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -246,6 +246,11 @@
             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;
 
     void setClient(ComposerClient* client);
 
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index ccfa6c5..68adbfc 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -45,6 +45,7 @@
         "OneShotTimerTest.cpp",
         "LayerHistoryTest.cpp",
         "LayerMetadataTest.cpp",
+        "PhaseOffsetsTest.cpp",
         "SchedulerTest.cpp",
         "SchedulerUtilsTest.cpp",
         "RefreshRateConfigsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 04991f9..cce21ce 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -361,7 +361,7 @@
 
     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));
@@ -1154,8 +1154,10 @@
     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();
         }
     }
 };
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 0aa8cf5..2e705da 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -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;
 };
 
@@ -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/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 80bca02..61d4f47 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -85,7 +85,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;
@@ -209,13 +210,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 {
@@ -450,18 +453,18 @@
 }
 
 TEST_F(EventThreadTest, postConfigChangedPrimary) {
-    mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(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, HwcConfigIndexType(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, HwcConfigIndexType(7));
-    expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7);
+    mThread->onConfigChanged(DISPLAY_ID_64BIT, HwcConfigIndexType(7), 16666666);
+    expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666);
 }
 
 TEST_F(EventThreadTest, suppressConfigChanged) {
@@ -470,8 +473,8 @@
             createConnection(suppressConnectionEventRecorder,
                              ISurfaceComposer::eConfigChangedSuppress);
 
-    mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(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 da4eea0..b50ddf5 100644
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -22,7 +22,7 @@
 
 namespace android::scheduler {
 
-struct FakePhaseOffsets : PhaseOffsets {
+struct FakePhaseOffsets : PhaseConfiguration {
     static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
 
     Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
@@ -30,8 +30,7 @@
     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},
-                FAKE_PHASE_OFFSET_NS};
+                {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
     }
 
     void setRefreshRateFps(float) override {}
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
new file mode 100644
index 0000000..6360ec1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -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.
+ */
+
+#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 PhaseOffsetsTest : public testing::Test {
+protected:
+    PhaseOffsetsTest()
+          : mPhaseOffsets(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
+                          38'000'000) {}
+
+    ~PhaseOffsetsTest() = default;
+
+    TestablePhaseOffsetsAsDurations mPhaseOffsets;
+};
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) {
+    mPhaseOffsets.setRefreshRateFps(60.0f);
+    auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
+    auto offsets = mPhaseOffsets.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, 500'001);
+
+    EXPECT_EQ(offsets.earlyGl.sf, 3'166'667);
+
+    EXPECT_EQ(offsets.earlyGl.app, 15'166'668);
+}
+
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_90Hz) {
+    mPhaseOffsets.setRefreshRateFps(90.0f);
+    auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
+    auto offsets = mPhaseOffsets.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, 6'055'555);
+
+    EXPECT_EQ(offsets.earlyGl.sf, -2'388'889);
+
+    EXPECT_EQ(offsets.earlyGl.app, 4'055'555);
+}
+
+TEST_F(PhaseOffsetsTest, 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);
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 546e65c..cc3c985 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -39,6 +39,7 @@
     static inline const HwcConfigGroupType HWC_GROUP_ID_0 = HwcConfigGroupType(0);
     static inline const HwcConfigGroupType HWC_GROUP_ID_1 = HwcConfigGroupType(1);
     static constexpr int64_t VSYNC_60 = 16666667;
+    static constexpr int64_t VSYNC_60_POINT_4 = 16666665;
     static constexpr int64_t VSYNC_90 = 11111111;
 
     RefreshRateConfigsTest();
@@ -79,6 +80,16 @@
     ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported());
 }
 
+TEST_F(RefreshRateConfigsTest, invalidPolicy) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+                                                 /*currentConfigId=*/HWC_CONFIG_ID_60);
+    ASSERT_LT(refreshRateConfigs->setPolicy(HwcConfigIndexType(10), 60, 60, nullptr), 0);
+    ASSERT_LT(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 20, 40, nullptr), 0);
+}
+
 TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
     std::vector<RefreshRateConfigs::InputConfig> configs{
             {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
@@ -123,7 +134,7 @@
     ASSERT_EQ(expectedDefaultConfig, minRate60);
     ASSERT_EQ(expectedDefaultConfig, performanceRate60);
 
-    refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 60, 90);
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 60, 90, nullptr), 0);
     refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
 
     ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
@@ -154,7 +165,7 @@
                                              90};
     ASSERT_EQ(expectedPerformanceConfig, performanceRate);
 
-    refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60);
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
     ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
 
     auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
@@ -181,7 +192,7 @@
         EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
     }
 
-    refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 90, 90);
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
     {
         auto current = refreshRateConfigs->getCurrentRefreshRate();
         EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
@@ -207,20 +218,20 @@
     ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
     ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
 
-    refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60);
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
     ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
     ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
     ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
     ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
     ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
 
-    refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 90, 90);
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
     ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
     ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
     ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
     ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
     ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
-    refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120);
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120, nullptr), 0);
     ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
     ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
     ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
@@ -228,6 +239,16 @@
     ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
 }
 
+TEST_F(RefreshRateConfigsTest, testInPolicy) {
+    RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60_POINT_4, HWC_GROUP_ID_0,
+                                         "60fps", 60};
+    ASSERT_TRUE(expectedDefaultConfig.inPolicy(60.000004, 60.000004));
+    ASSERT_TRUE(expectedDefaultConfig.inPolicy(59.0f, 60.1f));
+    ASSERT_FALSE(expectedDefaultConfig.inPolicy(75.0, 90.0));
+    ASSERT_FALSE(expectedDefaultConfig.inPolicy(60.0011, 90.0));
+    ASSERT_FALSE(expectedDefaultConfig.inPolicy(50.0, 59.998));
+}
+
 } // namespace
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index facf142..b5245e2 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -79,7 +79,8 @@
         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>();
     }
 
@@ -207,6 +208,8 @@
                 scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
                                              /*currentConfig=*/HwcConfigIndexType(0),
                                              /*powerMode=*/HWC_POWER_MODE_OFF);
+        mFlinger->mPhaseConfiguration =
+                mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
 
         mScheduler =
                 new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
@@ -218,7 +221,7 @@
 
         mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
                                           mFlinger->mSfConnectionHandle,
-                                          mFlinger->mPhaseOffsets->getCurrentOffsets());
+                                          mFlinger->mPhaseConfiguration->getCurrentOffsets());
     }
 
     void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
@@ -393,6 +396,11 @@
     auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
     auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
 
+    auto fromHandle(const sp<IBinder>& handle) {
+        Mutex::Autolock _l(mFlinger->mStateLock);
+        return mFlinger->fromHandle(handle);
+    }
+
     ~TestableSurfaceFlinger() {
         // All these pointer and container clears help ensure that GMock does
         // not report a leaked object, since the SurfaceFlinger instance may
@@ -510,15 +518,13 @@
             display->mutableIsConnected() = true;
             display->setPowerMode(static_cast<HWC2::PowerMode>(mPowerMode));
 
-            flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = display.get();
+            flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
 
             if (mHwcDisplayType == HWC2::DisplayType::Physical) {
                 flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, mDisplayId);
                 (mIsPrimary ? flinger->mutableInternalHwcDisplayId()
                             : flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId;
             }
-
-            flinger->mFakeHwcDisplays.push_back(std::move(display));
         }
 
     private:
@@ -627,9 +633,6 @@
     surfaceflinger::test::Factory mFactory;
     sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
     TestableScheduler* mScheduler = nullptr;
-
-    // We need to keep a reference to these so they are properly destroyed.
-    std::vector<std::unique_ptr<HWC2Display>> mFakeHwcDisplays;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index a465388..994a509 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -315,4 +315,9 @@
     BlockedByPriorTransaction(/*flags*/ 0, /*syncInputWindows*/ true);
 }
 
+TEST_F(TransactionApplicationTest, FromHandle) {
+    sp<IBinder> badHandle;
+    auto ret = mFlinger.fromHandle(badHandle);
+    EXPECT_EQ(nullptr, ret.get());
+}
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 484947d..5846c77 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -92,6 +92,7 @@
 
 struct VSyncDispatchRealtimeTest : testing::Test {
     static nsecs_t constexpr mDispatchGroupThreshold = toNs(100us);
+    static nsecs_t constexpr mVsyncMoveThreshold = toNs(500us);
     static size_t constexpr mIterations = 20;
 };
 
@@ -148,7 +149,8 @@
 
 TEST_F(VSyncDispatchRealtimeTest, triple_alarm) {
     FixedRateIdealStubTracker tracker;
-    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+                                     mVsyncMoveThreshold);
 
     static size_t constexpr num_clients = 3;
     std::array<RepeatingCallbackReceiver, num_clients>
@@ -176,7 +178,8 @@
 TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) {
     auto next_vsync_interval = toNs(3ms);
     VRRStubTracker tracker(next_vsync_interval);
-    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+                                     mVsyncMoveThreshold);
 
     RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
 
@@ -193,7 +196,8 @@
 // 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);
+    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+                                     mVsyncMoveThreshold);
 
     RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
 
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index d668a41..5aff429 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -196,16 +196,18 @@
     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};
+    VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
+                                      mVsyncMoveThreshold};
 };
 
 TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
     EXPECT_CALL(mMockClock, alarmIn(_, 900));
     EXPECT_CALL(mMockClock, alarmCancel());
     {
-        VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker,
-                                          mDispatchGroupThreshold};
+        VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
+                                          mVsyncMoveThreshold};
         CountingCallback cb(mDispatch);
         EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled);
     }
@@ -468,14 +470,24 @@
 
 TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) {
     VSyncDispatch::CallbackToken tmp;
+    std::optional<nsecs_t> lastTarget;
     tmp = mDispatch.registerCallback(
-            [&](auto) {
-                EXPECT_EQ(mDispatch.schedule(tmp, 400, 1000), ScheduleResult::CannotSchedule);
+            [&](auto timestamp) {
+                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) {
@@ -547,10 +559,33 @@
     EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
 }
 
-TEST_F(VSyncDispatchTimerQueueTest, distinguishesScheduleAndReschedule) {
+TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
     CountingCallback cb0(mDispatch);
     EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
-    EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::ReScheduled);
+    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) {
@@ -570,13 +605,15 @@
     EXPECT_EQ(mDispatch.schedule(cb0, 1900, 2000), ScheduleResult::Scheduled);
 }
 
-TEST_F(VSyncDispatchTimerQueueTest, cannotScheduleDoesNotAffectSchedulingState) {
+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();
-    EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::CannotSchedule);
 }
 
 TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
@@ -612,19 +649,22 @@
 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) {});
+    VSyncDispatchTimerQueueEntry entry(
+            name, [](auto) {}, mVsyncMoveThreshold);
     EXPECT_THAT(entry.name(), Eq(name));
     EXPECT_FALSE(entry.lastExecutedVsyncTarget());
     EXPECT_FALSE(entry.wakeupTime());
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) {
-    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
     EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
@@ -643,7 +683,8 @@
     EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration))
             .Times(1)
             .WillOnce(Return(10000));
-    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
     EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled));
@@ -655,10 +696,13 @@
 TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) {
     auto callCount = 0;
     auto calledTime = 0;
-    VSyncDispatchTimerQueueEntry entry("test", [&](auto time) {
-        callCount++;
-        calledTime = time;
-    });
+    VSyncDispatchTimerQueueEntry entry(
+            "test",
+            [&](auto time) {
+                callCount++;
+                calledTime = time;
+            },
+            mVsyncMoveThreshold);
 
     EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
     auto const wakeup = entry.wakeupTime();
@@ -681,7 +725,8 @@
             .WillOnce(Return(1000))
             .WillOnce(Return(1020));
 
-    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto) {}, mVsyncMoveThreshold);
 
     EXPECT_FALSE(entry.wakeupTime());
     entry.update(mStubTracker, 0);
@@ -699,7 +744,8 @@
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
-    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto) {}, mVsyncMoveThreshold);
     EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
     entry.update(mStubTracker, 0);
 
@@ -708,19 +754,52 @@
     EXPECT_THAT(*wakeup, Eq(wakeup));
 }
 
-TEST_F(VSyncDispatchTimerQueueEntryTest, reportsCannotScheduleIfMissedOpportunity) {
-    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto) {}, mVsyncMoveThreshold);
     EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
-    entry.executing();
-    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::CannotSchedule));
-    EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::CannotSchedule));
+    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, reportsReScheduleIfStillTime) {
-    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+TEST_F(VSyncDispatchTimerQueueEntryTest,
+       willRequestNextEstimateWhenSnappingToNextTargettableVSync) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](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));
-    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::ReScheduled));
+
+    entry.executing(); // 1000 is executing
+
+    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](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));
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 84df019..72f8bb9 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -122,21 +122,52 @@
     return ft;
 }
 
+class StubCallback : public DispSync::Callback {
+public:
+    void onDispSyncEvent(nsecs_t when) 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<MockVSyncDispatch>()),
+          : 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) {}
+                     std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {
+        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 dummyTime = 47;
+    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 mFakeCbTime = 2093;
+    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)> innerCb;
+
     VSyncReactor mReactor;
 };
 
@@ -149,8 +180,8 @@
 }
 
 TEST_F(VSyncReactorTest, addingSignalledFenceAddsToTracker) {
-    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
-    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
 }
 
 TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) {
@@ -161,9 +192,9 @@
     EXPECT_FALSE(mReactor.addPresentFence(pendingFence));
     Mock::VerifyAndClearExpectations(mMockTracker.get());
 
-    signalFenceWithTime(pendingFence, dummyTime);
+    signalFenceWithTime(pendingFence, mDummyTime);
 
-    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
     EXPECT_CALL(*mMockTracker, addVsyncTimestamp(anotherDummyTime));
     EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(anotherDummyTime)));
 }
@@ -193,15 +224,15 @@
 
 TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) {
     static constexpr size_t aFewTimes = 8;
-    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime)).Times(1);
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime)).Times(1);
 
     mReactor.setIgnorePresentFences(true);
     for (auto i = 0; i < aFewTimes; i++) {
-        mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime));
+        mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime));
     }
 
     mReactor.setIgnorePresentFences(false);
-    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
 }
 
 TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
@@ -227,11 +258,11 @@
 TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
     nsecs_t const fakeTimestamp = 4839;
     nsecs_t const fakePeriod = 1010;
-    nsecs_t const fakeNow = 2214;
+    nsecs_t const mFakeNow = 2214;
     int const numPeriodsOut = 3;
-    EXPECT_CALL(*mMockClock, now()).WillOnce(Return(fakeNow));
+    EXPECT_CALL(*mMockClock, now()).WillOnce(Return(mFakeNow));
     EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
-    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(fakeNow + numPeriodsOut * fakePeriod))
+    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(mFakeNow + numPeriodsOut * fakePeriod))
             .WillOnce(Return(fakeTimestamp));
     EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut), Eq(fakeTimestamp));
 }
@@ -242,10 +273,38 @@
     EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod));
 }
 
-TEST_F(VSyncReactorTest, setPeriod) {
-    nsecs_t const fakePeriod = 4098;
-    EXPECT_CALL(*mMockTracker, setPeriod(fakePeriod));
-    mReactor.setPeriod(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, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+
+    EXPECT_TRUE(mReactor.addResyncSample(20000, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+
+    Mock::VerifyAndClearExpectations(mMockTracker.get());
+    EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
+
+    EXPECT_FALSE(mReactor.addResyncSample(25000, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+}
+
+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, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    Mock::VerifyAndClearExpectations(mMockTracker.get());
+
+    EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
+    EXPECT_FALSE(mReactor.addResyncSample(10000, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
 }
 
 TEST_F(VSyncReactorTest, addResyncSampleTypical) {
@@ -259,12 +318,239 @@
 
 TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) {
     bool periodFlushed = false;
-    nsecs_t const fakeTimestamp = 4398;
-    nsecs_t const newPeriod = 3490;
-    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
+    nsecs_t const newPeriod = 4000;
+
     mReactor.setPeriod(newPeriod);
-    EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, &periodFlushed));
+
+    auto time = 0;
+    auto constexpr numTimestampSubmissions = 10;
+    for (auto i = 0; i < numTimestampSubmissions; i++) {
+        time += period;
+        EXPECT_TRUE(mReactor.addResyncSample(time, &periodFlushed));
+        EXPECT_FALSE(periodFlushed);
+    }
+
+    time += newPeriod;
+    EXPECT_FALSE(mReactor.addResyncSample(time, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
+
+    for (auto i = 0; i < numTimestampSubmissions; i++) {
+        time += newPeriod;
+        EXPECT_FALSE(mReactor.addResyncSample(time, &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, &periodFlushed);
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+
+    time += newPeriod;
+    mReactor.addResyncSample(time, &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), mFakeCbTime))
+            .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(mFakeCbTime);
+    innerCb(mFakeCbTime);
+}
+
+TEST_F(VSyncReactorTest, callbackTimestampReadapted) {
+    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), mFakeCbTime))
+            .InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    ASSERT_TRUE(innerCb);
+    innerCb(mFakeCbTime);
+    EXPECT_THAT(outerCb.lastCallTime(), Optional(mFakeCbTime - period));
+}
+
+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);
+}
+
+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, &periodFlushed));
+    EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, &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(mFakeCbTime);
+}
+
+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);
+}
+
+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(mFakeCbTime), ".*");
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 2453ccb..0f9dd5b 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -130,6 +130,10 @@
     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));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index f7c3804..9bda095 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -33,13 +33,14 @@
     MOCK_METHOD0(onScreenReleased, void());
     MOCK_METHOD0(onScreenAcquired, void());
     MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
-    MOCK_METHOD2(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType));
+    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_METHOD0(requestLatestConfig, void());
     MOCK_METHOD1(pauseVsyncCallback, void(bool));
 };
 
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index a020e74..a44b9e7 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -24,6 +24,7 @@
 #include <system/window.h>
 #include <ui/BufferQueueDefs.h>
 #include <utils/StrongPointer.h>
+#include <utils/Timers.h>
 #include <utils/Trace.h>
 
 #include <algorithm>
@@ -228,8 +229,10 @@
           mailbox_mode(present_mode == VK_PRESENT_MODE_MAILBOX_KHR),
           pre_transform(pre_transform_),
           frame_timestamps_enabled(false),
+          acquire_next_image_timeout(-1),
           shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
-                 present_mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+                 present_mode ==
+                     VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
         ANativeWindow* window = surface.window.get();
         native_window_get_refresh_cycle_duration(
             window,
@@ -251,6 +254,7 @@
     int pre_transform;
     bool frame_timestamps_enabled;
     int64_t refresh_duration;
+    nsecs_t acquire_next_image_timeout;
     bool shared;
 
     struct Image {
@@ -363,7 +367,7 @@
         int64_t composition_latch_time = 0;
         int64_t actual_present_time = 0;
         // Obtain timestamps:
-        int ret = native_window_get_frame_timestamps(
+        int err = native_window_get_frame_timestamps(
             swapchain.surface.window.get(), ti.native_frame_id_,
             &desired_present_time, &render_complete_time,
             &composition_latch_time,
@@ -374,7 +378,7 @@
             nullptr,  //&dequeue_ready_time,
             nullptr /*&reads_done_time*/);
 
-        if (ret != android::NO_ERROR) {
+        if (err != android::OK) {
             continue;
         }
 
@@ -530,7 +534,7 @@
     surface->swapchain_handle = VK_NULL_HANDLE;
     int err = native_window_get_consumer_usage(surface->window.get(),
                                                &surface->consumer_usage);
-    if (err != android::NO_ERROR) {
+    if (err != android::OK) {
         ALOGE("native_window_get_consumer_usage() failed: %s (%d)",
               strerror(-err), err);
         surface->~Surface();
@@ -540,7 +544,7 @@
 
     err =
         native_window_api_connect(surface->window.get(), NATIVE_WINDOW_API_EGL);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("native_window_api_connect() failed: %s (%d)", strerror(-err),
               err);
         surface->~Surface();
@@ -588,7 +592,7 @@
 
     int query_value;
     int err = window->query(window, NATIVE_WINDOW_FORMAT, &query_value);
-    if (err != 0 || query_value < 0) {
+    if (err != android::OK || query_value < 0) {
         ALOGE("NATIVE_WINDOW_FORMAT query failed: %s (%d) value=%d",
               strerror(-err), err, query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -629,13 +633,13 @@
 
     int width, height;
     err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
     err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -643,7 +647,7 @@
 
     int transform_hint;
     err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -651,7 +655,7 @@
 
     int max_buffer_count;
     err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &max_buffer_count);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -694,20 +698,6 @@
 
     const InstanceData& instance_data = GetData(pdev);
 
-    // TODO(b/143296550): Fill out the set of supported formats. Longer term,
-    // add a new gralloc method to query whether a (format, usage) pair is
-    // supported, and check that for each gralloc format that corresponds to a
-    // Vulkan format. Shorter term, just add a few more formats to the ones
-    // hardcoded below.
-
-    const VkSurfaceFormatKHR kFormats[] = {
-        {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
-        {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
-        {VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
-    };
-    const uint32_t kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
-    uint32_t total_num_formats = kNumFormats;
-
     bool wide_color_support = false;
     Surface& surface = *SurfaceFromHandle(surface_handle);
     int err = native_window_get_wide_color_support(surface.window.get(),
@@ -720,43 +710,72 @@
         wide_color_support &&
         instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
 
-    const VkSurfaceFormatKHR kWideColorFormats[] = {
-        {VK_FORMAT_R8G8B8A8_UNORM,
-         VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
-        {VK_FORMAT_R8G8B8A8_SRGB,
-         VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
-        {VK_FORMAT_R16G16B16A16_SFLOAT,
-         VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT},
-        {VK_FORMAT_R16G16B16A16_SFLOAT,
-         VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT},
-        {VK_FORMAT_A2B10G10R10_UNORM_PACK32,
-         VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
-    };
-    const uint32_t kNumWideColorFormats =
-        sizeof(kWideColorFormats) / sizeof(kWideColorFormats[0]);
+    AHardwareBuffer_Desc desc = {};
+    desc.width = 1;
+    desc.height = 1;
+    desc.layers = 1;
+    desc.usage = surface.consumer_usage |
+                 AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
+                 AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
+
+    // We must support R8G8B8A8
+    std::vector<VkSurfaceFormatKHR> all_formats = {
+        {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
+        {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}};
+
     if (wide_color_support) {
-        total_num_formats += kNumWideColorFormats;
+        all_formats.emplace_back(VkSurfaceFormatKHR{
+            VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
+        all_formats.emplace_back(VkSurfaceFormatKHR{
+            VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
+    }
+
+    desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
+    if (AHardwareBuffer_isSupported(&desc)) {
+        all_formats.emplace_back(VkSurfaceFormatKHR{
+            VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+    }
+
+    desc.format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT;
+    if (AHardwareBuffer_isSupported(&desc)) {
+        all_formats.emplace_back(VkSurfaceFormatKHR{
+            VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+        if (wide_color_support) {
+            all_formats.emplace_back(
+                VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
+                                   VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT});
+            all_formats.emplace_back(
+                VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
+                                   VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT});
+        }
+    }
+
+    desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
+    if (AHardwareBuffer_isSupported(&desc)) {
+        all_formats.emplace_back(
+            VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+                               VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+        if (wide_color_support) {
+            all_formats.emplace_back(
+                VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+                                   VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
+        }
     }
 
     VkResult result = VK_SUCCESS;
     if (formats) {
-        uint32_t out_count = 0;
-        uint32_t transfer_count = 0;
-        if (*count < total_num_formats)
+        uint32_t transfer_count = all_formats.size();
+        if (transfer_count > *count) {
+            transfer_count = *count;
             result = VK_INCOMPLETE;
-        transfer_count = std::min(*count, kNumFormats);
-        std::copy(kFormats, kFormats + transfer_count, formats);
-        out_count += transfer_count;
-        if (wide_color_support) {
-            transfer_count = std::min(*count - out_count, kNumWideColorFormats);
-            std::copy(kWideColorFormats, kWideColorFormats + transfer_count,
-                      formats + out_count);
-            out_count += transfer_count;
         }
-        *count = out_count;
+        std::copy(all_formats.begin(), all_formats.begin() + transfer_count,
+                  formats);
+        *count = transfer_count;
     } else {
-        *count = total_num_formats;
+        *count = all_formats.size();
     }
+
     return result;
 }
 
@@ -841,7 +860,7 @@
     ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
 
     err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
-    if (err != 0 || query_value < 0) {
+    if (err != android::OK || query_value < 0) {
         ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) value=%d",
               strerror(-err), err, query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -849,7 +868,7 @@
     uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
 
     err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
-    if (err != 0 || query_value < 0) {
+    if (err != android::OK || query_value < 0) {
         ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d",
               strerror(-err), err, query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -941,12 +960,12 @@
 
         int width = 0, height = 0;
         err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
-        if (err != 0) {
+        if (err != android::OK) {
             ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
                   strerror(-err), err);
         }
         err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
-        if (err != 0) {
+        if (err != android::OK) {
             ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
                   strerror(-err), err);
         }
@@ -1070,17 +1089,23 @@
     // dequeue all buffers.
     //
     // TODO(http://b/134186185) recycle swapchain images more efficiently
-    err = native_window_api_disconnect(surface.window.get(),
-                                       NATIVE_WINDOW_API_EGL);
-    ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)",
+    ANativeWindow* window = surface.window.get();
+    err = native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+    ALOGW_IF(err != android::OK, "native_window_api_disconnect failed: %s (%d)",
              strerror(-err), err);
-    err =
-        native_window_api_connect(surface.window.get(), NATIVE_WINDOW_API_EGL);
-    ALOGW_IF(err != 0, "native_window_api_connect failed: %s (%d)",
+    err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+    ALOGW_IF(err != android::OK, "native_window_api_connect failed: %s (%d)",
              strerror(-err), err);
 
-    err = native_window_set_buffer_count(surface.window.get(), 0);
-    if (err != 0) {
+    err = window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, -1);
+    if (err != android::OK) {
+        ALOGE("window->perform(SET_DEQUEUE_TIMEOUT) failed: %s (%d)",
+              strerror(-err), err);
+        return VK_ERROR_SURFACE_LOST_KHR;
+    }
+
+    err = native_window_set_buffer_count(window, 0);
+    if (err != android::OK) {
         ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1088,22 +1113,22 @@
 
     int swap_interval =
         create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
-    err = surface.window->setSwapInterval(surface.window.get(), swap_interval);
-    if (err != 0) {
+    err = window->setSwapInterval(window, swap_interval);
+    if (err != android::OK) {
         ALOGE("native_window->setSwapInterval(1) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
-    err = native_window_set_shared_buffer_mode(surface.window.get(), false);
-    if (err != 0) {
+    err = native_window_set_shared_buffer_mode(window, false);
+    if (err != android::OK) {
         ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
-    err = native_window_set_auto_refresh(surface.window.get(), false);
-    if (err != 0) {
+    err = native_window_set_auto_refresh(window, false);
+    if (err != android::OK) {
         ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1113,25 +1138,23 @@
 
     const auto& dispatch = GetData(device).driver;
 
-    err = native_window_set_buffers_format(surface.window.get(),
-                                           native_pixel_format);
-    if (err != 0) {
+    err = native_window_set_buffers_format(window, native_pixel_format);
+    if (err != android::OK) {
         ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)",
               native_pixel_format, strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
-    err = native_window_set_buffers_data_space(surface.window.get(),
-                                               native_dataspace);
-    if (err != 0) {
+    err = native_window_set_buffers_data_space(window, native_dataspace);
+    if (err != android::OK) {
         ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)",
               native_dataspace, strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     err = native_window_set_buffers_dimensions(
-        surface.window.get(), static_cast<int>(create_info->imageExtent.width),
+        window, static_cast<int>(create_info->imageExtent.width),
         static_cast<int>(create_info->imageExtent.height));
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
               create_info->imageExtent.width, create_info->imageExtent.height,
               strerror(-err), err);
@@ -1147,9 +1170,8 @@
     // it's job the two transforms cancel each other out and the compositor ends
     // up applying an identity transform to the app's buffer.
     err = native_window_set_buffers_transform(
-        surface.window.get(),
-        InvertTransformToNative(create_info->preTransform));
-    if (err != 0) {
+        window, InvertTransformToNative(create_info->preTransform));
+    if (err != android::OK) {
         ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
               InvertTransformToNative(create_info->preTransform),
               strerror(-err), err);
@@ -1157,8 +1179,8 @@
     }
 
     err = native_window_set_scaling_mode(
-        surface.window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-    if (err != 0) {
+        window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+    if (err != android::OK) {
         ALOGE("native_window_set_scaling_mode(SCALE_TO_WINDOW) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1168,26 +1190,25 @@
     if (create_info->presentMode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
         create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
         swapchain_image_usage |= VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID;
-        err = native_window_set_shared_buffer_mode(surface.window.get(), true);
-        if (err != 0) {
+        err = native_window_set_shared_buffer_mode(window, true);
+        if (err != android::OK) {
             ALOGE("native_window_set_shared_buffer_mode failed: %s (%d)", strerror(-err), err);
             return VK_ERROR_SURFACE_LOST_KHR;
         }
     }
 
     if (create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
-        err = native_window_set_auto_refresh(surface.window.get(), true);
-        if (err != 0) {
+        err = native_window_set_auto_refresh(window, true);
+        if (err != android::OK) {
             ALOGE("native_window_set_auto_refresh failed: %s (%d)", strerror(-err), err);
             return VK_ERROR_SURFACE_LOST_KHR;
         }
     }
 
     int query_value;
-    err = surface.window->query(surface.window.get(),
-                                NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
-                                &query_value);
-    if (err != 0 || query_value < 0) {
+    err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                        &query_value);
+    if (err != android::OK || query_value < 0) {
         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
               query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1203,8 +1224,8 @@
     // in place for that to work yet. Note we only lie to the lower layer-- we
     // don't want to give the app back a swapchain with extra images (which they
     // can't actually use!).
-    err = native_window_set_buffer_count(surface.window.get(), std::max(2u, num_images));
-    if (err != 0) {
+    err = native_window_set_buffer_count(window, std::max(2u, num_images));
+    if (err != android::OK) {
         ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1242,16 +1263,15 @@
         createProtectedSwapchain = true;
         native_usage |= BufferUsage::PROTECTED;
     }
-    err = native_window_set_usage(surface.window.get(), native_usage);
-    if (err != 0) {
+    err = native_window_set_usage(window, native_usage);
+    if (err != android::OK) {
         ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     int transform_hint;
-    err = surface.window->query(surface.window.get(),
-                                NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
-    if (err != 0) {
+    err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1307,9 +1327,8 @@
         Swapchain::Image& img = swapchain->images[i];
 
         ANativeWindowBuffer* buffer;
-        err = surface.window->dequeueBuffer(surface.window.get(), &buffer,
-                                            &img.dequeue_fence);
-        if (err != 0) {
+        err = window->dequeueBuffer(window, &buffer, &img.dequeue_fence);
+        if (err != android::OK) {
             ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err);
             switch (-err) {
                 case ENOMEM:
@@ -1353,8 +1372,8 @@
         Swapchain::Image& img = swapchain->images[i];
         if (img.dequeued) {
             if (!swapchain->shared) {
-                surface.window->cancelBuffer(surface.window.get(), img.buffer.get(),
-                                             img.dequeue_fence);
+                window->cancelBuffer(window, img.buffer.get(),
+                                     img.dequeue_fence);
                 img.dequeue_fence = -1;
                 img.dequeued = false;
             }
@@ -1432,10 +1451,6 @@
     if (swapchain.surface.swapchain_handle != swapchain_handle)
         return VK_ERROR_OUT_OF_DATE_KHR;
 
-    ALOGW_IF(
-        timeout != UINT64_MAX,
-        "vkAcquireNextImageKHR: non-infinite timeouts not yet implemented");
-
     if (swapchain.shared) {
         // In shared mode, we keep the buffer dequeued all the time, so we don't
         // want to dequeue a buffer here. Instead, just ask the driver to ensure
@@ -1446,10 +1461,27 @@
         return result;
     }
 
+    const nsecs_t acquire_next_image_timeout =
+        timeout > (uint64_t)std::numeric_limits<nsecs_t>::max() ? -1 : timeout;
+    if (acquire_next_image_timeout != swapchain.acquire_next_image_timeout) {
+        // Cache the timeout to avoid the duplicate binder cost.
+        err = window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT,
+                              acquire_next_image_timeout);
+        if (err != android::OK) {
+            ALOGE("window->perform(SET_DEQUEUE_TIMEOUT) failed: %s (%d)",
+                  strerror(-err), err);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+        swapchain.acquire_next_image_timeout = acquire_next_image_timeout;
+    }
+
     ANativeWindowBuffer* buffer;
     int fence_fd;
     err = window->dequeueBuffer(window, &buffer, &fence_fd);
-    if (err != 0) {
+    if (err == android::TIMED_OUT) {
+        ALOGW("dequeueBuffer timed out: %s (%d)", strerror(-err), err);
+        return timeout ? VK_TIMEOUT : VK_NOT_READY;
+    } else if (err != android::OK) {
         ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
@@ -1656,7 +1688,7 @@
                     uint64_t nativeFrameId = 0;
                     err = native_window_get_next_frame_id(
                             window, &nativeFrameId);
-                    if (err != android::NO_ERROR) {
+                    if (err != android::OK) {
                         ALOGE("Failed to get next native frame ID.");
                     }
 
@@ -1680,7 +1712,7 @@
 
                 err = window->queueBuffer(window, img.buffer.get(), fence);
                 // queueBuffer always closes fence, even on error
-                if (err != 0) {
+                if (err != android::OK) {
                     ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
                     swapchain_result = WorstPresentResult(
                         swapchain_result, VK_ERROR_OUT_OF_DATE_KHR);
@@ -1700,17 +1732,15 @@
                     ANativeWindowBuffer* buffer;
                     int fence_fd;
                     err = window->dequeueBuffer(window, &buffer, &fence_fd);
-                    if (err != 0) {
+                    if (err != android::OK) {
                         ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
                         swapchain_result = WorstPresentResult(swapchain_result,
                             VK_ERROR_SURFACE_LOST_KHR);
-                    }
-                    else if (img.buffer != buffer) {
+                    } else if (img.buffer != buffer) {
                         ALOGE("got wrong image back for shared swapchain");
                         swapchain_result = WorstPresentResult(swapchain_result,
                             VK_ERROR_SURFACE_LOST_KHR);
-                    }
-                    else {
+                    } else {
                         img.dequeue_fence = fence_fd;
                         img.dequeued = true;
                     }
@@ -1722,7 +1752,7 @@
             int window_transform_hint;
             err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
                                 &window_transform_hint);
-            if (err != 0) {
+            if (err != android::OK) {
                 ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
                       strerror(-err), err);
                 swapchain_result = WorstPresentResult(