Merge "SurfaceFlinger: enhance fakehwc tests"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 3fa5430..20bfe65 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -4,6 +4,7 @@
 [Builtin Hooks Options]
 # Only turn on clang-format check for the following subfolders.
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+               cmds/idlcli/
                include/input/
                libs/binder/fuzzer/
                libs/binder/ndk/
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..8173c89
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,63 @@
+{
+  "presubmit": [
+    {
+      "name": "SurfaceFlinger_test",
+      "options": [
+        {
+          "include-filter": "*CredentialsTest.*"
+        },
+        {
+          "include-filter": "*SurfaceFlingerStress.*"
+        },
+        {
+          "include-filter": "*SurfaceInterceptorTest.*"
+        },
+        {
+          "include-filter": "*LayerTransactionTest.*"
+        },
+        {
+          "include-filter": "*LayerTypeTransactionTest.*"
+        },
+        {
+          "include-filter": "*LayerUpdateTest.*"
+        },
+        {
+          "include-filter": "*GeometryLatchingTest.*"
+        },
+        {
+          "include-filter": "*CropLatchingTest.*"
+        },
+        {
+          "include-filter": "*ChildLayerTest.*"
+        },
+        {
+          "include-filter": "*ScreenCaptureTest.*"
+        },
+        {
+          "include-filter": "*ScreenCaptureChildOnlyTest.*"
+        },
+        {
+          "include-filter": "*DereferenceSurfaceControlTest.*"
+        },
+        {
+          "include-filter": "*BoundlessLayerTest.*"
+        },
+        {
+          "include-filter": "*MultiDisplayLayerBoundsTest.*"
+        },
+        {
+          "include-filter": "*InvalidHandleTest.*"
+        },
+        {
+          "include-filter": "*VirtualDisplayTest.*"
+        },
+        {
+          "include-filter": "*RelativeZTest.*"
+        }
+      ]
+    },
+    {
+      "name": "libsurfaceflinger_unittest"
+    }
+  ]
+}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 8400cdc..9d6a7ff 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1545,11 +1545,7 @@
  * Returns RunStatus::USER_DENIED_CONSENT if user explicitly denied consent to sharing the bugreport
  * with the caller.
  */
-static Dumpstate::RunStatus DumpstateDefault() {
-    // Invoking the following dumpsys calls before DumpTraces() to try and
-    // keep the system stats as close to its initial state as possible.
-    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysCritical);
-
+Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() {
     // Capture first logcat early on; useful to take a snapshot before dumpstate logs take over the
     // buffer.
     DoLogcat();
@@ -1634,6 +1630,7 @@
 // This method collects dumpsys for telephony debugging only
 static void DumpstateTelephonyOnly() {
     DurationReporter duration_reporter("DUMPSTATE");
+
     const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
 
     DumpstateRadioCommon();
@@ -2060,12 +2057,12 @@
                                   ? StringPrintf("[fd:%d]", ds.options_->bugreport_fd.get())
                                   : ds.bugreport_internal_dir_.c_str();
     MYLOGD(
-        "Bugreport dir: %s\n"
-        "Base name: %s\n"
-        "Suffix: %s\n"
-        "Log path: %s\n"
-        "Temporary path: %s\n"
-        "Screenshot path: %s\n",
+        "Bugreport dir: [%s] "
+        "Base name: [%s] "
+        "Suffix: [%s] "
+        "Log path: [%s] "
+        "Temporary path: [%s] "
+        "Screenshot path: [%s]\n",
         destination.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(),
         ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
 
@@ -2167,21 +2164,14 @@
 }
 
 static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
-    MYLOGI("do_zip_file: %d\n", options.do_zip_file);
-    MYLOGI("do_add_date: %d\n", options.do_add_date);
-    MYLOGI("do_vibrate: %d\n", options.do_vibrate);
-    MYLOGI("use_socket: %d\n", options.use_socket);
-    MYLOGI("use_control_socket: %d\n", options.use_control_socket);
-    MYLOGI("do_fb: %d\n", options.do_fb);
-    MYLOGI("is_remote_mode: %d\n", options.is_remote_mode);
-    MYLOGI("show_header_only: %d\n", options.show_header_only);
-    MYLOGI("do_start_service: %d\n", options.do_start_service);
-    MYLOGI("telephony_only: %d\n", options.telephony_only);
-    MYLOGI("wifi_only: %d\n", options.wifi_only);
-    MYLOGI("do_progress_updates: %d\n", options.do_progress_updates);
-    MYLOGI("fd: %d\n", options.bugreport_fd.get());
-    MYLOGI("bugreport_mode: %s\n", options.bugreport_mode.c_str());
-    MYLOGI("args: %s\n", options.args.c_str());
+    MYLOGI(
+        "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_fb: %d "
+        "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
+        "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s args: %s\n",
+        options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
+        options.do_fb, options.is_remote_mode, options.show_header_only, options.do_start_service,
+        options.telephony_only, options.wifi_only, options.do_progress_updates,
+        options.bugreport_fd.get(), options.bugreport_mode.c_str(), options.args.c_str());
 }
 
 void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
@@ -2353,11 +2343,6 @@
 
     MYLOGD("dumpstate calling_uid = %d ; calling package = %s \n",
             calling_uid, calling_package.c_str());
-    if (CalledByApi()) {
-        // If the output needs to be copied over to the caller's fd, get user consent.
-        android::String16 package(calling_package.c_str());
-        CheckUserConsent(calling_uid, package);
-    }
 
     // Redirect output if needed
     bool is_redirecting = options_->OutputToFile();
@@ -2374,8 +2359,6 @@
     id_ = ++last_id;
     android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
 
-    MYLOGI("begin\n");
-
     if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
         MYLOGE("Failed to acquire wake lock: %s\n", strerror(errno));
     } else {
@@ -2398,10 +2381,8 @@
         MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
     }
 
-    MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s)\n", id_, options_->args.c_str(),
-           options_->bugreport_mode.c_str());
-
-    MYLOGI("bugreport format version: %s\n", version_.c_str());
+    MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s bugreport format version: %s\n",
+           id_, options_->args.c_str(), options_->bugreport_mode.c_str(), version_.c_str());
 
     do_early_screenshot_ = options_->do_progress_updates;
 
@@ -2500,13 +2481,23 @@
     PrintHeader();
 
     if (options_->telephony_only) {
+        MaybeCheckUserConsent(calling_uid, calling_package);
         DumpstateTelephonyOnly();
         DumpstateBoard();
     } else if (options_->wifi_only) {
+        MaybeCheckUserConsent(calling_uid, calling_package);
         DumpstateWifiOnly();
     } else {
+        // Invoking the critical dumpsys calls before DumpTraces() to try and
+        // keep the system stats as close to its initial state as possible.
+        RunDumpsysCritical();
+
+        // Run consent check only after critical dumpsys has finished -- so the consent
+        // isn't going to pollute the system state / logs.
+        MaybeCheckUserConsent(calling_uid, calling_package);
+
         // Dump state for the default case. This also drops root.
-        RunStatus s = DumpstateDefault();
+        RunStatus s = DumpstateDefaultAfterCritical();
         if (s != RunStatus::OK) {
             if (s == RunStatus::USER_CONSENT_DENIED) {
                 HandleUserConsentDenied();
@@ -2591,17 +2582,20 @@
                : RunStatus::OK;
 }
 
-void Dumpstate::CheckUserConsent(int32_t calling_uid, const android::String16& calling_package) {
-    if (calling_uid == AID_SHELL) {
+void Dumpstate::MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package) {
+    if (calling_uid == AID_SHELL || !CalledByApi()) {
+        // No need to get consent for shell triggered dumpstates, or not through
+        // bugreporting API (i.e. no fd to copy back).
         return;
     }
     consent_callback_ = new ConsentCallback();
     const String16 incidentcompanion("incidentcompanion");
     sp<android::IBinder> ics(defaultServiceManager()->getService(incidentcompanion));
+    android::String16 package(calling_package.c_str());
     if (ics != nullptr) {
         MYLOGD("Checking user consent via incidentcompanion service\n");
         android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
-            calling_uid, calling_package, String16(), String16(),
+            calling_uid, package, String16(), String16(),
             0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get());
     } else {
         MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n");
@@ -3498,8 +3492,8 @@
     }
 
     if (listener_ != nullptr) {
-        if (percent % 5 == 0) {
-            // We don't want to spam logcat, so only log multiples of 5.
+        if (percent % 10 == 0) {
+            // We don't want to spam logcat, so only log multiples of 10.
             MYLOGD("Setting progress: %d/%d (%d%%)\n", progress, max, percent);
         } else {
             // stderr is ignored on normal invocations, but useful when calling
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 831574d..7d9b113 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -486,7 +486,9 @@
   private:
     RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
 
-    void CheckUserConsent(int32_t calling_uid, const android::String16& calling_package);
+    RunStatus DumpstateDefaultAfterCritical();
+
+    void MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package);
 
     // Removes the in progress files output files (tmp file, zip/txt file, screenshot),
     // but leaves the log file alone.
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index bb92fd3..5476319 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -36,7 +36,10 @@
     defaults: ["idlcli-defaults"],
     srcs: [
         "CommandVibrator.cpp",
+        "vibrator/CommandCompose.cpp",
         "vibrator/CommandGetCapabilities.cpp",
+        "vibrator/CommandGetCompositionDelayMax.cpp",
+        "vibrator/CommandGetCompositionSizeMax.cpp",
         "vibrator/CommandOff.cpp",
         "vibrator/CommandOn.cpp",
         "vibrator/CommandPerform.cpp",
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
new file mode 100644
index 0000000..705e40b
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositeEffect;
+
+class CommandCompose : public Command {
+    std::string getDescription() const override { return "Compose vibration."; }
+
+    std::string getUsageSummary() const override { return "<delay> <primitive> <scale> ..."; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{
+                {"<delay>", {"In milliseconds"}},
+                {"<primitive>", {"Primitive ID."}},
+                {"<scale>", {"0.0 (exclusive) - 1.0 (inclusive)."}},
+                {"...", {"May repeat multiple times."}},
+        };
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        while (!args.empty()) {
+            CompositeEffect effect;
+            if (auto delay = args.pop<decltype(effect.delayMs)>()) {
+                effect.delayMs = *delay;
+                std::cout << "Delay: " << effect.delayMs << std::endl;
+            } else {
+                std::cerr << "Missing or Invalid Delay!" << std::endl;
+                return USAGE;
+            }
+            // TODO: Use range validation when supported by AIDL
+            if (auto primitive = args.pop<std::underlying_type_t<decltype(effect.primitive)>>()) {
+                effect.primitive = static_cast<decltype(effect.primitive)>(*primitive);
+                std::cout << "Primitive: " << toString(effect.primitive) << std::endl;
+            } else {
+                std::cerr << "Missing or Invalid Primitive!" << std::endl;
+                return USAGE;
+            }
+            if (auto scale = args.pop<decltype(effect.scale)>();
+                scale && *scale > 0.0 && scale <= 1.0) {
+                effect.scale = *scale;
+                std::cout << "Scale: " << effect.scale << std::endl;
+            } else {
+                std::cerr << "Missing or Invalid Scale!" << std::endl;
+                return USAGE;
+            }
+            mComposite.emplace_back(std::move(effect));
+        }
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        Status ret;
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr);
+            statusStr = status.toString8();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+
+        return ret;
+    }
+
+    std::vector<CompositeEffect> mComposite;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandCompose>("compose");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
new file mode 100644
index 0000000..b414307
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetCompositionDelayMax : public Command {
+    std::string getDescription() const override {
+        return "Retrieves vibrator composition delay max.";
+    }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        int32_t maxDelayMs;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getCompositionDelayMax, &maxDelayMs);
+            statusStr = status.toString8();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Max Delay: " << maxDelayMs << " ms" << std::endl;
+
+        return ret;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandGetCompositionDelayMax>(
+                "getCompositionDelayMax");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
new file mode 100644
index 0000000..360fc9d
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetCompositionSizeMax : public Command {
+    std::string getDescription() const override {
+        return "Retrieves vibrator composition size max.";
+    }
+
+    std::string getUsageSummary() const override { return ""; }
+
+    UsageDetails getUsageDetails() const override {
+        UsageDetails details{};
+        return details;
+    }
+
+    Status doArgs(Args &args) override {
+        if (!args.empty()) {
+            std::cerr << "Unexpected Arguments!" << std::endl;
+            return USAGE;
+        }
+        return OK;
+    }
+
+    Status doMain(Args && /*args*/) override {
+        std::string statusStr;
+        int32_t maxSize;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getCompositionSizeMax, &maxSize);
+            statusStr = status.toString8();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Max Size: " << maxSize << std::endl;
+
+        return ret;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandGetCompositionSizeMax>(
+                "getCompositionSizeMax");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
index 6e2261f..33d7eed 100644
--- a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -54,7 +54,8 @@
         Status ret;
 
         if (auto hal = getHal<aidl::IVibrator>()) {
-            auto status = hal->call(&aidl::IVibrator::setAmplitude, mAmplitude);
+            auto status = hal->call(&aidl::IVibrator::setAmplitude,
+                                    static_cast<float>(mAmplitude) / UINT8_MAX);
             statusStr = status.toString8();
             ret = status.isOk() ? OK : ERROR;
         } else if (auto hal = getHal<V1_0::IVibrator>()) {
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index e6e232c..95957a0 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -97,6 +97,7 @@
 static constexpr int kVerityPageSize = 4096;
 static constexpr size_t kSha256Size = 32;
 static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
+static constexpr const char* kFuseProp = "persist.sys.fuse";
 
 namespace {
 
@@ -588,12 +589,21 @@
         std::lock_guard<std::recursive_mutex> lock(mMountsLock);
         for (const auto& n : mStorageMounts) {
             auto extPath = n.second;
-            if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
-                extPath += StringPrintf("/%d", userId);
-            } else if (userId != 0) {
-                // TODO: support devices mounted under secondary users
-                continue;
+
+            if (android::base::GetBoolProperty(kFuseProp, false)) {
+                std::regex re("^\\/mnt\\/pass_through\\/[0-9]+\\/emulated");
+                if (std::regex_match(extPath, re)) {
+                    extPath += "/" + std::to_string(userId);
+                }
+            } else {
+                if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
+                    extPath += StringPrintf("/%d", userId);
+                } else if (userId != 0) {
+                    // TODO: support devices mounted under secondary users
+                    continue;
+                }
             }
+
             if (flags & FLAG_CLEAR_CACHE_ONLY) {
                 // Clear only cached data from shared storage
                 auto path = StringPrintf("%s/Android/data/%s/cache", extPath.c_str(), pkgname);
@@ -684,16 +694,26 @@
         std::lock_guard<std::recursive_mutex> lock(mMountsLock);
         for (const auto& n : mStorageMounts) {
             auto extPath = n.second;
-            if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
-                extPath += StringPrintf("/%d", userId);
-            } else if (userId != 0) {
-                // TODO: support devices mounted under secondary users
-                continue;
+
+            if (android::base::GetBoolProperty(kFuseProp, false)) {
+                std::regex re("^\\/mnt\\/pass_through\\/[0-9]+\\/emulated");
+                if (std::regex_match(extPath, re)) {
+                    extPath += "/" + std::to_string(userId);
+                }
+            } else {
+                if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
+                    extPath += StringPrintf("/%d", userId);
+                } else if (userId != 0) {
+                    // TODO: support devices mounted under secondary users
+                    continue;
+                }
             }
+
             auto path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname);
             if (delete_dir_contents_and_dir(path, true) != 0) {
                 res = error("Failed to delete contents of " + path);
             }
+
             path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname);
             if (delete_dir_contents_and_dir(path, true) != 0) {
                 res = error("Failed to delete contents of " + path);
@@ -2574,12 +2594,19 @@
         std::getline(in, target, ' ');
         std::getline(in, ignored);
 
+        if (android::base::GetBoolProperty(kFuseProp, false)) {
+            if (target.compare(0, 17, "/mnt/pass_through") == 0) {
+                LOG(DEBUG) << "Found storage mount " << source << " at " << target;
+                mStorageMounts[source] = target;
+            }
+        } else {
 #if !BYPASS_SDCARDFS
-        if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
-            LOG(DEBUG) << "Found storage mount " << source << " at " << target;
-            mStorageMounts[source] = target;
-        }
+            if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
+                LOG(DEBUG) << "Found storage mount " << source << " at " << target;
+                mStorageMounts[source] = target;
+            }
 #endif
+        }
     }
     return ok();
 }
diff --git a/include/android/input.h b/include/android/input.h
index ce439c6..f51cd79 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -158,7 +158,10 @@
     AINPUT_EVENT_TYPE_KEY = 1,
 
     /** Indicates that the input event is a motion event. */
-    AINPUT_EVENT_TYPE_MOTION = 2
+    AINPUT_EVENT_TYPE_MOTION = 2,
+
+    /** Focus event */
+    AINPUT_EVENT_TYPE_FOCUS = 3,
 };
 
 /**
diff --git a/include/binder b/include/binder
deleted file mode 120000
index 35a022a..0000000
--- a/include/binder
+++ /dev/null
@@ -1 +0,0 @@
-../libs/binder/include/binder/
\ No newline at end of file
diff --git a/include/binder/Binder.h b/include/binder/Binder.h
new file mode 120000
index 0000000..0fc6db7
--- /dev/null
+++ b/include/binder/Binder.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Binder.h
\ No newline at end of file
diff --git a/include/binder/BinderService.h b/include/binder/BinderService.h
new file mode 120000
index 0000000..370b260
--- /dev/null
+++ b/include/binder/BinderService.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/BinderService.h
\ No newline at end of file
diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h
new file mode 120000
index 0000000..93a6219
--- /dev/null
+++ b/include/binder/IBinder.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IBinder.h
\ No newline at end of file
diff --git a/include/binder/IInterface.h b/include/binder/IInterface.h
new file mode 120000
index 0000000..8579878
--- /dev/null
+++ b/include/binder/IInterface.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IInterface.h
\ No newline at end of file
diff --git a/include/binder/IMemory.h b/include/binder/IMemory.h
new file mode 120000
index 0000000..5171c08
--- /dev/null
+++ b/include/binder/IMemory.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IMemory.h
\ No newline at end of file
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
new file mode 120000
index 0000000..ecd4f81
--- /dev/null
+++ b/include/binder/IPCThreadState.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IPCThreadState.h
\ No newline at end of file
diff --git a/include/binder/IServiceManager.h b/include/binder/IServiceManager.h
new file mode 120000
index 0000000..33d18cc
--- /dev/null
+++ b/include/binder/IServiceManager.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IServiceManager.h
\ No newline at end of file
diff --git a/include/binder/MemoryDealer.h b/include/binder/MemoryDealer.h
new file mode 120000
index 0000000..71881fb
--- /dev/null
+++ b/include/binder/MemoryDealer.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/MemoryDealer.h
\ No newline at end of file
diff --git a/include/binder/MemoryHeapBase.h b/include/binder/MemoryHeapBase.h
new file mode 120000
index 0000000..8fb51cc
--- /dev/null
+++ b/include/binder/MemoryHeapBase.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/MemoryHeapBase.h
\ No newline at end of file
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
new file mode 120000
index 0000000..23492be
--- /dev/null
+++ b/include/binder/Parcel.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Parcel.h
\ No newline at end of file
diff --git a/include/binder/ParcelFileDescriptor.h b/include/binder/ParcelFileDescriptor.h
new file mode 120000
index 0000000..777bd49
--- /dev/null
+++ b/include/binder/ParcelFileDescriptor.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ParcelFileDescriptor.h
\ No newline at end of file
diff --git a/include/binder/Parcelable.h b/include/binder/Parcelable.h
new file mode 120000
index 0000000..438e223
--- /dev/null
+++ b/include/binder/Parcelable.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Parcelable.h
\ No newline at end of file
diff --git a/include/binder/PermissionCache.h b/include/binder/PermissionCache.h
new file mode 120000
index 0000000..e910c12
--- /dev/null
+++ b/include/binder/PermissionCache.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/PermissionCache.h
\ No newline at end of file
diff --git a/include/binder/PersistableBundle.h b/include/binder/PersistableBundle.h
new file mode 120000
index 0000000..785f2b4
--- /dev/null
+++ b/include/binder/PersistableBundle.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/PersistableBundle.h
\ No newline at end of file
diff --git a/include/binder/ProcessState.h b/include/binder/ProcessState.h
new file mode 120000
index 0000000..4cbe7a5
--- /dev/null
+++ b/include/binder/ProcessState.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ProcessState.h
\ No newline at end of file
diff --git a/include/binder/Stability.h b/include/binder/Stability.h
new file mode 120000
index 0000000..9b431d2
--- /dev/null
+++ b/include/binder/Stability.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Stability.h
\ No newline at end of file
diff --git a/include/binder/Status.h b/include/binder/Status.h
new file mode 120000
index 0000000..ccb994e
--- /dev/null
+++ b/include/binder/Status.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Status.h
\ No newline at end of file
diff --git a/include/input/Input.h b/include/input/Input.h
index cbd1a41..f871847 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -31,8 +31,8 @@
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 #include <utils/Vector.h>
-
 #include <limits>
+#include <queue>
 
 /*
  * Additional private constants not defined in ndk/ui/input.h.
@@ -167,6 +167,8 @@
 class Parcel;
 #endif
 
+const char* inputEventTypeToString(int32_t type);
+
 /*
  * Flags that flow alongside events in the input dispatch system to help with certain
  * policy decisions such as waking from device sleep.
@@ -687,6 +689,28 @@
 };
 
 /*
+ * Focus events.
+ */
+class FocusEvent : public InputEvent {
+public:
+    virtual ~FocusEvent() {}
+
+    virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_FOCUS; }
+
+    inline bool getHasFocus() const { return mHasFocus; }
+
+    inline bool getInTouchMode() const { return mInTouchMode; }
+
+    void initialize(bool hasFocus, bool inTouchMode);
+
+    void initialize(const FocusEvent& from);
+
+protected:
+    bool mHasFocus;
+    bool mInTouchMode;
+};
+
+/*
  * Input event factory.
  */
 class InputEventFactoryInterface {
@@ -698,6 +722,7 @@
 
     virtual KeyEvent* createKeyEvent() = 0;
     virtual MotionEvent* createMotionEvent() = 0;
+    virtual FocusEvent* createFocusEvent() = 0;
 };
 
 /*
@@ -709,12 +734,14 @@
     PreallocatedInputEventFactory() { }
     virtual ~PreallocatedInputEventFactory() { }
 
-    virtual KeyEvent* createKeyEvent() { return & mKeyEvent; }
-    virtual MotionEvent* createMotionEvent() { return & mMotionEvent; }
+    virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; }
+    virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; }
+    virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; }
 
 private:
     KeyEvent mKeyEvent;
     MotionEvent mMotionEvent;
+    FocusEvent mFocusEvent;
 };
 
 /*
@@ -725,16 +752,18 @@
     explicit PooledInputEventFactory(size_t maxPoolSize = 20);
     virtual ~PooledInputEventFactory();
 
-    virtual KeyEvent* createKeyEvent();
-    virtual MotionEvent* createMotionEvent();
+    virtual KeyEvent* createKeyEvent() override;
+    virtual MotionEvent* createMotionEvent() override;
+    virtual FocusEvent* createFocusEvent() override;
 
     void recycle(InputEvent* event);
 
 private:
     const size_t mMaxPoolSize;
 
-    Vector<KeyEvent*> mKeyEventPool;
-    Vector<MotionEvent*> mMotionEventPool;
+    std::queue<std::unique_ptr<KeyEvent>> mKeyEventPool;
+    std::queue<std::unique_ptr<MotionEvent>> mMotionEventPool;
+    std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool;
 };
 
 } // namespace android
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 94d90ad..ae47438 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -64,6 +64,7 @@
         KEY,
         MOTION,
         FINISHED,
+        FOCUS,
     };
 
     struct Header {
@@ -92,9 +93,7 @@
             uint32_t empty2;
             nsecs_t downTime __attribute__((aligned(8)));
 
-            inline size_t size() const {
-                return sizeof(Key);
-            }
+            inline size_t size() const { return sizeof(Key); }
         } key;
 
         struct Motion {
@@ -110,7 +109,7 @@
             int32_t metaState;
             int32_t buttonState;
             MotionClassification classification; // base type: uint8_t
-            uint8_t empty2[3];
+            uint8_t empty2[3];                   // 3 bytes to fill gap created by classification
             int32_t edgeFlags;
             nsecs_t downTime __attribute__((aligned(8)));
             float xOffset;
@@ -121,11 +120,16 @@
             float yCursorPosition;
             uint32_t pointerCount;
             uint32_t empty3;
-            // Note that PointerCoords requires 8 byte alignment.
+            /**
+             * The "pointers" field must be the last field of the struct InputMessage.
+             * When we send the struct InputMessage across the socket, we are not
+             * writing the entire "pointers" array, but only the pointerCount portion
+             * of it as an optimization. Adding a field after "pointers" would break this.
+             */
             struct Pointer {
                 PointerProperties properties;
                 PointerCoords coords;
-            } pointers[MAX_POINTERS];
+            } pointers[MAX_POINTERS] __attribute__((aligned(8)));
 
             int32_t getActionId() const {
                 uint32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
@@ -141,12 +145,19 @@
 
         struct Finished {
             uint32_t seq;
-            bool handled;
+            uint32_t handled; // actually a bool, but we must maintain 8-byte alignment
 
-            inline size_t size() const {
-                return sizeof(Finished);
-            }
+            inline size_t size() const { return sizeof(Finished); }
         } finished;
+
+        struct Focus {
+            uint32_t seq;
+            // The following two fields take up 4 bytes total
+            uint16_t hasFocus;    // actually a bool
+            uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment
+
+            inline size_t size() const { return sizeof(Focus); }
+        } focus;
     } __attribute__((aligned(8))) body;
 
     bool isValid(size_t actualSize) const;
@@ -289,6 +300,15 @@
                                 uint32_t pointerCount, const PointerProperties* pointerProperties,
                                 const PointerCoords* pointerCoords);
 
+    /* Publishes a focus event to the input channel.
+     *
+     * Returns OK on success.
+     * Returns WOULD_BLOCK if the channel is full.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode);
+
     /* Receives the finished signal from the consumer in reply to the original dispatch signal.
      * If a signal was received, returns the message sequence number,
      * and whether the consumer handled the message.
@@ -344,8 +364,8 @@
      * Returns NO_MEMORY if the event could not be created.
      * Other errors probably indicate that the channel is broken.
      */
-    status_t consume(InputEventFactoryInterface* factory, bool consumeBatches,
-            nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
+    status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime,
+                     uint32_t* outSeq, InputEvent** outEvent);
 
     /* Sends a finished signal to the publisher to inform it that the message
      * with the specified sequence number has finished being process and whether
@@ -516,6 +536,7 @@
     static void rewriteMessage(TouchState& state, InputMessage& msg);
     static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
     static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
+    static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
     static void addSample(MotionEvent* event, const InputMessage* msg);
     static bool canAddSample(const Batch& batch, const InputMessage* msg);
     static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
diff --git a/include/ui b/include/ui
deleted file mode 120000
index 2fb3147..0000000
--- a/include/ui
+++ /dev/null
@@ -1 +0,0 @@
-../libs/ui/include/ui
\ No newline at end of file
diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h
new file mode 120000
index 0000000..9a195ea
--- /dev/null
+++ b/include/ui/DisplayInfo.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/DisplayInfo.h
\ No newline at end of file
diff --git a/include/ui/FloatRect.h b/include/ui/FloatRect.h
new file mode 120000
index 0000000..d7bd737
--- /dev/null
+++ b/include/ui/FloatRect.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/FloatRect.h
\ No newline at end of file
diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h
new file mode 120000
index 0000000..4085433
--- /dev/null
+++ b/include/ui/PixelFormat.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/PixelFormat.h
\ No newline at end of file
diff --git a/include/ui/Point.h b/include/ui/Point.h
new file mode 120000
index 0000000..443938b
--- /dev/null
+++ b/include/ui/Point.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Point.h
\ No newline at end of file
diff --git a/include/ui/PublicFormat.h b/include/ui/PublicFormat.h
new file mode 120000
index 0000000..7984c0e
--- /dev/null
+++ b/include/ui/PublicFormat.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/PublicFormat.h
\ No newline at end of file
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
new file mode 120000
index 0000000..a99c5f2
--- /dev/null
+++ b/include/ui/Rect.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Rect.h
\ No newline at end of file
diff --git a/include/ui/Region.h b/include/ui/Region.h
new file mode 120000
index 0000000..2e46e0f
--- /dev/null
+++ b/include/ui/Region.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Region.h
\ No newline at end of file
diff --git a/include/ui/Size.h b/include/ui/Size.h
new file mode 120000
index 0000000..c0da99b
--- /dev/null
+++ b/include/ui/Size.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Size.h
\ No newline at end of file
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 8d72a6b..79d9b79 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -109,7 +109,27 @@
 
 
 #define __IINTF_CONCAT(x, y) (x ## y)
+
+#ifndef DO_NOT_CHECK_MANUAL_BINDER_INTERFACES
+
 #define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
+    static_assert(internal::allowedManualInterface(NAME),               \
+                  "b/64223827: Manually written binder interfaces are " \
+                  "considered error prone and frequently have bugs. "   \
+                  "The preferred way to add interfaces is to define "   \
+                  "an .aidl file to auto-generate the interface. If "   \
+                  "an interface must be manually written, add its "     \
+                  "name to the whitelist.");                            \
+    DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)    \
+
+#else
+
+#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
+    DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)    \
+
+#endif
+
+#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)\
     const ::android::StaticString16                                     \
         I##INTERFACE##_descriptor_static_str16(__IINTF_CONCAT(u, NAME));\
     const ::android::String16 I##INTERFACE::descriptor(                 \
@@ -192,6 +212,122 @@
 
 // ----------------------------------------------------------------------
 
+namespace internal {
+constexpr const char* const kManualInterfaces[] = {
+  "android.app.IActivityManager",
+  "android.app.IUidObserver",
+  "android.drm.IDrm",
+  "android.dvr.IVsyncCallback",
+  "android.dvr.IVsyncService",
+  "android.gfx.tests.ICallback",
+  "android.gfx.tests.IIPCTest",
+  "android.gfx.tests.ISafeInterfaceTest",
+  "android.graphicsenv.IGpuService",
+  "android.gui.DisplayEventConnection",
+  "android.gui.IConsumerListener",
+  "android.gui.IGraphicBufferConsumer",
+  "android.gui.IRegionSamplingListener",
+  "android.gui.ITransactionComposerListener",
+  "android.gui.SensorEventConnection",
+  "android.gui.SensorServer",
+  "android.hardware.ICamera",
+  "android.hardware.ICameraClient",
+  "android.hardware.ICameraRecordingProxy",
+  "android.hardware.ICameraRecordingProxyListener",
+  "android.hardware.ICrypto",
+  "android.hardware.IOMXObserver",
+  "android.hardware.ISoundTrigger",
+  "android.hardware.ISoundTriggerClient",
+  "android.hardware.ISoundTriggerHwService",
+  "android.hardware.IStreamListener",
+  "android.hardware.IStreamSource",
+  "android.input.IInputFlinger",
+  "android.input.ISetInputWindowsListener",
+  "android.media.IAudioFlinger",
+  "android.media.IAudioFlingerClient",
+  "android.media.IAudioPolicyService",
+  "android.media.IAudioPolicyServiceClient",
+  "android.media.IAudioService",
+  "android.media.IAudioTrack",
+  "android.media.IDataSource",
+  "android.media.IDrmClient",
+  "android.media.IEffect",
+  "android.media.IEffectClient",
+  "android.media.IMediaAnalyticsService",
+  "android.media.IMediaCodecList",
+  "android.media.IMediaDrmService",
+  "android.media.IMediaExtractor",
+  "android.media.IMediaExtractorService",
+  "android.media.IMediaHTTPConnection",
+  "android.media.IMediaHTTPService",
+  "android.media.IMediaLogService",
+  "android.media.IMediaMetadataRetriever",
+  "android.media.IMediaPlayer",
+  "android.media.IMediaPlayerClient",
+  "android.media.IMediaPlayerService",
+  "android.media.IMediaRecorder",
+  "android.media.IMediaRecorderClient",
+  "android.media.IMediaResourceMonitor",
+  "android.media.IMediaSource",
+  "android.media.IRemoteDisplay",
+  "android.media.IRemoteDisplayClient",
+  "android.media.IResourceManagerClient",
+  "android.media.IResourceManagerService",
+  "android.os.IComplexTypeInterface",
+  "android.os.IPermissionController",
+  "android.os.IPingResponder",
+  "android.os.IPowerManager",
+  "android.os.IProcessInfoService",
+  "android.os.ISchedulingPolicyService",
+  "android.os.IStringConstants",
+  "android.os.storage.IObbActionListener",
+  "android.os.storage.IStorageEventListener",
+  "android.os.storage.IStorageManager",
+  "android.os.storage.IStorageShutdownObserver",
+  "android.service.vr.IPersistentVrStateCallbacks",
+  "android.service.vr.IVrManager",
+  "android.service.vr.IVrStateCallbacks",
+  "android.ui.ISurfaceComposer",
+  "android.ui.ISurfaceComposerClient",
+  "android.utils.IMemory",
+  "android.utils.IMemoryHeap",
+  "com.android.car.procfsinspector.IProcfsInspector",
+  "com.android.internal.app.IAppOpsCallback",
+  "com.android.internal.app.IAppOpsService",
+  "com.android.internal.app.IBatteryStats",
+  "com.android.internal.os.IResultReceiver",
+  "com.android.internal.os.IShellCallback",
+  "drm.IDrmManagerService",
+  "drm.IDrmServiceListener",
+  "IAAudioClient",
+  "IAAudioService",
+  "VtsFuzzer",
+  nullptr,
+};
+
+constexpr const char* const kDownstreamManualInterfaces[] = {
+  // Add downstream interfaces here.
+  nullptr,
+};
+
+constexpr bool equals(const char* a, const char* b) {
+  if (*a != *b) return false;
+  if (*a == '\0') return true;
+  return equals(a + 1, b + 1);
+}
+
+constexpr bool inList(const char* a, const char* const* whitelist) {
+  if (*whitelist == nullptr) return false;
+  if (equals(a, *whitelist)) return true;
+  return inList(a, whitelist + 1);
+}
+
+constexpr bool allowedManualInterface(const char* name) {
+  return inList(name, kManualInterfaces) ||
+         inList(name, kDownstreamManualInterfaces);
+}
+
+} // namespace internal
 } // namespace android
 
 #endif // ANDROID_IINTERFACE_H
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 56b94c1..0477801 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -16,7 +16,9 @@
 #include <set>
 
 #include <android-base/file.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <dumputils/dump_utils.h>
 #include <log/log.h>
@@ -68,13 +70,34 @@
         NULL,
 };
 
-bool should_dump_hal_interface(const char* interface) {
+/* list of extra hal interfaces to dump containing process during native dumps */
+// This is filled when dumpstate is called.
+static std::set<const std::string> extra_hal_interfaces_to_dump;
+
+static void read_extra_hals_to_dump_from_property() {
+    // extra hals to dump are already filled
+    if (extra_hal_interfaces_to_dump.size() > 0) {
+        return;
+    }
+    std::string value = android::base::GetProperty("ro.dump.hals.extra", "");
+    std::vector<std::string> tokens = android::base::Split(value, ",");
+    for (const auto &token : tokens) {
+        std::string trimmed_token = android::base::Trim(token);
+        if (trimmed_token.length() == 0) {
+            continue;
+        }
+        extra_hal_interfaces_to_dump.insert(trimmed_token);
+    }
+}
+
+// check if interface is included in either default hal list or extra hal list
+bool should_dump_hal_interface(const std::string& interface) {
     for (const char** i = hal_interfaces_to_dump; *i; i++) {
-        if (!strcmp(*i, interface)) {
+        if (interface == *i) {
             return true;
         }
     }
-    return false;
+    return extra_hal_interfaces_to_dump.find(interface) != extra_hal_interfaces_to_dump.end();
 }
 
 bool should_dump_native_traces(const char* path) {
@@ -94,13 +117,15 @@
     sp<IServiceManager> manager = IServiceManager::getService();
     std::set<int> pids;
 
+    read_extra_hals_to_dump_from_property();
+
     Return<void> ret = manager->debugDump([&](auto& hals) {
         for (const auto &info : hals) {
             if (info.pid == static_cast<int>(IServiceManager::PidConstant::NO_PID)) {
                 continue;
             }
 
-            if (!should_dump_hal_interface(info.interfaceName.c_str())) {
+            if (!should_dump_hal_interface(info.interfaceName)) {
                 continue;
             }
 
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 3bded7f..0330dac 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -39,77 +39,210 @@
 
 namespace gralloc4 {
 
-bool isStandardMetadataType(const MetadataType& metadataType) {
-    return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE, metadataType.name.size());
+static inline bool hasAdditionOverflow(size_t a, size_t b) {
+    return a > SIZE_MAX - b;
 }
 
-StandardMetadataType getStandardMetadataTypeValue(const MetadataType& metadataType) {
-    return static_cast<StandardMetadataType>(metadataType.value);
-}
+/**
+ * OutputHidlVec represents the hidl_vec that is outputed when a type is encoded into a byte stream.
+ * This class is used to track the current state of a hidl_vec as it is filled with the encoded
+ * byte stream.
+ *
+ * This type is needed because hidl_vec's resize() allocates a new backing array every time.
+ * This type does not need an copies and only needs one resize operation.
+ */
+class OutputHidlVec {
+public:
+    OutputHidlVec(hidl_vec<uint8_t>* vec)
+        : mVec(vec) {}
 
-status_t copyToHidlVec(const std::vector<uint8_t>& vec, hidl_vec<uint8_t>* hidlVec) {
-    if (!hidlVec) {
-        return BAD_VALUE;
+    status_t resize() {
+        if (!mVec) {
+            return BAD_VALUE;
+        }
+        mVec->resize(mNeededResize);
+        return NO_ERROR;
     }
 
-    hidlVec->setToExternal(const_cast<uint8_t*>(vec.data()), vec.size(), false /*shouldOwn*/);
+    status_t encode(const uint8_t* data, size_t size) {
+        if (!mVec) {
+            return BAD_VALUE;
+        }
+        if (mVec->size() == 0) {
+            if (hasAdditionOverflow(mNeededResize, size)) {
+                clear();
+                return BAD_VALUE;
+            }
+            mNeededResize += size;
+            return NO_ERROR;
+        }
+
+        if (hasAdditionOverflow(mOffset, size) || (mVec->size() < size + mOffset)) {
+            clear();
+            return BAD_VALUE;
+        }
+
+        std::copy(data, data + size, mVec->data() + mOffset);
+
+        mOffset += size;
+        return NO_ERROR;
+    }
+
+    void clear() {
+        if (mVec) {
+            mVec->resize(0);
+        }
+        mNeededResize = 0;
+        mOffset = 0;
+    }
+
+private:
+    hidl_vec<uint8_t>* mVec;
+    size_t mNeededResize = 0;
+    size_t mOffset = 0;
+};
+
+/**
+ * InputHidlVec represents the hidl_vec byte stream that is inputed when a type is decoded.
+ * This class is used to track the current index of the byte stream of the hidl_vec as it is
+ * decoded.
+ */
+class InputHidlVec {
+public:
+    InputHidlVec(const hidl_vec<uint8_t>* vec)
+        : mVec(vec) {}
+
+    status_t decode(uint8_t* data, size_t size) {
+        if (!mVec || hasAdditionOverflow(mOffset, size) || mOffset + size > mVec->size()) {
+            return BAD_VALUE;
+        }
+
+        std::copy(mVec->data() + mOffset, mVec->data() + mOffset + size, data);
+
+        mOffset += size;
+        return NO_ERROR;
+    }
+
+    status_t decode(std::string* string, size_t size) {
+        if (!mVec || hasAdditionOverflow(mOffset, size) || mOffset + size > mVec->size()) {
+            return BAD_VALUE;
+        }
+
+        string->assign(mVec->data() + mOffset, mVec->data() + mOffset + size);
+
+        mOffset += size;
+        return NO_ERROR;
+    }
+
+    bool hasRemainingData() {
+        if (!mVec) {
+            return false;
+        }
+        return mVec->size() - mOffset;
+    }
+
+private:
+    const hidl_vec<uint8_t>* mVec;
+    size_t mOffset = 0;
+};
+
+/**
+ * EncodeHelper is a function type that encodes T into the OutputHidlVec.
+ */
+template<class T>
+using EncodeHelper = status_t(*)(const T&, OutputHidlVec*);
+
+/**
+ * DecodeHelper is a function type that decodes InputHidlVec into T.
+ */
+template<class T>
+using DecodeHelper = status_t(*)(InputHidlVec*, T*);
+
+/**
+ * ErrorHandler is a function type that is called when the corresponding DecodeHelper function
+ * fails. ErrorHandler cleans up the object T so the caller doesn't receive a partially created
+ * T.
+ */
+template<class T>
+using ErrorHandler = void(*)(T*);
+
+/**
+ * encode is the main encoding function. It takes in T and uses the encodeHelper function to turn T
+ * into the hidl_vec byte stream.
+ *
+ * This function first calls the encodeHelper function to determine how large the hidl_vec
+ * needs to be. It resizes the hidl_vec. Finally, it reruns the encodeHelper function which
+ * 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) {
+    OutputHidlVec outputHidlVec{output};
+    status_t err = encodeHelper(input, &outputHidlVec);
+    if (err) {
+        return err;
+    }
+
+    err = outputHidlVec.resize();
+    if (err) {
+        return err;
+    }
+
+    return encodeHelper(input, &outputHidlVec);
+}
+
+/**
+ * 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) {
+    InputHidlVec inputHidlVec{&input};
+    status_t err = decodeHelper(&inputHidlVec, output);
+    if (err) {
+        return err;
+    }
+
+    err = inputHidlVec.hasRemainingData();
+    if (err) {
+        if (errorHandler) {
+            errorHandler(output);
+        }
+        return BAD_VALUE;
+    }
 
     return NO_ERROR;
 }
 
+/**
+ * Private helper functions
+ */
 template <class T>
-status_t encodeInteger(T input, std::vector<uint8_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);
+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);
     if (!output) {
         return BAD_VALUE;
     }
 
-    size_t outputOffset = output->size();
-    size_t size = sizeof(input);
-
-    if (outputOffset > UINT_MAX - size) {
-        return BAD_VALUE;
-    }
-    output->resize(size + outputOffset);
-
-    uint8_t* tmp = reinterpret_cast<uint8_t*>(&input);
-    std::copy(tmp, tmp + size, output->data() + outputOffset);
-
-    return NO_ERROR;
+    const uint8_t* tmp = reinterpret_cast<const uint8_t*>(&input);
+    return output->encode(tmp, sizeof(input));
 }
 
 template <class T>
-status_t decodeInteger(const hidl_vec<uint8_t>& input, T* output, size_t* inputOffset = nullptr) {
+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);
     if (!output) {
         return BAD_VALUE;
     }
 
-    size_t offset = (inputOffset)? *inputOffset: 0;
-    if (offset >= input.size()) {
-        return BAD_VALUE;
-    }
-    size_t inputMaxSize = input.size() - offset;
-    size_t outputSize = sizeof(*output);
-
-    if (inputMaxSize < outputSize) {
-        return BAD_VALUE;
-    }
-
     uint8_t* tmp = reinterpret_cast<uint8_t*>(output);
-    const uint8_t* data = input.data() + offset;
-    std::copy(data, data + outputSize, tmp);
-
-    if (inputOffset) {
-        *inputOffset += outputSize;
-    }
-
-    return NO_ERROR;
+    return input->decode(tmp, sizeof(*output));
 }
 
-status_t encodeString(const std::string& input, std::vector<uint8_t>* output) {
+status_t encodeString(const std::string& input, OutputHidlVec* output) {
     if (!output) {
         return BAD_VALUE;
     }
@@ -119,66 +252,60 @@
         return err;
     }
 
-    size_t outputOffset = output->size();
-    size_t size = input.size();
-    output->resize(size + outputOffset);
-
-    std::copy(input.c_str(), input.c_str() + size, output->data() + outputOffset);
-
-    return NO_ERROR;
+    return output->encode(reinterpret_cast<const uint8_t*>(input.c_str()), input.size());
 }
 
-status_t decodeString(const hidl_vec<uint8_t>& input, std::string* output, size_t* inputOffset = nullptr) {
+status_t decodeString(InputHidlVec* input, std::string* output) {
     if (!output) {
         return BAD_VALUE;
     }
 
     int64_t size = 0;
-    status_t err = decodeInteger<int64_t>(input, &size, inputOffset);
+    status_t err = decodeInteger<int64_t>(input, &size);
     if (err || size < 0) {
         return err;
     }
 
-    size_t offset = (inputOffset)? *inputOffset + sizeof(size): sizeof(size);
-    if ((offset > UINT_MAX - size) || (offset + size > input.size())) {
-        return BAD_VALUE;
-    }
-
-    auto data = input.data() + offset;
-    output->assign(data, data + size);
-
-    if (inputOffset) {
-        *inputOffset += size;
-    }
-
-    return NO_ERROR;
+    return input->decode(output, size);
 }
 
-status_t encodeExtendableType(const ExtendableType& input, std::vector<uint8_t>* output) {
+status_t encodeExtendableType(const ExtendableType& input, OutputHidlVec* output) {
     status_t err = encodeString(input.name, output);
     if (err) {
         return err;
     }
 
-    return encodeInteger<int64_t>(input.value, output);
-}
-
-status_t decodeExtendableType(const hidl_vec<uint8_t>& input, ExtendableType* output, size_t* inputOffset = nullptr) {
-    status_t err = decodeString(input, &output->name, inputOffset);
+    err = encodeInteger<int64_t>(input.value, output);
     if (err) {
         return err;
     }
 
-    err = decodeInteger<int64_t>(input, &output->value, inputOffset);
-    if (err) {
-        output->name.clear();
-        return err;
-    }
-
     return NO_ERROR;
 }
 
-status_t encodeRect(const Rect& input, std::vector<uint8_t>* output) {
+status_t decodeExtendableType(InputHidlVec* input, ExtendableType* output) {
+    status_t err = decodeString(input, &output->name);
+    if (err) {
+        return err;
+    }
+
+    err = decodeInteger<int64_t>(input, &output->value);
+    if (err) {
+        return err;
+    }
+
+    return NO_ERROR;
+}
+
+void clearExtendableType(ExtendableType* output) {
+    if (!output) {
+        return;
+    }
+    output->name.clear();
+    output->value = 0;
+}
+
+status_t encodeRect(const Rect& input, OutputHidlVec* output) {
     status_t err = encodeInteger<int32_t>(static_cast<int32_t>(input.left), output);
     if (err) {
         return err;
@@ -194,23 +321,23 @@
     return encodeInteger<int32_t>(static_cast<int32_t>(input.bottom), output);
 }
 
-status_t decodeRect(const hidl_vec<uint8_t>& input, Rect* output, size_t* inputOffset = nullptr) {
-    status_t err = decodeInteger<int32_t>(input, &output->left, inputOffset);
+status_t decodeRect(InputHidlVec* input, Rect* output) {
+    status_t err = decodeInteger<int32_t>(input, &output->left);
     if (err) {
         return err;
     }
-    err = decodeInteger<int32_t>(input, &output->top, inputOffset);
+    err = decodeInteger<int32_t>(input, &output->top);
     if (err) {
         return err;
     }
-    err = decodeInteger<int32_t>(input, &output->right, inputOffset);
+    err = decodeInteger<int32_t>(input, &output->right);
     if (err) {
         return err;
     }
-    return decodeInteger<int32_t>(input, &output->bottom, inputOffset);
+    return decodeInteger<int32_t>(input, &output->bottom);
 }
 
-status_t encodePlaneLayoutComponent(const PlaneLayoutComponent& input, std::vector<uint8_t>* output) {
+status_t encodePlaneLayoutComponent(const PlaneLayoutComponent& input, OutputHidlVec* output) {
     if (!output) {
         return BAD_VALUE;
     }
@@ -226,23 +353,23 @@
     return encodeInteger<int64_t>(static_cast<int64_t>(input.sizeInBits), output);
 }
 
-status_t decodePlaneLayoutComponent(const hidl_vec<uint8_t>& input, PlaneLayoutComponent* output, size_t* inputOffset = nullptr) {
+status_t decodePlaneLayoutComponent(InputHidlVec* input, PlaneLayoutComponent* output) {
     if (!output) {
         return BAD_VALUE;
     }
 
-    status_t err = decodeExtendableType(input, &output->type, inputOffset);
+    status_t err = decodeExtendableType(input, &output->type);
     if (err) {
         return err;
     }
-    err = decodeInteger<int64_t>(input, &output->offsetInBits, inputOffset);
+    err = decodeInteger<int64_t>(input, &output->offsetInBits);
     if (err) {
         return err;
     }
-    return decodeInteger<int64_t>(input, &output->sizeInBits, inputOffset);
+    return decodeInteger<int64_t>(input, &output->sizeInBits);
 }
 
-status_t encodePlaneLayoutComponents(const std::vector<PlaneLayoutComponent>& input, std::vector<uint8_t>* output) {
+status_t encodePlaneLayoutComponents(const std::vector<PlaneLayoutComponent>& input, OutputHidlVec* output) {
     if (!output) {
         return BAD_VALUE;
     }
@@ -262,20 +389,20 @@
     return NO_ERROR;
 }
 
-status_t decodePlaneLayoutComponents(const hidl_vec<uint8_t>& input, std::vector<PlaneLayoutComponent>* output, size_t* inputOffset = nullptr) {
+status_t decodePlaneLayoutComponents(InputHidlVec* input, std::vector<PlaneLayoutComponent>* output) {
     if (!output) {
         return BAD_VALUE;
     }
 
     int64_t size = 0;
-    status_t err = decodeInteger<int64_t>(input, &size, inputOffset);
+    status_t err = decodeInteger<int64_t>(input, &size);
     if (err || size < 0) {
         return err;
     }
 
     for (int i = 0; i < size; i++) {
         output->emplace_back();
-        err = decodePlaneLayoutComponent(input, &output->back(), inputOffset);
+        err = decodePlaneLayoutComponent(input, &output->back());
         if (err) {
             return err;
         }
@@ -283,7 +410,7 @@
     return NO_ERROR;
 }
 
-status_t encodePlaneLayout(const PlaneLayout& input, std::vector<uint8_t>* output) {
+status_t encodePlaneLayout(const PlaneLayout& input, OutputHidlVec* output) {
     if (!output) {
         return BAD_VALUE;
     }
@@ -329,307 +456,239 @@
     return encodeRect(input.crop, output);
 }
 
-status_t decodePlaneLayout(const hidl_vec<uint8_t>& input, PlaneLayout* output, size_t* inputOffset = nullptr) {
+status_t decodePlaneLayout(InputHidlVec* input, PlaneLayout* output) {
     if (!output) {
         return BAD_VALUE;
     }
 
-    status_t err = decodePlaneLayoutComponents(input, &output->components, inputOffset);
+    status_t err = decodePlaneLayoutComponents(input, &output->components);
     if (err) {
         return err;
     }
 
-    err = decodeInteger<int64_t>(input, &output->offsetInBytes, inputOffset);
+    err = decodeInteger<int64_t>(input, &output->offsetInBytes);
     if (err) {
         return err;
     }
-    err = decodeInteger<int64_t>(input, &output->sampleIncrementInBits, inputOffset);
+    err = decodeInteger<int64_t>(input, &output->sampleIncrementInBits);
     if (err) {
         return err;
     }
-    err = decodeInteger<int64_t>(input, &output->strideInBytes, inputOffset);
+    err = decodeInteger<int64_t>(input, &output->strideInBytes);
     if (err) {
         return err;
     }
-    err = decodeInteger<int64_t>(input, &output->widthInSamples, inputOffset);
+    err = decodeInteger<int64_t>(input, &output->widthInSamples);
     if (err) {
         return err;
     }
-    err = decodeInteger<int64_t>(input, &output->heightInSamples, inputOffset);
+    err = decodeInteger<int64_t>(input, &output->heightInSamples);
     if (err) {
         return err;
     }
-    err = decodeInteger<int64_t>(input, &output->totalSizeInBytes, inputOffset);
+    err = decodeInteger<int64_t>(input, &output->totalSizeInBytes);
     if (err) {
         return err;
     }
-    err = decodeInteger<int64_t>(input, &output->horizontalSubsampling, inputOffset);
+    err = decodeInteger<int64_t>(input, &output->horizontalSubsampling);
     if (err) {
         return err;
     }
-    err = decodeInteger<int64_t>(input, &output->verticalSubsampling, inputOffset);
+    err = decodeInteger<int64_t>(input, &output->verticalSubsampling);
     if (err) {
         return err;
     }
 
-    return decodeRect(input, &output->crop, inputOffset);
+    return decodeRect(input, &output->crop);
 }
 
-status_t encodePlaneLayouts(const std::vector<PlaneLayout>& planeLayouts, hidl_vec<uint8_t>* outPlaneLayouts) {
-    if (!outPlaneLayouts) {
-        return BAD_VALUE;
-    }
-
-    std::vector<uint8_t> tmpOutPlaneLayouts;
-
-    status_t err = encodeInteger<int64_t>(static_cast<int64_t>(planeLayouts.size()), &tmpOutPlaneLayouts);
+status_t encodePlaneLayoutsHelper(const std::vector<PlaneLayout>& planeLayouts, OutputHidlVec* outOutputHidlVec) {
+    status_t err = encodeInteger<int64_t>(static_cast<int64_t>(planeLayouts.size()), outOutputHidlVec);
     if (err) {
         return err;
     }
 
     for (const auto& planeLayout : planeLayouts) {
-        err = encodePlaneLayout(planeLayout, &tmpOutPlaneLayouts);
+        err = encodePlaneLayout(planeLayout, outOutputHidlVec);
         if (err) {
             return err;
         }
     }
 
-    return copyToHidlVec(tmpOutPlaneLayouts, outPlaneLayouts);
+    return NO_ERROR;
 }
 
-status_t decodePlaneLayouts(const hidl_vec<uint8_t>& planeLayouts, std::vector<PlaneLayout>* outPlaneLayouts) {
-    if (!outPlaneLayouts) {
-        return BAD_VALUE;
-    }
-
-    size_t offset = 0;
+status_t decodePlaneLayoutsHelper(InputHidlVec* inputHidlVec, std::vector<PlaneLayout>* outPlaneLayouts) {
     int64_t size = 0;
-    status_t err = decodeInteger<int64_t>(planeLayouts, &size, &offset);
+    status_t err = decodeInteger<int64_t>(inputHidlVec, &size);
     if (err || size < 0) {
         return err;
     }
 
     for (size_t i = 0; i < size; i++) {
         outPlaneLayouts->emplace_back();
-        err = decodePlaneLayout(planeLayouts, &outPlaneLayouts->back(), &offset);
+        err = decodePlaneLayout(inputHidlVec, &outPlaneLayouts->back());
         if (err) {
-            outPlaneLayouts->resize(0);
             return err;
         }
     }
-    if (offset < planeLayouts.size()) {
-        return BAD_VALUE;
-    }
-
     return NO_ERROR;
 }
 
-status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
-    std::vector<uint8_t> tmpOutBufferId;
-    status_t err = encodeInteger<uint64_t>(bufferId, &tmpOutBufferId);
-    if (err) {
-        return err;
+void clearPlaneLayouts(std::vector<PlaneLayout>* output) {
+    if (!output) {
+        return;
     }
-    return copyToHidlVec(tmpOutBufferId, outBufferId);
+    output->clear();
+}
+
+/**
+ * Public API functions
+ */
+bool isStandardMetadataType(const MetadataType& metadataType) {
+    return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE, metadataType.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);
 }
 
 status_t decodeBufferId(const hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId) {
-    return decodeInteger<uint64_t>(bufferId, outBufferId);
+    return decode(bufferId, outBufferId, decodeInteger);
 }
 
 status_t encodeName(const std::string& name, hidl_vec<uint8_t>* outName) {
-    std::vector<uint8_t> tmpOutName;
-    status_t err = encodeString(name, &tmpOutName);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutName, outName);
+    return encode(name, outName, encodeString);
 }
 
 status_t decodeName(const hidl_vec<uint8_t>& name, std::string* outName) {
-    return decodeString(name, outName);
+    return decode(name, outName, decodeString);
 }
 
 status_t encodeWidth(uint64_t width, hidl_vec<uint8_t>* outWidth) {
-    std::vector<uint8_t> tmpOutWidth;
-    status_t err = encodeInteger<uint64_t>(width, &tmpOutWidth);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutWidth, outWidth);
+    return encode(width, outWidth, encodeInteger);
 }
 
 status_t decodeWidth(const hidl_vec<uint8_t>& width, uint64_t* outWidth) {
-    return decodeInteger<uint64_t>(width, outWidth);
+    return decode(width, outWidth, decodeInteger);
 }
 
 status_t encodeHeight(uint64_t height, hidl_vec<uint8_t>* outHeight) {
-    std::vector<uint8_t> tmpOutHeight;
-    status_t err = encodeInteger<uint64_t>(height, &tmpOutHeight);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutHeight, outHeight);
+    return encode(height, outHeight, encodeInteger);
 }
 
 status_t decodeHeight(const hidl_vec<uint8_t>& height, uint64_t* outHeight) {
-    return decodeInteger<uint64_t>(height, outHeight);
+    return decode(height, outHeight, decodeInteger);
 }
 
 status_t encodeLayerCount(uint64_t layerCount, hidl_vec<uint8_t>* outLayerCount) {
-    std::vector<uint8_t> tmpOutLayerCount;
-    status_t err = encodeInteger<uint64_t>(layerCount, &tmpOutLayerCount);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutLayerCount, outLayerCount);
+    return encode(layerCount, outLayerCount, encodeInteger);
 }
 
 status_t decodeLayerCount(const hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount) {
-    return decodeInteger<uint64_t>(layerCount, outLayerCount);
+    return decode(layerCount, outLayerCount, decodeInteger);
 }
 
-status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested, hidl_vec<uint8_t>* outPixelFormatRequested) {
-    std::vector<uint8_t> tmpOutPixelFormatRequested;
-    status_t err = encodeInteger<int32_t>(static_cast<int32_t>(pixelFormatRequested), &tmpOutPixelFormatRequested);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutPixelFormatRequested, outPixelFormatRequested);
+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 decodeInteger<int32_t>(pixelFormatRequested, reinterpret_cast<int32_t*>(outPixelFormatRequested));
+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) {
-    std::vector<uint8_t> tmpOutPixelFormatFourCC;
-    status_t err = encodeInteger<uint32_t>(pixelFormatFourCC, &tmpOutPixelFormatFourCC);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutPixelFormatFourCC, outPixelFormatFourCC);
+    return encode(pixelFormatFourCC, outPixelFormatFourCC, encodeInteger);
 }
 
 status_t decodePixelFormatFourCC(const hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC) {
-    return decodeInteger<uint32_t>(pixelFormatFourCC, outPixelFormatFourCC);
+    return decode(pixelFormatFourCC, outPixelFormatFourCC, decodeInteger);
 }
 
 status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, hidl_vec<uint8_t>* outPixelFormatModifier) {
-    std::vector<uint8_t> tmpOutPixelFormatModifier;
-    status_t err = encodeInteger<uint64_t>(pixelFormatModifier, &tmpOutPixelFormatModifier);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutPixelFormatModifier, outPixelFormatModifier);
+    return encode(pixelFormatModifier, outPixelFormatModifier, encodeInteger);
 }
 
 status_t decodePixelFormatModifier(const hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier) {
-    return decodeInteger<uint64_t>(pixelFormatModifier, outPixelFormatModifier);
+    return decode(pixelFormatModifier, outPixelFormatModifier, decodeInteger);
 }
 
 status_t encodeUsage(uint64_t usage, hidl_vec<uint8_t>* outUsage) {
-    std::vector<uint8_t> tmpOutUsage;
-    status_t err = encodeInteger<uint64_t>(usage, &tmpOutUsage);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutUsage, outUsage);
+    return encode(usage, outUsage, encodeInteger);
 }
 
 status_t decodeUsage(const hidl_vec<uint8_t>& usage, uint64_t* outUsage) {
-    return decodeInteger<uint64_t>(usage, outUsage);
+    return decode(usage, outUsage, decodeInteger);
 }
 
 status_t encodeAllocationSize(uint64_t allocationSize, hidl_vec<uint8_t>* outAllocationSize) {
-    std::vector<uint8_t> tmpOutAllocationSize;
-    status_t err = encodeInteger<uint64_t>(allocationSize, &tmpOutAllocationSize);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutAllocationSize, outAllocationSize);
+    return encode(allocationSize, outAllocationSize, encodeInteger);
 }
 
 status_t decodeAllocationSize(const hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize) {
-    return decodeInteger<uint64_t>(allocationSize, outAllocationSize);
+    return decode(allocationSize, outAllocationSize, decodeInteger);
 }
 
 status_t encodeProtectedContent(uint64_t protectedContent, hidl_vec<uint8_t>* outProtectedContent) {
-    std::vector<uint8_t> tmpOutProtectedContent;
-    status_t err = encodeInteger<uint64_t>(protectedContent, &tmpOutProtectedContent);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutProtectedContent, outProtectedContent);
+    return encode(protectedContent, outProtectedContent, encodeInteger);
 }
 
 status_t decodeProtectedContent(const hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent) {
-    return decodeInteger<uint64_t>(protectedContent, outProtectedContent);
+    return decode(protectedContent, outProtectedContent, decodeInteger);
 }
 
 status_t encodeCompression(const ExtendableType& compression, hidl_vec<uint8_t>* outCompression) {
-    std::vector<uint8_t> tmpOutCompression;
-    status_t err = encodeExtendableType(compression, &tmpOutCompression);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutCompression, outCompression);
+    return encode(compression, outCompression, encodeExtendableType);
 }
 
 status_t decodeCompression(const hidl_vec<uint8_t>& compression, ExtendableType* outCompression) {
-    return decodeExtendableType(compression, outCompression);
+    return decode(compression, outCompression, decodeExtendableType, clearExtendableType);
 }
 
 status_t encodeInterlaced(const ExtendableType& interlaced, hidl_vec<uint8_t>* outInterlaced) {
-    std::vector<uint8_t> tmpOutInterlaced;
-    status_t err = encodeExtendableType(interlaced, &tmpOutInterlaced);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutInterlaced, outInterlaced);
+    return encode(interlaced, outInterlaced, encodeExtendableType);
 }
 
 status_t decodeInterlaced(const hidl_vec<uint8_t>& interlaced, ExtendableType* outInterlaced) {
-    return decodeExtendableType(interlaced, outInterlaced);
+    return decode(interlaced, outInterlaced, decodeExtendableType, clearExtendableType);
 }
 
 status_t encodeChromaSiting(const ExtendableType& chromaSiting, hidl_vec<uint8_t>* outChromaSiting) {
-    std::vector<uint8_t> tmpOutChromaSiting;
-    status_t err = encodeExtendableType(chromaSiting, &tmpOutChromaSiting);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutChromaSiting, outChromaSiting);
+    return encode(chromaSiting, outChromaSiting, encodeExtendableType);
 }
 
 status_t decodeChromaSiting(const hidl_vec<uint8_t>& chromaSiting, ExtendableType* outChromaSiting) {
-    return decodeExtendableType(chromaSiting, 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) {
-    std::vector<uint8_t> tmpOutDataspace;
-    status_t err = encodeInteger<int32_t>(static_cast<int32_t>(dataspace), &tmpOutDataspace);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutDataspace, outDataspace);
+    return encode(static_cast<int32_t>(dataspace), outDataspace, encodeInteger);
 }
 
 status_t decodeDataspace(const hidl_vec<uint8_t>& dataspace, Dataspace* outDataspace) {
-    return decodeInteger<int32_t>(dataspace, reinterpret_cast<int32_t*>(outDataspace));
+    return decode(dataspace, reinterpret_cast<int32_t*>(outDataspace), decodeInteger);
 }
 
 status_t encodeBlendMode(const BlendMode& blendMode, hidl_vec<uint8_t>* outBlendMode) {
-    std::vector<uint8_t> tmpOutBlendMode;
-    status_t err = encodeInteger<int32_t>(static_cast<int32_t>(blendMode), &tmpOutBlendMode);
-    if (err) {
-        return err;
-    }
-    return copyToHidlVec(tmpOutBlendMode, outBlendMode);
+    return encode(static_cast<int32_t>(blendMode), outBlendMode, encodeInteger);
 }
 
 status_t decodeBlendMode(const hidl_vec<uint8_t>& blendMode, BlendMode* outBlendMode) {
-    return decodeInteger<int32_t>(blendMode, reinterpret_cast<int32_t*>(outBlendMode));
+    return decode(blendMode, reinterpret_cast<int32_t*>(outBlendMode), decodeInteger);
 }
 
 } // namespace gralloc4
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index e062345..80588cd 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -33,15 +33,6 @@
 
 namespace gralloc4 {
 
-/**
- * This library is compiled into VNDK-SP and FWK_ONLY copies. When a device is upgraded, the vendor
- * partition may choose to use an older copy of the VNDK-SP.
- *
- * Prepend the version to every encode and decode so the system partition can fallback to an older
- * version if necessary.
- */
-#define GRALLOC4_METADATA_VERSION 1
-
 #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"
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 4f605e0..1ae148c 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -50,6 +50,7 @@
         "ConsumerBase.cpp",
         "CpuConsumer.cpp",
         "DebugEGLImageTracker.cpp",
+        "DisplayEventDispatcher.cpp",
         "DisplayEventReceiver.cpp",
         "GLConsumer.cpp",
         "GuiConfig.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 3c31d74..29ea84e 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -14,8 +14,12 @@
  * limitations under the License.
  */
 
+#undef LOG_TAG
+#define LOG_TAG "BLASTBufferQueue"
+
 #include <gui/BLASTBufferQueue.h>
 #include <gui/BufferItemConsumer.h>
+#include <gui/GLConsumer.h>
 
 #include <chrono>
 
@@ -24,8 +28,14 @@
 namespace android {
 
 BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height)
-      : mSurfaceControl(surface), mWidth(width), mHeight(height) {
+      : mSurfaceControl(surface),
+        mPendingCallbacks(0),
+        mWidth(width),
+        mHeight(height),
+        mNextTransaction(nullptr) {
     BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+    mConsumer->setMaxBufferCount(MAX_BUFFERS);
+    mProducer->setMaxDequeuedBufferCount(MAX_BUFFERS - 1);
     mBufferItemConsumer =
             new BufferItemConsumer(mConsumer, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, 1, true);
     mBufferItemConsumer->setName(String8("BLAST Consumer"));
@@ -34,6 +44,8 @@
     mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
     mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
     mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint());
+
+    mAcquired = false;
 }
 
 void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
@@ -59,20 +71,32 @@
                                            const std::vector<SurfaceControlStats>& stats) {
     std::unique_lock _lock{mMutex};
 
-    if (stats.size() > 0 && mNextCallbackBufferItem.mGraphicBuffer != nullptr) {
+    if (stats.size() > 0 && !mShadowQueue.empty()) {
         mBufferItemConsumer->releaseBuffer(mNextCallbackBufferItem,
                                            stats[0].previousReleaseFence
                                                    ? stats[0].previousReleaseFence
                                                    : Fence::NO_FENCE);
+        mAcquired = false;
         mNextCallbackBufferItem = BufferItem();
         mBufferItemConsumer->setTransformHint(stats[0].transformHint);
     }
-    mDequeueWaitCV.notify_all();
+    mPendingCallbacks--;
+    processNextBufferLocked();
+    mCallbackCV.notify_all();
     decStrong((void*)transactionCallbackThunk);
 }
 
-void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
-    std::unique_lock _lock{mMutex};
+void BLASTBufferQueue::processNextBufferLocked() {
+    if (mShadowQueue.empty()) {
+        return;
+    }
+
+    if (mAcquired) {
+        return;
+    }
+
+    BufferItem item = std::move(mShadowQueue.front());
+    mShadowQueue.pop();
 
     SurfaceComposerClient::Transaction localTransaction;
     bool applyTransaction = true;
@@ -83,11 +107,11 @@
         applyTransaction = false;
     }
 
-    int status = OK;
     mNextCallbackBufferItem = mLastSubmittedBufferItem;
-
     mLastSubmittedBufferItem = BufferItem();
-    status = mBufferItemConsumer->acquireBuffer(&mLastSubmittedBufferItem, -1, false);
+
+    status_t status = mBufferItemConsumer->acquireBuffer(&mLastSubmittedBufferItem, -1, false);
+    mAcquired = true;
     if (status != OK) {
         ALOGE("Failed to acquire?");
     }
@@ -99,7 +123,6 @@
         return;
     }
 
-
     // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
     incStrong((void*)transactionCallbackThunk);
 
@@ -109,20 +132,31 @@
     t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
 
     t->setFrame(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
-    t->setCrop(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
+    t->setCrop(mSurfaceControl, computeCrop(mLastSubmittedBufferItem));
 
     if (applyTransaction) {
-        ALOGE("Apply transaction");
         t->apply();
-
-        if (mNextCallbackBufferItem.mGraphicBuffer != nullptr) {
-            mDequeueWaitCV.wait_for(_lock, 5000ms);
-        }
     }
 }
 
+Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
+    if (item.mScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
+        return GLConsumer::scaleDownCrop(item.mCrop, mWidth, mHeight);
+    }
+    return item.mCrop;
+}
+
+void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
+    std::lock_guard _lock{mMutex};
+
+    // add to shadow queue
+    mShadowQueue.push(item);
+    processNextBufferLocked();
+    mPendingCallbacks++;
+}
+
 void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
-    std::unique_lock _lock{mMutex};
+    std::lock_guard _lock{mMutex};
     mNextTransaction = t;
 }
 
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
new file mode 100644
index 0000000..54f383e
--- /dev/null
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015 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 "DisplayEventDispatcher"
+
+#include <cinttypes>
+#include <cstdint>
+
+#include <gui/DisplayEventDispatcher.h>
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+
+#include <utils/Timers.h>
+
+namespace android {
+
+// Number of events to read at a time from the DisplayEventDispatcher pipe.
+// The value should be large enough that we can quickly drain the pipe
+// using just a few large reads.
+static const size_t EVENT_BUFFER_SIZE = 100;
+
+DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
+                                               ISurfaceComposer::VsyncSource vsyncSource,
+                                               ISurfaceComposer::ConfigChanged configChanged)
+      : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
+    ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
+}
+
+status_t DisplayEventDispatcher::initialize() {
+    status_t result = mReceiver.initCheck();
+    if (result) {
+        ALOGW("Failed to initialize display event receiver, status=%d", result);
+        return result;
+    }
+
+    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()) {
+        mLooper->removeFd(mReceiver.getFd());
+    }
+}
+
+status_t DisplayEventDispatcher::scheduleVsync() {
+    if (!mWaitingForVsync) {
+        ALOGV("dispatcher %p ~ Scheduling vsync.", this);
+
+        // Drain all pending events.
+        nsecs_t vsyncTimestamp;
+        PhysicalDisplayId vsyncDisplayId;
+        uint32_t vsyncCount;
+        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+            ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this,
+                  ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
+        }
+
+        status_t status = mReceiver.requestNextVsync();
+        if (status) {
+            ALOGW("Failed to request next vsync, status=%d", status);
+            return status;
+        }
+
+        mWaitingForVsync = true;
+    }
+    return OK;
+}
+
+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.  "
+              "events=0x%x",
+              events);
+        return 0; // remove the callback
+    }
+
+    if (!(events & Looper::EVENT_INPUT)) {
+        ALOGW("Received spurious callback for unhandled poll event.  "
+              "events=0x%x",
+              events);
+        return 1; // keep the callback
+    }
+
+    // Drain all pending events, keep the last vsync.
+    nsecs_t vsyncTimestamp;
+    PhysicalDisplayId vsyncDisplayId;
+    uint32_t vsyncCount;
+    if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+        ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
+              ", displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
+              this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
+        mWaitingForVsync = false;
+        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
+    }
+
+    return 1; // keep the callback
+}
+
+bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
+                                                  PhysicalDisplayId* outDisplayId,
+                                                  uint32_t* outCount) {
+    bool gotVsync = false;
+    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+    ssize_t n;
+    while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+        ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
+        for (ssize_t i = 0; i < n; i++) {
+            const DisplayEventReceiver::Event& ev = buf[i];
+            switch (ev.header.type) {
+                case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+                    // Later vsync events will just overwrite the info from earlier
+                    // ones. That's fine, we only care about the most recent.
+                    gotVsync = true;
+                    *outTimestamp = ev.header.timestamp;
+                    *outDisplayId = ev.header.displayId;
+                    *outCount = ev.vsync.count;
+                    break;
+                case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+                    dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
+                    break;
+                case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+                    dispatchConfigChanged(ev.header.timestamp, ev.header.displayId,
+                                          ev.config.configId);
+                    break;
+                default:
+                    ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
+                    break;
+            }
+        }
+    }
+    if (n < 0) {
+        ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n));
+    }
+    return gotVsync;
+}
+} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index b9597db..546757b 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -905,6 +905,85 @@
         return reply.readInt32();
     }
 
+    virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                  int32_t defaultModeId, float minRefreshRate,
+                                                  float maxRefreshRate) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("setDesiredDisplayConfigSpecs: failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeStrongBinder(displayToken);
+        if (result != NO_ERROR) {
+            ALOGE("setDesiredDisplayConfigSpecs: failed to write display token: %d", result);
+            return result;
+        }
+        result = data.writeInt32(defaultModeId);
+        if (result != NO_ERROR) {
+            ALOGE("setDesiredDisplayConfigSpecs failed to write defaultModeId: %d", result);
+            return result;
+        }
+        result = data.writeFloat(minRefreshRate);
+        if (result != NO_ERROR) {
+            ALOGE("setDesiredDisplayConfigSpecs failed to write minRefreshRate: %d", result);
+            return result;
+        }
+        result = data.writeFloat(maxRefreshRate);
+        if (result != NO_ERROR) {
+            ALOGE("setDesiredDisplayConfigSpecs failed to write maxRefreshRate: %d", result);
+            return result;
+        }
+
+        result = remote()->transact(BnSurfaceComposer::SET_DESIRED_DISPLAY_CONFIG_SPECS, data,
+                                    &reply);
+        if (result != NO_ERROR) {
+            ALOGE("setDesiredDisplayConfigSpecs failed to transact: %d", result);
+            return result;
+        }
+        return reply.readInt32();
+    }
+
+    virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                  int32_t* outDefaultModeId,
+                                                  float* outMinRefreshRate,
+                                                  float* outMaxRefreshRate) {
+        if (!outDefaultModeId || !outMinRefreshRate || !outMaxRefreshRate) return BAD_VALUE;
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("getDesiredDisplayConfigSpecs failed to writeInterfaceToken: %d", result);
+            return result;
+        }
+        result = data.writeStrongBinder(displayToken);
+        if (result != NO_ERROR) {
+            ALOGE("getDesiredDisplayConfigSpecs failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::GET_DESIRED_DISPLAY_CONFIG_SPECS, data,
+                                    &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getDesiredDisplayConfigSpecs failed to transact: %d", result);
+            return result;
+        }
+        result = reply.readInt32(outDefaultModeId);
+        if (result != NO_ERROR) {
+            ALOGE("getDesiredDisplayConfigSpecs failed to read defaultModeId: %d", result);
+            return result;
+        }
+        result = reply.readFloat(outMinRefreshRate);
+        if (result != NO_ERROR) {
+            ALOGE("getDesiredDisplayConfigSpecs failed to read minRefreshRate: %d", result);
+            return result;
+        }
+        result = reply.readFloat(outMaxRefreshRate);
+        if (result != NO_ERROR) {
+            ALOGE("getDesiredDisplayConfigSpecs failed to read maxRefreshRate: %d", result);
+            return result;
+        }
+        return reply.readInt32();
+    }
+
     virtual status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
                                                  bool* outSupport) const {
         Parcel data, reply;
@@ -1583,6 +1662,72 @@
             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);
+            if (result != NO_ERROR) {
+                ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultModeId: %d", result);
+                return result;
+            }
+            float minRefreshRate;
+            result = data.readFloat(&minRefreshRate);
+            if (result != NO_ERROR) {
+                ALOGE("setDesiredDisplayConfigSpecs: failed to read minRefreshRate: %d", result);
+                return result;
+            }
+            float maxRefreshRate;
+            result = data.readFloat(&maxRefreshRate);
+            if (result != NO_ERROR) {
+                ALOGE("setDesiredDisplayConfigSpecs: failed to read maxRefreshRate: %d", result);
+                return result;
+            }
+            result = setDesiredDisplayConfigSpecs(displayToken, defaultModeId, minRefreshRate,
+                                                  maxRefreshRate);
+            if (result != NO_ERROR) {
+                ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: "
+                      "%d",
+                      result);
+                return result;
+            }
+            reply->writeInt32(result);
+            return result;
+        }
+        case GET_DESIRED_DISPLAY_CONFIG_SPECS: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> displayToken = data.readStrongBinder();
+            int32_t defaultModeId;
+            float minRefreshRate;
+            float maxRefreshRate;
+
+            status_t result = getDesiredDisplayConfigSpecs(displayToken, &defaultModeId,
+                                                           &minRefreshRate, &maxRefreshRate);
+            if (result != NO_ERROR) {
+                ALOGE("getDesiredDisplayConfigSpecs: failed to get getDesiredDisplayConfigSpecs: "
+                      "%d",
+                      result);
+                return result;
+            }
+
+            result = reply->writeInt32(defaultModeId);
+            if (result != NO_ERROR) {
+                ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultModeId: %d", result);
+                return result;
+            }
+            result = reply->writeFloat(minRefreshRate);
+            if (result != NO_ERROR) {
+                ALOGE("getDesiredDisplayConfigSpecs: failed to write minRefreshRate: %d", result);
+                return result;
+            }
+            result = reply->writeFloat(maxRefreshRate);
+            if (result != NO_ERROR) {
+                ALOGE("getDesiredDisplayConfigSpecs: failed to write maxRefreshRate: %d", result);
+                return result;
+            }
+            reply->writeInt32(result);
+            return result;
+        }
         case GET_DISPLAY_BRIGHTNESS_SUPPORT: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> displayToken;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 2ab4d8a..9d7d7d0 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -189,48 +189,49 @@
 }
 
 void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
-    std::lock_guard<std::mutex> lock(mMutex);
+    std::unordered_map<CallbackId, CallbackTranslation> callbacksMap;
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
 
-    /* This listener knows all the sp<IBinder> to sp<SurfaceControl> for all its registered
-     * callbackIds, except for when Transactions are merged together. This probably cannot be
-     * solved before this point because the Transactions could be merged together and applied in a
-     * different process.
-     *
-     * Fortunately, we get all the callbacks for this listener for the same frame together at the
-     * same time. This means if any Transactions were merged together, we will get their callbacks
-     * at the same time. We can combine all the sp<IBinder> to sp<SurfaceControl> maps for all the
-     * callbackIds to generate one super map that contains all the sp<IBinder> to sp<SurfaceControl>
-     * that could possibly exist for the callbacks.
-     */
-    std::unordered_map<sp<IBinder>, sp<SurfaceControl>, SurfaceComposerClient::IBinderHash>
-            surfaceControls;
-    for (const auto& transactionStats : listenerStats.transactionStats) {
-        for (auto callbackId : transactionStats.callbackIds) {
-            auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
-            surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end());
+        /* This listener knows all the sp<IBinder> to sp<SurfaceControl> for all its registered
+         * callbackIds, except for when Transactions are merged together. This probably cannot be
+         * solved before this point because the Transactions could be merged together and applied in
+         * a different process.
+         *
+         * Fortunately, we get all the callbacks for this listener for the same frame together at
+         * the same time. This means if any Transactions were merged together, we will get their
+         * callbacks at the same time. We can combine all the sp<IBinder> to sp<SurfaceControl> maps
+         * for all the callbackIds to generate one super map that contains all the sp<IBinder> to
+         * sp<SurfaceControl> that could possibly exist for the callbacks.
+         */
+        callbacksMap = mCallbacks;
+        for (const auto& transactionStats : listenerStats.transactionStats) {
+            for (auto& callbackId : transactionStats.callbackIds) {
+                mCallbacks.erase(callbackId);
+            }
         }
     }
-
     for (const auto& transactionStats : listenerStats.transactionStats) {
         for (auto callbackId : transactionStats.callbackIds) {
-            auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
+            auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
             if (!callbackFunction) {
                 ALOGE("cannot call null callback function, skipping");
                 continue;
             }
             std::vector<SurfaceControlStats> surfaceControlStats;
             for (const auto& surfaceStats : transactionStats.surfaceStats) {
-                surfaceControlStats.emplace_back(surfaceControls[surfaceStats.surfaceControl],
-                                                 surfaceStats.acquireTime,
-                                                 surfaceStats.previousReleaseFence,
-                                                 surfaceStats.transformHint);
-                surfaceControls[surfaceStats.surfaceControl]->setTransformHint(
-                        surfaceStats.transformHint);
+                surfaceControlStats
+                        .emplace_back(callbacksMap[callbackId]
+                                              .surfaceControls[surfaceStats.surfaceControl],
+                                      surfaceStats.acquireTime, surfaceStats.previousReleaseFence,
+                                      surfaceStats.transformHint);
+                callbacksMap[callbackId]
+                        .surfaceControls[surfaceStats.surfaceControl]
+                        ->setTransformHint(surfaceStats.transformHint);
             }
 
             callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
                              surfaceControlStats);
-            mCallbacks.erase(callbackId);
         }
     }
 }
@@ -1616,6 +1617,26 @@
                                                                            outAllowedConfigs);
 }
 
+status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                             int32_t defaultModeId,
+                                                             float minRefreshRate,
+                                                             float maxRefreshRate) {
+    return ComposerService::getComposerService()->setDesiredDisplayConfigSpecs(displayToken,
+                                                                               defaultModeId,
+                                                                               minRefreshRate,
+                                                                               maxRefreshRate);
+}
+
+status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                             int32_t* outDefaultModeId,
+                                                             float* outMinRefreshRate,
+                                                             float* outMaxRefreshRate) {
+    return ComposerService::getComposerService()->getDesiredDisplayConfigSpecs(displayToken,
+                                                                               outDefaultModeId,
+                                                                               outMinRefreshRate,
+                                                                               outMaxRefreshRate);
+}
+
 status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display,
         Vector<ColorMode>* outColorModes) {
     return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes);
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 6320556..dd0b470 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -27,6 +27,7 @@
 #include <utils/RefBase.h>
 
 #include <system/window.h>
+#include <thread>
 
 namespace android {
 
@@ -50,7 +51,6 @@
     void setNextTransaction(SurfaceComposerClient::Transaction *t);
 
     void update(const sp<SurfaceControl>& surface, int width, int height);
-    
 
     virtual ~BLASTBufferQueue() = default;
 
@@ -61,32 +61,36 @@
     BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs);
     BLASTBufferQueue(const BLASTBufferQueue& rhs);
 
-    sp<SurfaceControl> mSurfaceControl;
-    
-    mutable std::mutex mMutex;
+    void processNextBufferLocked() REQUIRES(mMutex);
+    Rect computeCrop(const BufferItem& item);
 
-    static const int MAX_BUFFERS = 2;
+    sp<SurfaceControl> mSurfaceControl;
+
+    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;
     };
-    
-    int mDequeuedBuffers = 0;
 
-    int mWidth;
-    int mHeight;
+    std::queue<const BufferItem> mShadowQueue GUARDED_BY(mMutex);
+    bool mAcquired GUARDED_BY(mMutex);
 
-    BufferItem mLastSubmittedBufferItem;
-    BufferItem mNextCallbackBufferItem;
-    sp<Fence> mLastFence;
+    int mWidth GUARDED_BY(mMutex);
+    int mHeight GUARDED_BY(mMutex);
 
-    std::condition_variable mDequeueWaitCV;
+    BufferItem mLastSubmittedBufferItem GUARDED_BY(mMutex);
+    BufferItem mNextCallbackBufferItem GUARDED_BY(mMutex);
+    sp<Fence> mLastFence GUARDED_BY(mMutex);
 
     sp<IGraphicBufferConsumer> mConsumer;
     sp<IGraphicBufferProducer> mProducer;
     sp<BufferItemConsumer> mBufferItemConsumer;
 
-    SurfaceComposerClient::Transaction* mNextTransaction = nullptr;
+    SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
 };
 
 } // namespace android
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
new file mode 100644
index 0000000..f0b7ff5
--- /dev/null
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+
+namespace android {
+
+class DisplayEventDispatcher : public LooperCallback {
+public:
+    explicit DisplayEventDispatcher(
+            const sp<Looper>& looper,
+            ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
+            ISurfaceComposer::ConfigChanged configChanged =
+                    ISurfaceComposer::eConfigChangedSuppress);
+
+    status_t initialize();
+    void dispose();
+    status_t scheduleVsync();
+
+protected:
+    virtual ~DisplayEventDispatcher() = default;
+
+private:
+    sp<Looper> mLooper;
+    DisplayEventReceiver mReceiver;
+    bool mWaitingForVsync;
+
+    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;
+
+    virtual int handleEvent(int receiveFd, int events, void* data);
+    bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
+                              uint32_t* outCount);
+};
+} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 514dfe2..345425d 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -401,6 +401,19 @@
     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,
+                                                  float maxRefreshRate) = 0;
+
+    virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                  int32_t* outDefaultModeId,
+                                                  float* outMinRefreshRate,
+                                                  float* outMaxRefreshRate) = 0;
+    /*
      * Gets whether brightness operations are supported on a display.
      *
      * displayToken
@@ -512,6 +525,8 @@
         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,
         SET_DISPLAY_BRIGHTNESS,
         CAPTURE_SCREEN_BY_ID,
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index d218356..2c0b143 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -129,6 +129,22 @@
     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,
+                                                 float maxRefreshRate);
+    // Gets the refresh rate boundaries for display configuration.
+    // For all other parameters, default configuration is used. The index for the default is
+    // corresponting to the configs returned from getDisplayConfigs().
+    // The reason is passed in for telemetry tracking, and it corresponds to the list of all
+    // the policy rules that were used.
+    static status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                 int32_t* outDefaultModeId,
+                                                 float* outMinRefreshRate,
+                                                 float* outMaxRefreshRate);
+
     // Gets the list of supported color modes for the given display
     static status_t getDisplayColorModes(const sp<IBinder>& display,
             Vector<ui::ColorMode>* outColorModes);
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index ff22913..ae6c5cf 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -19,6 +19,8 @@
 #include <gui/BLASTBufferQueue.h>
 
 #include <android/hardware/graphics/common/1.2/types.h>
+#include <gui/BufferQueueCore.h>
+#include <gui/BufferQueueProducer.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/IProducerListener.h>
 #include <gui/SurfaceComposerClient.h>
@@ -65,9 +67,11 @@
         return mBlastBufferQueueAdapter->mSurfaceControl;
     }
 
-    void waitForCallback() {
+    void waitForCallbacks() {
         std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
-        mBlastBufferQueueAdapter->mDequeueWaitCV.wait_for(lock, 1s);
+        while (mBlastBufferQueueAdapter->mPendingCallbacks > 0) {
+            mBlastBufferQueueAdapter->mCallbackCV.wait(lock);
+        }
     }
 
 private:
@@ -116,10 +120,21 @@
                 .apply();
     }
 
-    void fillBuffer(uint32_t* bufData, uint32_t width, uint32_t height, uint32_t stride, uint8_t r,
-                    uint8_t g, uint8_t b) {
-        for (uint32_t row = 0; row < height; row++) {
-            for (uint32_t col = 0; col < width; col++) {
+    void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
+        auto igbProducer = adapter.getIGraphicBufferProducer();
+        ASSERT_NE(nullptr, igbProducer.get());
+        IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        ASSERT_EQ(NO_ERROR,
+                  igbProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
+                                       &qbOutput));
+        ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
+        producer = igbProducer;
+    }
+
+    void fillBuffer(uint32_t* bufData, Rect rect, uint32_t stride, uint8_t r, uint8_t g,
+                    uint8_t b) {
+        for (uint32_t row = rect.top; row < rect.bottom; row++) {
+            for (uint32_t col = rect.left; col < rect.right; col++) {
                 uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
                 *pixel = r;
                 *(pixel + 1) = g;
@@ -129,7 +144,7 @@
         }
     }
 
-    void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b) {
+    void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b, Rect region) {
         const auto width = mScreenCaptureBuf->getWidth();
         const auto height = mScreenCaptureBuf->getHeight();
         const auto stride = mScreenCaptureBuf->getStride();
@@ -141,9 +156,16 @@
         for (uint32_t row = 0; row < height; row++) {
             for (uint32_t col = 0; col < width; col++) {
                 uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
-                EXPECT_EQ(r, *(pixel));
-                EXPECT_EQ(g, *(pixel + 1));
-                EXPECT_EQ(b, *(pixel + 2));
+                if (row >= region.top && row < region.bottom && col >= region.left &&
+                    col < region.right) {
+                    EXPECT_EQ(r, *(pixel));
+                    EXPECT_EQ(g, *(pixel + 1));
+                    EXPECT_EQ(b, *(pixel + 2));
+                } else {
+                    EXPECT_EQ(0, *(pixel));
+                    EXPECT_EQ(0, *(pixel + 1));
+                    EXPECT_EQ(0, *(pixel + 2));
+                }
             }
         }
         mScreenCaptureBuf->unlock();
@@ -195,14 +217,8 @@
     uint8_t b = 0;
 
     BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
-    auto igbProducer = adapter.getIGraphicBufferProducer();
-    ASSERT_NE(nullptr, igbProducer.get());
-    IGraphicBufferProducer::QueueBufferOutput qbOutput;
-    ASSERT_EQ(NO_ERROR,
-              igbProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
-                                   &qbOutput));
-    ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(3));
-    ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
 
     int slot;
     sp<Fence> fence;
@@ -216,9 +232,10 @@
     uint32_t* bufData;
     buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
               reinterpret_cast<void**>(&bufData));
-    fillBuffer(bufData, buf->getWidth(), buf->getHeight(), buf->getStride(), r, g, b);
+    fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), r, g, b);
     buf->unlock();
 
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
     IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
                                                    Rect(mDisplayWidth, mDisplayHeight),
                                                    NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
@@ -226,7 +243,7 @@
     igbProducer->queueBuffer(slot, input, &qbOutput);
     ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
 
-    adapter.waitForCallback();
+    adapter.waitForCallbacks();
 
     // capture screen and verify that it is red
     bool capturedSecureLayers;
@@ -235,6 +252,152 @@
                                        ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
                                        mDisplayWidth, mDisplayHeight,
                                        /*useIdentityTransform*/ false));
-    ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
 }
+
+TEST_F(BLASTBufferQueueTest, TripleBuffering) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+
+    std::vector<std::pair<int, sp<Fence>>> allocated;
+    for (int i = 0; i < 3; i++) {
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+        auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                              PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                              nullptr, nullptr);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+        ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+        allocated.push_back({slot, fence});
+    }
+    for (int i = 0; i < allocated.size(); i++) {
+        igbProducer->cancelBuffer(allocated[i].first, allocated[i].second);
+    }
+
+    for (int i = 0; i < 10; i++) {
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+        auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                              PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                              nullptr, nullptr);
+        ASSERT_EQ(NO_ERROR, ret);
+        IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                       Rect(mDisplayWidth, mDisplayHeight),
+                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                       Fence::NO_FENCE);
+        igbProducer->queueBuffer(slot, input, &qbOutput);
+    }
+    adapter.waitForCallbacks();
+}
+
+TEST_F(BLASTBufferQueueTest, SetCrop_Item) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buf;
+    auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                          PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                          nullptr, nullptr);
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+    ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+    uint32_t* bufData;
+    buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+              reinterpret_cast<void**>(&bufData));
+    fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight() / 2), buf->getStride(), r, g, b);
+    buf->unlock();
+
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                   Rect(mDisplayWidth, mDisplayHeight / 2),
+                                                   NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                   Fence::NO_FENCE);
+    igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
+
+    adapter.waitForCallbacks();
+    // capture screen and verify that it is red
+    bool capturedSecureLayers;
+    ASSERT_EQ(NO_ERROR,
+              mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+                                       ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+                                       mDisplayWidth, mDisplayHeight,
+                                       /*useIdentityTransform*/ false));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    int32_t bufferSideLength =
+            (mDisplayWidth < mDisplayHeight) ? mDisplayWidth / 2 : mDisplayHeight / 2;
+    int32_t finalCropSideLength = bufferSideLength / 2;
+
+    auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                     ISurfaceComposerClient::eFXSurfaceColor);
+    ASSERT_NE(nullptr, bg.get());
+    Transaction t;
+    t.setLayerStack(bg, 0)
+            .setCrop_legacy(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+            .setColor(bg, half3{0, 0, 0})
+            .setLayer(bg, 0)
+            .apply();
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, bufferSideLength, bufferSideLength);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buf;
+    auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSideLength, bufferSideLength,
+                                          PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                          nullptr, nullptr);
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+    ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+    uint32_t* bufData;
+    buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+              reinterpret_cast<void**>(&bufData));
+    fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), 0, 0, 0);
+    fillBuffer(bufData,
+               Rect(finalCropSideLength / 2, 0, buf->getWidth() - finalCropSideLength / 2,
+                    buf->getHeight()),
+               buf->getStride(), r, g, b);
+    buf->unlock();
+
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                   Rect(bufferSideLength, finalCropSideLength),
+                                                   NATIVE_WINDOW_SCALING_MODE_SCALE_CROP, 0,
+                                                   Fence::NO_FENCE);
+    igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
+
+    adapter.waitForCallbacks();
+    // capture screen and verify that it is red
+    bool capturedSecureLayers;
+    ASSERT_EQ(NO_ERROR,
+              mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+                                       ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+                                       mDisplayWidth, mDisplayHeight,
+                                       /*useIdentityTransform*/ false));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b,
+                               {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength}));
+}
+
 } // namespace android
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index aef7aed..103f775 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -543,7 +543,6 @@
     // Should now be able to dequeue up to minBuffers times
     DequeueBufferResult result;
     for (int i = 0; i < minBuffers; ++i) {
-
         EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
                 (dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
                               TEST_PRODUCER_USAGE_BITS, &result)))
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index c4f35ae..3d90369 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -831,6 +831,17 @@
                                       std::vector<int32_t>* /*outAllowedConfigs*/) override {
         return NO_ERROR;
     }
+    status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
+                                          int32_t /*defaultModeId*/, float /*minRefreshRate*/,
+                                          float /*maxRefreshRate*/) override {
+        return NO_ERROR;
+    }
+    status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
+                                          int32_t* /*outDefaultModeId*/,
+                                          float* /*outMinRefreshRate*/,
+                                          float* /*outMaxRefreshRate*/) override {
+        return NO_ERROR;
+    };
     status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; }
 
     status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/,
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 34b305e..8ccbc7f 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -20,6 +20,7 @@
 #include <limits.h>
 
 #include <input/Input.h>
+#include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
 
 #ifdef __ANDROID__
@@ -41,6 +42,21 @@
 
 // --- InputEvent ---
 
+const char* inputEventTypeToString(int32_t type) {
+    switch (type) {
+        case AINPUT_EVENT_TYPE_KEY: {
+            return "KEY";
+        }
+        case AINPUT_EVENT_TYPE_MOTION: {
+            return "MOTION";
+        }
+        case AINPUT_EVENT_TYPE_FOCUS: {
+            return "FOCUS";
+        }
+    }
+    return "UNKNOWN";
+}
+
 void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) {
     mDeviceId = deviceId;
     mSource = source;
@@ -587,6 +603,20 @@
     return getAxisByLabel(label);
 }
 
+// --- FocusEvent ---
+
+void FocusEvent::initialize(bool hasFocus, bool inTouchMode) {
+    InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+                           ADISPLAY_ID_NONE);
+    mHasFocus = hasFocus;
+    mInTouchMode = inTouchMode;
+}
+
+void FocusEvent::initialize(const FocusEvent& from) {
+    InputEvent::initialize(from);
+    mHasFocus = from.mHasFocus;
+    mInTouchMode = from.mInTouchMode;
+}
 
 // --- PooledInputEventFactory ---
 
@@ -595,43 +625,52 @@
 }
 
 PooledInputEventFactory::~PooledInputEventFactory() {
-    for (size_t i = 0; i < mKeyEventPool.size(); i++) {
-        delete mKeyEventPool.itemAt(i);
-    }
-    for (size_t i = 0; i < mMotionEventPool.size(); i++) {
-        delete mMotionEventPool.itemAt(i);
-    }
 }
 
 KeyEvent* PooledInputEventFactory::createKeyEvent() {
-    if (!mKeyEventPool.isEmpty()) {
-        KeyEvent* event = mKeyEventPool.top();
-        mKeyEventPool.pop();
-        return event;
+    if (mKeyEventPool.empty()) {
+        return new KeyEvent();
     }
-    return new KeyEvent();
+    KeyEvent* event = mKeyEventPool.front().release();
+    mKeyEventPool.pop();
+    return event;
 }
 
 MotionEvent* PooledInputEventFactory::createMotionEvent() {
-    if (!mMotionEventPool.isEmpty()) {
-        MotionEvent* event = mMotionEventPool.top();
-        mMotionEventPool.pop();
-        return event;
+    if (mMotionEventPool.empty()) {
+        return new MotionEvent();
     }
-    return new MotionEvent();
+    MotionEvent* event = mMotionEventPool.front().release();
+    mMotionEventPool.pop();
+    return event;
+}
+
+FocusEvent* PooledInputEventFactory::createFocusEvent() {
+    if (mFocusEventPool.empty()) {
+        return new FocusEvent();
+    }
+    FocusEvent* event = mFocusEventPool.front().release();
+    mFocusEventPool.pop();
+    return event;
 }
 
 void PooledInputEventFactory::recycle(InputEvent* event) {
     switch (event->getType()) {
     case AINPUT_EVENT_TYPE_KEY:
         if (mKeyEventPool.size() < mMaxPoolSize) {
-            mKeyEventPool.push(static_cast<KeyEvent*>(event));
+            mKeyEventPool.push(std::unique_ptr<KeyEvent>(static_cast<KeyEvent*>(event)));
             return;
         }
         break;
     case AINPUT_EVENT_TYPE_MOTION:
         if (mMotionEventPool.size() < mMaxPoolSize) {
-            mMotionEventPool.push(static_cast<MotionEvent*>(event));
+            mMotionEventPool.push(std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event)));
+            return;
+        }
+        break;
+    case AINPUT_EVENT_TYPE_FOCUS:
+        if (mFocusEventPool.size() < mMaxPoolSize) {
+            mFocusEventPool.push(std::unique_ptr<FocusEvent>(static_cast<FocusEvent*>(event)));
             return;
         }
         break;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index a5dd3c0..200e1f3 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -14,7 +14,7 @@
 static constexpr bool DEBUG_CHANNEL_LIFECYCLE = false;
 
 // Log debug messages about transport actions
-#define DEBUG_TRANSPORT_ACTIONS 0
+static constexpr bool DEBUG_TRANSPORT_ACTIONS = false;
 
 // Log debug messages about touch event resampling
 #define DEBUG_RESAMPLING 0
@@ -88,6 +88,10 @@
     return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
 }
 
+inline static const char* toString(bool value) {
+    return value ? "true" : "false";
+}
+
 // --- InputMessage ---
 
 bool InputMessage::isValid(size_t actualSize) const {
@@ -99,6 +103,8 @@
                 return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS;
             case Type::FINISHED:
                 return true;
+            case Type::FOCUS:
+                return true;
         }
     }
     return false;
@@ -112,6 +118,8 @@
             return sizeof(Header) + body.motion.size();
         case Type::FINISHED:
             return sizeof(Header) + body.finished.size();
+        case Type::FOCUS:
+            return sizeof(Header) + body.focus.size();
     }
     return sizeof(Header);
 }
@@ -216,8 +224,10 @@
             msg->body.finished.handled = body.finished.handled;
             break;
         }
-        default: {
-            LOG_FATAL("Unexpected message type %i", header.type);
+        case InputMessage::Type::FOCUS: {
+            msg->body.focus.seq = body.focus.seq;
+            msg->body.focus.hasFocus = body.focus.hasFocus;
+            msg->body.focus.inTouchMode = body.focus.inTouchMode;
             break;
         }
     }
@@ -432,14 +442,13 @@
                 mChannel->getName().c_str(), keyCode);
         ATRACE_NAME(message.c_str());
     }
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
-            "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
-            "downTime=%" PRId64 ", eventTime=%" PRId64,
-            mChannel->getName().c_str(), seq,
-            deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
-            downTime, eventTime);
-#endif
+    if (DEBUG_TRANSPORT_ACTIONS) {
+        ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
+              "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
+              "downTime=%" PRId64 ", eventTime=%" PRId64,
+              mChannel->getName().c_str(), seq, deviceId, source, action, flags, keyCode, scanCode,
+              metaState, repeatCount, downTime, eventTime);
+    }
 
     if (!seq) {
         ALOGE("Attempted to publish a key event with sequence number 0.");
@@ -476,18 +485,18 @@
                 mChannel->getName().c_str(), action);
         ATRACE_NAME(message.c_str());
     }
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
-            "displayId=%" PRId32 ", "
-            "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
-            "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, "
-            "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
-            "pointerCount=%" PRIu32,
-            mChannel->getName().c_str(), seq,
-            deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState,
-            buttonState, motionClassificationToString(classification),
-            xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
-#endif
+    if (DEBUG_TRANSPORT_ACTIONS) {
+        ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
+              "displayId=%" PRId32 ", "
+              "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
+              "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, "
+              "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
+              "pointerCount=%" PRIu32,
+              mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
+              flags, edgeFlags, metaState, buttonState,
+              motionClassificationToString(classification), xOffset, yOffset, xPrecision,
+              yPrecision, downTime, eventTime, pointerCount);
+    }
 
     if (!seq) {
         ALOGE("Attempted to publish a motion event with sequence number 0.");
@@ -530,11 +539,27 @@
     return mChannel->sendMessage(&msg);
 }
 
+status_t InputPublisher::publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode) {
+    if (ATRACE_ENABLED()) {
+        std::string message =
+                StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)",
+                             mChannel->getName().c_str(), toString(hasFocus),
+                             toString(inTouchMode));
+        ATRACE_NAME(message.c_str());
+    }
+
+    InputMessage msg;
+    msg.header.type = InputMessage::Type::FOCUS;
+    msg.body.focus.seq = seq;
+    msg.body.focus.hasFocus = hasFocus ? 1 : 0;
+    msg.body.focus.inTouchMode = inTouchMode ? 1 : 0;
+    return mChannel->sendMessage(&msg);
+}
+
 status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ receiveFinishedSignal",
-            mChannel->getName().c_str());
-#endif
+    if (DEBUG_TRANSPORT_ACTIONS) {
+        ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
+    }
 
     InputMessage msg;
     status_t result = mChannel->receiveMessage(&msg);
@@ -549,7 +574,7 @@
         return UNKNOWN_ERROR;
     }
     *outSeq = msg.body.finished.seq;
-    *outHandled = msg.body.finished.handled;
+    *outHandled = msg.body.finished.handled == 1;
     return OK;
 }
 
@@ -567,12 +592,12 @@
     return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true);
 }
 
-status_t InputConsumer::consume(InputEventFactoryInterface* factory,
-        bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
-            mChannel->getName().c_str(), consumeBatches ? "true" : "false", frameTime);
-#endif
+status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
+                                nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
+    if (DEBUG_TRANSPORT_ACTIONS) {
+        ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
+              mChannel->getName().c_str(), toString(consumeBatches), frameTime);
+    }
 
     *outSeq = 0;
     *outEvent = nullptr;
@@ -592,10 +617,10 @@
                 if (consumeBatches || result != WOULD_BLOCK) {
                     result = consumeBatch(factory, frameTime, outSeq, outEvent);
                     if (*outEvent) {
-#if DEBUG_TRANSPORT_ACTIONS
-                        ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
-                                mChannel->getName().c_str(), *outSeq);
-#endif
+                        if (DEBUG_TRANSPORT_ACTIONS) {
+                            ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
+                                  mChannel->getName().c_str(), *outSeq);
+                        }
                         break;
                     }
                 }
@@ -611,10 +636,10 @@
                 initializeKeyEvent(keyEvent, &mMsg);
                 *outSeq = mMsg.body.key.seq;
                 *outEvent = keyEvent;
-#if DEBUG_TRANSPORT_ACTIONS
-            ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
-                    mChannel->getName().c_str(), *outSeq);
-#endif
+                if (DEBUG_TRANSPORT_ACTIONS) {
+                    ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
+                          mChannel->getName().c_str(), *outSeq);
+                }
             break;
             }
 
@@ -624,10 +649,10 @@
                     Batch& batch = mBatches.editItemAt(batchIndex);
                     if (canAddSample(batch, &mMsg)) {
                         batch.samples.push(mMsg);
-#if DEBUG_TRANSPORT_ACTIONS
-                    ALOGD("channel '%s' consumer ~ appended to batch event",
-                            mChannel->getName().c_str());
-#endif
+                        if (DEBUG_TRANSPORT_ACTIONS) {
+                            ALOGD("channel '%s' consumer ~ appended to batch event",
+                                  mChannel->getName().c_str());
+                        }
                     break;
                     } else if (isPointerEvent(mMsg.body.motion.source) &&
                                mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) {
@@ -649,47 +674,58 @@
                         if (result) {
                             return result;
                         }
-#if DEBUG_TRANSPORT_ACTIONS
-                    ALOGD("channel '%s' consumer ~ consumed batch event and "
-                            "deferred current event, seq=%u",
-                            mChannel->getName().c_str(), *outSeq);
-#endif
+                        if (DEBUG_TRANSPORT_ACTIONS) {
+                            ALOGD("channel '%s' consumer ~ consumed batch event and "
+                                  "deferred current event, seq=%u",
+                                  mChannel->getName().c_str(), *outSeq);
+                        }
                     break;
                     }
                 }
 
-            // Start a new batch if needed.
-            if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE
-                    || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-                mBatches.push();
-                Batch& batch = mBatches.editTop();
-                batch.samples.push(mMsg);
-#if DEBUG_TRANSPORT_ACTIONS
-                ALOGD("channel '%s' consumer ~ started batch event",
-                        mChannel->getName().c_str());
-#endif
+                // Start a new batch if needed.
+                if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE ||
+                    mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+                    mBatches.push();
+                    Batch& batch = mBatches.editTop();
+                    batch.samples.push(mMsg);
+                    if (DEBUG_TRANSPORT_ACTIONS) {
+                        ALOGD("channel '%s' consumer ~ started batch event",
+                              mChannel->getName().c_str());
+                    }
+                    break;
+                }
+
+                MotionEvent* motionEvent = factory->createMotionEvent();
+                if (!motionEvent) return NO_MEMORY;
+
+                updateTouchState(mMsg);
+                initializeMotionEvent(motionEvent, &mMsg);
+                *outSeq = mMsg.body.motion.seq;
+                *outEvent = motionEvent;
+
+                if (DEBUG_TRANSPORT_ACTIONS) {
+                    ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
+                          mChannel->getName().c_str(), *outSeq);
+                }
                 break;
             }
 
-            MotionEvent* motionEvent = factory->createMotionEvent();
-            if (! motionEvent) return NO_MEMORY;
-
-            updateTouchState(mMsg);
-            initializeMotionEvent(motionEvent, &mMsg);
-            *outSeq = mMsg.body.motion.seq;
-            *outEvent = motionEvent;
-
-#if DEBUG_TRANSPORT_ACTIONS
-            ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
-                    mChannel->getName().c_str(), *outSeq);
-#endif
-            break;
+            case InputMessage::Type::FINISHED: {
+                LOG_ALWAYS_FATAL("Consumed a FINISHED message, which should never be seen by "
+                                 "InputConsumer!");
+                break;
             }
 
-        default:
-            ALOGE("channel '%s' consumer ~ Received unexpected message of type %d",
-                    mChannel->getName().c_str(), mMsg.header.type);
-            return UNKNOWN_ERROR;
+            case InputMessage::Type::FOCUS: {
+                FocusEvent* focusEvent = factory->createFocusEvent();
+                if (!focusEvent) return NO_MEMORY;
+
+                initializeFocusEvent(focusEvent, &mMsg);
+                *outSeq = mMsg.body.focus.seq;
+                *outEvent = focusEvent;
+                break;
+            }
         }
     }
     return OK;
@@ -1014,10 +1050,10 @@
 }
 
 status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
-            mChannel->getName().c_str(), seq, handled ? "true" : "false");
-#endif
+    if (DEBUG_TRANSPORT_ACTIONS) {
+        ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
+              mChannel->getName().c_str(), seq, toString(handled));
+    }
 
     if (!seq) {
         ALOGE("Attempted to send a finished signal with sequence number 0.");
@@ -1066,7 +1102,7 @@
     InputMessage msg;
     msg.header.type = InputMessage::Type::FINISHED;
     msg.body.finished.seq = seq;
-    msg.body.finished.handled = handled;
+    msg.body.finished.handled = handled ? 1 : 0;
     return mChannel->sendMessage(&msg);
 }
 
@@ -1114,6 +1150,10 @@
             msg->body.key.eventTime);
 }
 
+void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
+    event->initialize(msg->body.focus.hasFocus == 1, msg->body.focus.inTouchMode == 1);
+}
+
 void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
     uint32_t pointerCount = msg->body.motion.pointerCount;
     PointerProperties pointerProperties[pointerCount];
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index a362f32..2fc77e9 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -60,6 +60,7 @@
 
     void PublishAndConsumeKeyEvent();
     void PublishAndConsumeMotionEvent();
+    void PublishAndConsumeFocusEvent();
 };
 
 TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -256,6 +257,43 @@
             << "publisher receiveFinishedSignal should have set handled to consumer's reply";
 }
 
+void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() {
+    status_t status;
+
+    constexpr uint32_t seq = 15;
+    constexpr bool hasFocus = true;
+    constexpr bool inTouchMode = true;
+
+    status = mPublisher->publishFocusEvent(seq, hasFocus, inTouchMode);
+    ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
+
+    uint32_t consumeSeq;
+    InputEvent* event;
+    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+    ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+    ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+    ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType())
+            << "consumer should have returned a focus event";
+
+    FocusEvent* focusEvent = static_cast<FocusEvent*>(event);
+    EXPECT_EQ(seq, consumeSeq);
+    EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+    EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
+
+    status = mConsumer->sendFinishedSignal(seq, true);
+    ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+    uint32_t finishedSeq = 0;
+    bool handled = false;
+    status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+    ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, finishedSeq)
+            << "publisher receiveFinishedSignal should have returned the original sequence number";
+    ASSERT_TRUE(handled)
+            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+}
+
 TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
 }
@@ -264,6 +302,10 @@
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
 }
 
+TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) {
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
+}
+
 TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
     status_t status;
     const size_t pointerCount = 1;
@@ -322,6 +364,7 @@
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
 }
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 8d8cf06..9ab0dba 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -69,8 +69,29 @@
   CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88);
   CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96);
 
+  CHECK_OFFSET(InputMessage::Body::Focus, seq, 0);
+  CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
+  CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6);
+
   CHECK_OFFSET(InputMessage::Body::Finished, seq, 0);
   CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
 }
 
+void TestHeaderSize() {
+    static_assert(sizeof(InputMessage::Header) == 8);
+}
+
+/**
+ * We cannot use the Body::size() method here because it is not static for
+ * the Motion type, where "pointerCount" variable affects the size and can change at runtime.
+ */
+void TestBodySize() {
+    static_assert(sizeof(InputMessage::Body::Key) == 64);
+    static_assert(sizeof(InputMessage::Body::Motion) ==
+                  offsetof(InputMessage::Body::Motion, pointers) +
+                          sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
+    static_assert(sizeof(InputMessage::Body::Finished) == 8);
+    static_assert(sizeof(InputMessage::Body::Focus) == 8);
+}
+
 } // namespace android
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 63e0734..05ff93e 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -22,7 +22,7 @@
 #include <thread>
 
 #include <android/choreographer.h>
-#include <androidfw/DisplayEventDispatcher.h>
+#include <gui/DisplayEventDispatcher.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 #include <utils/Looper.h>
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index a9b8d66..7a497ea 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -46,7 +46,6 @@
     ],
 
     shared_libs: [
-        "libandroidfw",
         "libgui",
         "liblog",
         "libnativewindow",
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 15d025b..394d05a 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -983,6 +983,8 @@
 
     Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
     for (auto layer : layers) {
+        mState.maxMasteringLuminance = layer.source.buffer.maxMasteringLuminance;
+        mState.maxContentLuminance = layer.source.buffer.maxContentLuminance;
         mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
 
         const FloatRect bounds = layer.geometry.boundaries;
@@ -998,7 +1000,6 @@
         bool usePremultipliedAlpha = true;
         bool disableTexture = true;
         bool isOpaque = false;
-
         if (layer.source.buffer.buffer != nullptr) {
             disableTexture = false;
             isOpaque = layer.source.buffer.isOpaque;
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index fe9d909..4eb5eb6 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -65,6 +65,8 @@
         mSamplerLoc = glGetUniformLocation(programId, "sampler");
         mColorLoc = glGetUniformLocation(programId, "color");
         mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
+        mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance");
+        mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance");
         mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
         mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
         mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius");
@@ -138,6 +140,12 @@
     if (mDisplayMaxLuminanceLoc >= 0) {
         glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance);
     }
+    if (mMaxMasteringLuminanceLoc >= 0) {
+        glUniform1f(mMaxMasteringLuminanceLoc, desc.maxMasteringLuminance);
+    }
+    if (mMaxContentLuminanceLoc >= 0) {
+        glUniform1f(mMaxContentLuminanceLoc, desc.maxContentLuminance);
+    }
     if (mCornerRadiusLoc >= 0) {
         glUniform1f(mCornerRadiusLoc, desc.cornerRadius);
     }
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index bc9cf08..c9beb68 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -90,6 +90,10 @@
 
     /* location of display luminance uniform */
     GLint mDisplayMaxLuminanceLoc;
+    /* location of max mastering luminance uniform */
+    GLint mMaxMasteringLuminanceLoc;
+    /* location of max content luminance uniform */
+    GLint mMaxContentLuminanceLoc;
 
     /* location of transform matrix */
     GLint mInputTransformMatrixLoc;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 494623e..e2757e1 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -339,9 +339,9 @@
                 default:
                     fs << R"__SHADER__(
                         highp vec3 ToneMap(highp vec3 color) {
-                            const float maxMasteringLumi = 1000.0;
-                            const float maxContentLumi = 1000.0;
-                            const float maxInLumi = min(maxMasteringLumi, maxContentLumi);
+                            float maxMasteringLumi = maxMasteringLuminance;
+                            float maxContentLumi = maxContentLuminance;
+                            float maxInLumi = min(maxMasteringLumi, maxContentLumi);
                             float maxOutLumi = displayMaxLuminance;
 
                             float nits = color.y;
@@ -633,9 +633,10 @@
     }
 
     if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
-        // Currently, display maximum luminance is needed when doing tone mapping.
         if (needs.needsToneMapping()) {
             fs << "uniform float displayMaxLuminance;";
+            fs << "uniform float maxMasteringLuminance;";
+            fs << "uniform float maxContentLuminance;";
         }
 
         if (needs.hasInputTransformMatrix()) {
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index b8bf801..d890ccd 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -60,6 +60,8 @@
 
     // HDR color-space setting for Y410.
     bool isY410BT2020 = false;
+    float maxMasteringLuminance = 0.0;
+    float maxContentLuminance = 0.0;
 };
 
 // Metadata describing the layer geometry.
@@ -96,6 +98,33 @@
     half3 solidColor = half3(0.0f, 0.0f, 0.0f);
 };
 
+/*
+ * Contains the configuration for the shadows drawn by single layer. Shadow follows
+ * material design guidelines.
+ */
+struct ShadowSettings {
+    // Color to the ambient shadow. The alpha is premultiplied.
+    vec4 ambientColor = vec4();
+
+    // Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+    // depends on the light position.
+    vec4 spotColor = vec4();
+
+    // Position of the light source used to cast the spot shadow.
+    vec3 lightPos = vec3();
+
+    // Radius of the spot light source. Smaller radius will have sharper edges,
+    // larger radius will have softer shadows
+    float lightRadius = 0.f;
+
+    // Length of the cast shadow. If length is <= 0.f no shadows will be drawn.
+    float length = 0.f;
+
+    // If true fill in the casting layer is translucent and the shadow needs to fill the bounds.
+    // Otherwise the shadow will only be drawn around the edges of the casting layer.
+    bool casterIsTranslucent = false;
+};
+
 // The settings that RenderEngine requires for correctly rendering a Layer.
 struct LayerSettings {
     // Geometry information
@@ -116,6 +145,8 @@
 
     // True if blending will be forced to be disabled.
     bool disableBlending = false;
+
+    ShadowSettings shadow;
 };
 
 } // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index bd2055f..bad64c2 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -71,6 +71,8 @@
     TransferFunction outputTransferFunction = TransferFunction::LINEAR;
 
     float displayMaxLuminance;
+    float maxMasteringLuminance;
+    float maxContentLuminance;
 
     // projection matrix
     mat4 projectionMatrix;
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index afa6a2b..3775e2b 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -69,6 +69,10 @@
     include_dirs: [
         "frameworks/native/include",
     ],
+    export_include_dirs: [
+        "include",
+        "include_private",
+    ],
 
     // Uncomment the following line to enable VALIDATE_REGIONS traces
     //defaults: ["libui-validate-regions-defaults"],
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 5dc4530..0e23ddf 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -381,7 +381,8 @@
 
 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) const {
+                                     uint32_t* outStride, buffer_handle_t* outBufferHandles,
+                                     bool importBuffers) const {
     IMapper::BufferDescriptorInfo descriptorInfo = {};
     descriptorInfo.width = width;
     descriptorInfo.height = height;
@@ -404,19 +405,33 @@
                                             return;
                                         }
 
-                                        // import buffers
-                                        for (uint32_t i = 0; i < bufferCount; i++) {
-                                            error = mMapper.importBuffer(tmpBuffers[i],
-                                                                         &outBufferHandles[i]);
-                                            if (error != NO_ERROR) {
-                                                for (uint32_t j = 0; j < i; j++) {
-                                                    mMapper.freeBuffer(outBufferHandles[j]);
-                                                    outBufferHandles[j] = nullptr;
+                                        if (importBuffers) {
+                                            for (uint32_t i = 0; i < bufferCount; i++) {
+                                                error = mMapper.importBuffer(tmpBuffers[i],
+                                                                             &outBufferHandles[i]);
+                                                if (error != NO_ERROR) {
+                                                    for (uint32_t j = 0; j < i; j++) {
+                                                        mMapper.freeBuffer(outBufferHandles[j]);
+                                                        outBufferHandles[j] = nullptr;
+                                                    }
+                                                    return;
                                                 }
-                                                return;
+                                            }
+                                        } else {
+                                            for (uint32_t i = 0; i < bufferCount; i++) {
+                                                outBufferHandles[i] = native_handle_clone(
+                                                        tmpBuffers[i].getNativeHandle());
+                                                if (!outBufferHandles[i]) {
+                                                    for (uint32_t j = 0; j < i; j++) {
+                                                        auto buffer = const_cast<native_handle_t*>(
+                                                                outBufferHandles[j]);
+                                                        native_handle_close(buffer);
+                                                        native_handle_delete(buffer);
+                                                        outBufferHandles[j] = nullptr;
+                                                    }
+                                                }
                                             }
                                         }
-
                                         *outStride = tmpStride;
                                     });
 
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index eb43765..e189281 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -362,7 +362,8 @@
 
 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) const {
+                                     uint32_t* outStride, buffer_handle_t* outBufferHandles,
+                                     bool importBuffers) const {
     IMapper::BufferDescriptorInfo descriptorInfo;
     sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
 
@@ -381,16 +382,31 @@
                                             return;
                                         }
 
-                                        // import buffers
-                                        for (uint32_t i = 0; i < bufferCount; i++) {
-                                            error = mMapper.importBuffer(tmpBuffers[i],
-                                                                         &outBufferHandles[i]);
-                                            if (error != NO_ERROR) {
-                                                for (uint32_t j = 0; j < i; j++) {
-                                                    mMapper.freeBuffer(outBufferHandles[j]);
-                                                    outBufferHandles[j] = nullptr;
+                                        if (importBuffers) {
+                                            for (uint32_t i = 0; i < bufferCount; i++) {
+                                                error = mMapper.importBuffer(tmpBuffers[i],
+                                                                             &outBufferHandles[i]);
+                                                if (error != NO_ERROR) {
+                                                    for (uint32_t j = 0; j < i; j++) {
+                                                        mMapper.freeBuffer(outBufferHandles[j]);
+                                                        outBufferHandles[j] = nullptr;
+                                                    }
+                                                    return;
                                                 }
-                                                return;
+                                            }
+                                        } else {
+                                            for (uint32_t i = 0; i < bufferCount; i++) {
+                                                outBufferHandles[i] = native_handle_clone(
+                                                        tmpBuffers[i].getNativeHandle());
+                                                if (!outBufferHandles[i]) {
+                                                    for (uint32_t j = 0; j < i; j++) {
+                                                        auto buffer = const_cast<native_handle_t*>(
+                                                                outBufferHandles[j]);
+                                                        native_handle_close(buffer);
+                                                        native_handle_delete(buffer);
+                                                        outBufferHandles[j] = nullptr;
+                                                    }
+                                                }
                                             }
                                         }
                                         *outStride = tmpStride;
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 73945cf..afe26b7 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -327,7 +327,8 @@
 
 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) const {
+                                     uint32_t* outStride, buffer_handle_t* outBufferHandles,
+                                     bool importBuffers) const {
     IMapper::BufferDescriptorInfo descriptorInfo;
     sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
 
@@ -346,16 +347,31 @@
                                             return;
                                         }
 
-                                        // import buffers
-                                        for (uint32_t i = 0; i < bufferCount; i++) {
-                                            error = mMapper.importBuffer(tmpBuffers[i],
-                                                                         &outBufferHandles[i]);
-                                            if (error != NO_ERROR) {
-                                                for (uint32_t j = 0; j < i; j++) {
-                                                    mMapper.freeBuffer(outBufferHandles[j]);
-                                                    outBufferHandles[j] = nullptr;
+                                        if (importBuffers) {
+                                            for (uint32_t i = 0; i < bufferCount; i++) {
+                                                error = mMapper.importBuffer(tmpBuffers[i],
+                                                                             &outBufferHandles[i]);
+                                                if (error != NO_ERROR) {
+                                                    for (uint32_t j = 0; j < i; j++) {
+                                                        mMapper.freeBuffer(outBufferHandles[j]);
+                                                        outBufferHandles[j] = nullptr;
+                                                    }
+                                                    return;
                                                 }
-                                                return;
+                                            }
+                                        } else {
+                                            for (uint32_t i = 0; i < bufferCount; i++) {
+                                                outBufferHandles[i] = native_handle_clone(
+                                                        tmpBuffers[i].getNativeHandle());
+                                                if (!outBufferHandles[i]) {
+                                                    for (uint32_t j = 0; j < i; j++) {
+                                                        auto buffer = const_cast<native_handle_t*>(
+                                                                outBufferHandles[j]);
+                                                        native_handle_close(buffer);
+                                                        native_handle_delete(buffer);
+                                                        outBufferHandles[j] = nullptr;
+                                                    }
+                                                }
                                             }
                                         }
                                         *outStride = tmpStride;
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 579e68e..05fc590 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -27,7 +27,6 @@
 #include <ui/BufferHubBuffer.h>
 #endif // LIBUI_IN_VNDK
 
-#include <ui/Gralloc2.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
 #include <utils/Trace.h>
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index fcc2547..b2b9680 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -111,11 +111,10 @@
     ALOGD("%s", s.c_str());
 }
 
-status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
-        PixelFormat format, uint32_t layerCount, uint64_t usage,
-        buffer_handle_t* handle, uint32_t* stride,
-        uint64_t /*graphicBufferId*/, std::string requestorName)
-{
+status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, PixelFormat format,
+                                                uint32_t layerCount, uint64_t usage,
+                                                buffer_handle_t* handle, uint32_t* stride,
+                                                std::string requestorName, bool importBuffer) {
     ATRACE_CALL();
 
     // make sure to not allocate a N x 0 or 0 x N buffer, since this is
@@ -138,8 +137,18 @@
     // 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);
+    status_t error = mAllocator->allocate(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",
+              width, height, layerCount, format, usage, error);
+        return NO_MEMORY;
+    }
+
+    if (!importBuffer) {
+        return NO_ERROR;
+    }
     size_t bufSize;
 
     // if stride has no meaning or is too large,
@@ -151,28 +160,44 @@
         bufSize = static_cast<size_t>((*stride)) * height * bpp;
     }
 
-    if (error == NO_ERROR) {
-        Mutex::Autolock _l(sLock);
-        KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
-        alloc_rec_t rec;
-        rec.width = width;
-        rec.height = height;
-        rec.stride = *stride;
-        rec.format = format;
-        rec.layerCount = layerCount;
-        rec.usage = usage;
-        rec.size = bufSize;
-        rec.requestorName = std::move(requestorName);
-        list.add(*handle, rec);
+    Mutex::Autolock _l(sLock);
+    KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+    alloc_rec_t rec;
+    rec.width = width;
+    rec.height = height;
+    rec.stride = *stride;
+    rec.format = format;
+    rec.layerCount = layerCount;
+    rec.usage = usage;
+    rec.size = bufSize;
+    rec.requestorName = std::move(requestorName);
+    list.add(*handle, rec);
 
-        return NO_ERROR;
-    } else {
-        ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
-                "usage %" PRIx64 ": %d",
-                width, height, layerCount, format, usage,
-                error);
-        return NO_MEMORY;
-    }
+    return NO_ERROR;
+}
+status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+                                          uint32_t layerCount, uint64_t usage,
+                                          buffer_handle_t* handle, uint32_t* stride,
+                                          std::string requestorName) {
+    return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+                          true);
+}
+
+status_t GraphicBufferAllocator::allocateRawHandle(uint32_t width, uint32_t height,
+                                                   PixelFormat format, uint32_t layerCount,
+                                                   uint64_t usage, buffer_handle_t* handle,
+                                                   uint32_t* stride, std::string requestorName) {
+    return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+                          false);
+}
+
+// DEPRECATED
+status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+                                          uint32_t layerCount, uint64_t usage,
+                                          buffer_handle_t* handle, uint32_t* stride,
+                                          uint64_t /*graphicBufferId*/, std::string requestorName) {
+    return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+                          true);
 }
 
 status_t GraphicBufferAllocator::free(buffer_handle_t handle)
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 1222cd6..83ebeca 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -23,11 +23,10 @@
 
 #include <utils/Log.h>
 
+#include <ui/Point.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
-#include <ui/Point.h>
-
-#include <private/ui/RegionHelper.h>
+#include <ui/RegionHelper.h>
 
 // ----------------------------------------------------------------------------
 
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 28c3f7b..85abb38 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -33,8 +33,8 @@
     : mMatrix(other.mMatrix), mType(other.mType) {
 }
 
-Transform::Transform(uint32_t orientation) {
-    set(orientation, 0, 0);
+Transform::Transform(uint32_t orientation, int w, int h) {
+    set(orientation, w, h);
 }
 
 Transform::~Transform() = default;
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index 6cc23f0..c28f7a5 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -94,7 +94,8 @@
      */
     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) const = 0;
+                              uint32_t* outStride, buffer_handle_t* outBufferHandles,
+                              bool importBuffers = true) const = 0;
 };
 
 } // namespace android
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index 948f597..12c772a 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -85,7 +85,7 @@
 
     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) const override;
+                      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 0965f52..bfbc2aa 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -83,7 +83,7 @@
 
     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) const override;
+                      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 14b65bc..60115f9 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -83,7 +83,7 @@
 
     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) const override;
+                      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 324d9e1..34a5b17 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -42,6 +42,29 @@
 public:
     static inline GraphicBufferAllocator& get() { return getInstance(); }
 
+    /**
+     * Allocates and imports a gralloc buffer.
+     *
+     * The handle must be freed with GraphicBufferAllocator::free() when no longer needed.
+     */
+    status_t allocate(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+                      uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+                      std::string requestorName);
+
+    /**
+     * Allocates and does NOT import a gralloc buffer. Buffers cannot be used until they have
+     * been imported. This function is for advanced use cases only.
+     *
+     * The raw native handle must be freed by calling native_handle_close() followed by
+     * native_handle_delete().
+     */
+    status_t allocateRawHandle(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+                               uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+                               std::string requestorName);
+
+    /**
+     * DEPRECATED: GraphicBufferAllocator does not use the graphicBufferId.
+     */
     status_t allocate(uint32_t w, uint32_t h, PixelFormat format,
             uint32_t layerCount, uint64_t usage,
             buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
@@ -66,6 +89,10 @@
         std::string requestorName;
     };
 
+    status_t allocateHelper(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+                            uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+                            std::string requestorName, bool importBuffer);
+
     static Mutex sLock;
     static KeyedVector<buffer_handle_t, alloc_rec_t> sAllocList;
 
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index c401a48..83fb144 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -23,6 +23,7 @@
 #include <memory>
 
 #include <ui/PixelFormat.h>
+#include <ui/Rect.h>
 #include <utils/Singleton.h>
 
 
@@ -36,7 +37,6 @@
 // ---------------------------------------------------------------------------
 
 class GrallocMapper;
-class Rect;
 
 class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
 {
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index f29a370..fab2d9e 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -38,7 +38,7 @@
 public:
     Transform();
     Transform(const Transform&  other);
-    explicit Transform(uint32_t orientation);
+    explicit Transform(uint32_t orientation, int w = 0, int h = 0);
     ~Transform();
 
     enum orientation_flags {
diff --git a/include/private/ui/RegionHelper.h b/libs/ui/include_private/ui/RegionHelper.h
similarity index 70%
rename from include/private/ui/RegionHelper.h
rename to libs/ui/include_private/ui/RegionHelper.h
index 0ec3e94..92cfba8 100644
--- a/include/private/ui/RegionHelper.h
+++ b/libs/ui/include_private/ui/RegionHelper.h
@@ -17,18 +17,17 @@
 #ifndef ANDROID_UI_PRIVATE_REGION_HELPER_H
 #define ANDROID_UI_PRIVATE_REGION_HELPER_H
 
-#include <limits>
 #include <stdint.h>
 #include <sys/types.h>
+#include <limits>
 
 #include <limits>
 
 namespace android {
 // ----------------------------------------------------------------------------
 
-template<typename RECT>
-class region_operator
-{
+template <typename RECT>
+class region_operator {
 public:
     typedef typename RECT::value_type TYPE;
     static const TYPE max_value = std::numeric_limits<TYPE>::max();
@@ -40,39 +39,32 @@
      *    their corresponding value with the above formulae and use
      *    it when instantiating a region_operator.
      */
-    static const uint32_t LHS = 0x5;  // 0b101
-    static const uint32_t RHS = 0x6;  // 0b110
-    enum {
-        op_nand = LHS & ~RHS,
-        op_and  = LHS &  RHS,
-        op_or   = LHS |  RHS,
-        op_xor  = LHS ^  RHS
-    };
+    static const uint32_t LHS = 0x5; // 0b101
+    static const uint32_t RHS = 0x6; // 0b110
+    enum { op_nand = LHS & ~RHS, op_and = LHS & RHS, op_or = LHS | RHS, op_xor = LHS ^ RHS };
 
     struct region {
         RECT const* rects;
         size_t count;
         TYPE dx;
         TYPE dy;
-        inline region(const region& rhs) 
-            : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) { }
-        inline region(RECT const* _r, size_t _c)
-            : rects(_r), count(_c), dx(), dy() { }
+        inline region(const region& rhs)
+              : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) {}
+        inline region(RECT const* _r, size_t _c) : rects(_r), count(_c), dx(), dy() {}
         inline region(RECT const* _r, size_t _c, TYPE _dx, TYPE _dy)
-            : rects(_r), count(_c), dx(_dx), dy(_dy) { }
+              : rects(_r), count(_c), dx(_dx), dy(_dy) {}
     };
 
     class region_rasterizer {
         friend class region_operator;
         virtual void operator()(const RECT& rect) = 0;
+
     public:
-        virtual ~region_rasterizer() { }
+        virtual ~region_rasterizer() {}
     };
-    
+
     inline region_operator(uint32_t op, const region& lhs, const region& rhs)
-        : op_mask(op), spanner(lhs, rhs) 
-    {
-    }
+          : op_mask(op), spanner(lhs, rhs) {}
 
     void operator()(region_rasterizer& rasterizer) {
         RECT current(Rect::EMPTY_RECT);
@@ -83,31 +75,26 @@
             do {
                 int inner_inside = spannerInner.next(current.left, current.right);
                 if ((op_mask >> inner_inside) & 1) {
-                    if (current.left < current.right && 
-                            current.top < current.bottom) {
+                    if (current.left < current.right && current.top < current.bottom) {
                         rasterizer(current);
                     }
                 }
-            } while(!spannerInner.isDone());
-        } while(!spanner.isDone());
+            } while (!spannerInner.isDone());
+        } while (!spanner.isDone());
     }
 
-private:    
+private:
     uint32_t op_mask;
 
-    class SpannerBase
-    {
+    class SpannerBase {
     public:
         SpannerBase()
-            : lhs_head(max_value), lhs_tail(max_value),
-              rhs_head(max_value), rhs_tail(max_value) {
-        }
+              : lhs_head(max_value),
+                lhs_tail(max_value),
+                rhs_head(max_value),
+                rhs_tail(max_value) {}
 
-        enum {
-            lhs_before_rhs   = 0,
-            lhs_after_rhs    = 1,
-            lhs_coincide_rhs = 2
-        };
+        enum { lhs_before_rhs = 0, lhs_after_rhs = 1, lhs_coincide_rhs = 2 };
 
     protected:
         TYPE lhs_head;
@@ -115,9 +102,7 @@
         TYPE rhs_head;
         TYPE rhs_tail;
 
-        inline int next(TYPE& head, TYPE& tail,
-                bool& more_lhs, bool& more_rhs) 
-        {
+        inline int next(TYPE& head, TYPE& tail, bool& more_lhs, bool& more_rhs) {
             int inside;
             more_lhs = false;
             more_rhs = false;
@@ -157,32 +142,26 @@
         }
     };
 
-    class Spanner : protected SpannerBase 
-    {
+    class Spanner : protected SpannerBase {
         friend class region_operator;
         region lhs;
         region rhs;
 
     public:
-        inline Spanner(const region& _lhs, const region& _rhs)
-        : lhs(_lhs), rhs(_rhs)
-        {
+        inline Spanner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) {
             if (lhs.count) {
-                SpannerBase::lhs_head = lhs.rects->top      + lhs.dy;
-                SpannerBase::lhs_tail = lhs.rects->bottom   + lhs.dy;
+                SpannerBase::lhs_head = lhs.rects->top + lhs.dy;
+                SpannerBase::lhs_tail = lhs.rects->bottom + lhs.dy;
             }
             if (rhs.count) {
-                SpannerBase::rhs_head = rhs.rects->top      + rhs.dy;
-                SpannerBase::rhs_tail = rhs.rects->bottom   + rhs.dy;
+                SpannerBase::rhs_head = rhs.rects->top + rhs.dy;
+                SpannerBase::rhs_tail = rhs.rects->bottom + rhs.dy;
             }
         }
 
-        inline bool isDone() const {
-            return !rhs.count && !lhs.count;
-        }
+        inline bool isDone() const { return !rhs.count && !lhs.count; }
 
-        inline int next(TYPE& top, TYPE& bottom) 
-        {
+        inline int next(TYPE& top, TYPE& bottom) {
             bool more_lhs = false;
             bool more_rhs = false;
             int inside = SpannerBase::next(top, bottom, more_lhs, more_rhs);
@@ -196,22 +175,21 @@
         }
 
     private:
-        static inline 
-        void advance(region& reg, TYPE& aTop, TYPE& aBottom) {
+        static inline void advance(region& reg, TYPE& aTop, TYPE& aBottom) {
             // got to next span
             size_t count = reg.count;
-            RECT const * rects = reg.rects;
-            RECT const * const end = rects + count;
+            RECT const* rects = reg.rects;
+            RECT const* const end = rects + count;
             const int top = rects->top;
             while (rects != end && rects->top == top) {
                 rects++;
                 count--;
             }
             if (rects != end) {
-                aTop    = rects->top    + reg.dy;
+                aTop = rects->top + reg.dy;
                 aBottom = rects->bottom + reg.dy;
             } else {
-                aTop    = max_value;
+                aTop = max_value;
                 aBottom = max_value;
             }
             reg.rects = rects;
@@ -219,21 +197,17 @@
         }
     };
 
-    class SpannerInner : protected SpannerBase 
-    {
+    class SpannerInner : protected SpannerBase {
         region lhs;
         region rhs;
-        
+
     public:
-        inline SpannerInner(const region& _lhs, const region& _rhs)
-            : lhs(_lhs), rhs(_rhs)
-        {
-        }
+        inline SpannerInner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) {}
 
         inline void prepare(int inside) {
             if (inside == SpannerBase::lhs_before_rhs) {
                 if (lhs.count) {
-                    SpannerBase::lhs_head = lhs.rects->left  + lhs.dx;
+                    SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
                     SpannerBase::lhs_tail = lhs.rects->right + lhs.dx;
                 }
                 SpannerBase::rhs_head = max_value;
@@ -242,28 +216,26 @@
                 SpannerBase::lhs_head = max_value;
                 SpannerBase::lhs_tail = max_value;
                 if (rhs.count) {
-                    SpannerBase::rhs_head = rhs.rects->left  + rhs.dx;
+                    SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
                     SpannerBase::rhs_tail = rhs.rects->right + rhs.dx;
                 }
             } else {
                 if (lhs.count) {
-                    SpannerBase::lhs_head = lhs.rects->left  + lhs.dx;
+                    SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
                     SpannerBase::lhs_tail = lhs.rects->right + lhs.dx;
                 }
                 if (rhs.count) {
-                    SpannerBase::rhs_head = rhs.rects->left  + rhs.dx;
+                    SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
                     SpannerBase::rhs_tail = rhs.rects->right + rhs.dx;
                 }
             }
         }
 
         inline bool isDone() const {
-            return SpannerBase::lhs_head == max_value && 
-                   SpannerBase::rhs_head == max_value;
+            return SpannerBase::lhs_head == max_value && SpannerBase::rhs_head == max_value;
         }
 
-        inline int next(TYPE& left, TYPE& right) 
-        {
+        inline int next(TYPE& left, TYPE& right) {
             bool more_lhs = false;
             bool more_rhs = false;
             int inside = SpannerBase::next(left, right, more_lhs, more_rhs);
@@ -277,17 +249,16 @@
         }
 
     private:
-        static inline 
-        void advance(region& reg, TYPE& left, TYPE& right) {
+        static inline void advance(region& reg, TYPE& left, TYPE& right) {
             if (reg.rects && reg.count) {
                 const int cur_span_top = reg.rects->top;
                 reg.rects++;
                 reg.count--;
                 if (!reg.count || reg.rects->top != cur_span_top) {
-                    left  = max_value;
+                    left = max_value;
                     right = max_value;
                 } else {
-                    left  = reg.rects->left  + reg.dx;
+                    left = reg.rects->left + reg.dx;
                     right = reg.rects->right + reg.dx;
                 }
             }
@@ -298,6 +269,6 @@
 };
 
 // ----------------------------------------------------------------------------
-};
+}; // namespace android
 
 #endif /* ANDROID_UI_PRIVATE_REGION_HELPER_H */
diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h
index 22c80a4..7660e9f 100644
--- a/libs/ui/tests/mock/MockGrallocAllocator.h
+++ b/libs/ui/tests/mock/MockGrallocAllocator.h
@@ -36,7 +36,7 @@
     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,
-                 buffer_handle_t* outBufferHandles),
+                 buffer_handle_t* outBufferHandles, bool less),
                 (const, override));
 };
 
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 7b8e0f8..baba64f 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -1,20 +1,6 @@
-filegroup {
-    name: "gpuservice_sources",
-    srcs: [
-        "GpuService.cpp",
-        "gpustats/GpuStats.cpp"
-    ],
-}
-
-filegroup {
-    name: "gpuservice_binary_sources",
-    srcs: ["main_gpuservice.cpp"],
-}
-
 cc_defaults {
     name: "gpuservice_defaults",
     cflags: [
-        "-DLOG_TAG=\"GpuService\"",
         "-Wall",
         "-Werror",
         "-Wformat",
@@ -22,12 +8,13 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
-    srcs: [
-        ":gpuservice_sources",
-    ],
-    include_dirs: [
-        "frameworks/native/vulkan/vkjson",
-        "frameworks/native/vulkan/include",
+}
+
+cc_defaults {
+    name: "libgpuservice_defaults",
+    defaults: ["gpuservice_defaults"],
+    cflags: [
+        "-DLOG_TAG=\"GpuService\"",
     ],
     shared_libs: [
         "libbase",
@@ -36,17 +23,22 @@
         "libgraphicsenv",
         "liblog",
         "libutils",
-        "libvulkan",
+        "libvkjson",
     ],
     static_libs: [
         "libserviceutils",
-        "libvkjson",
+    ],
+    export_static_lib_headers: [
+        "libserviceutils",
+    ],
+    export_shared_lib_headers: [
+        "libgraphicsenv",
     ],
 }
 
 cc_defaults {
-    name: "gpuservice_production_defaults",
-    defaults: ["gpuservice_defaults"],
+    name: "libgpuservice_production_defaults",
+    defaults: ["libgpuservice_defaults"],
     cflags: [
         "-fvisibility=hidden",
         "-fwhole-program-vtables", // requires ThinLTO
@@ -56,8 +48,24 @@
     },
 }
 
+filegroup {
+    name: "libgpuservice_sources",
+    srcs: [
+        "GpuService.cpp",
+        "gpustats/GpuStats.cpp"
+    ],
+}
+
+cc_library_shared {
+    name: "libgpuservice",
+    defaults: ["libgpuservice_production_defaults"],
+    srcs: [
+        ":libgpuservice_sources",
+    ],
+}
+
 cc_defaults {
-    name: "gpuservice_binary",
+    name: "libgpuservice_binary",
     defaults: ["gpuservice_defaults"],
     shared_libs: [
         "libbinder",
@@ -68,9 +76,17 @@
     ldflags: ["-Wl,--export-dynamic"],
 }
 
+filegroup {
+    name: "gpuservice_binary_sources",
+    srcs: ["main_gpuservice.cpp"],
+}
+
 cc_binary {
     name: "gpuservice",
-    defaults: ["gpuservice_binary"],
+    defaults: ["libgpuservice_binary"],
     init_rc: ["gpuservice.rc"],
     srcs: [":gpuservice_binary_sources"],
+    shared_libs: [
+        "libgpuservice",
+    ],
 }
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index e7640dd..1043390 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -46,7 +46,6 @@
 }
 
 void InputManager::initialize() {
-    mReaderThread = new InputReaderThread(mReader);
     mDispatcherThread = new InputDispatcherThread(mDispatcher);
 }
 
@@ -57,9 +56,9 @@
         return result;
     }
 
-    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+    result = mReader->start();
     if (result) {
-        ALOGE("Could not start InputReader thread due to error %d.", result);
+        ALOGE("Could not start InputReader due to error %d.", result);
 
         mDispatcherThread->requestExit();
         return result;
@@ -69,9 +68,9 @@
 }
 
 status_t InputManager::stop() {
-    status_t result = mReaderThread->requestExitAndWait();
+    status_t result = mReader->stop();
     if (result) {
-        ALOGW("Could not stop InputReader thread due to error %d.", result);
+        ALOGW("Could not stop InputReader due to error %d.", result);
     }
 
     result = mDispatcherThread->requestExitAndWait();
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 40f66d8..2a7ed0f 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -43,15 +43,15 @@
 /*
  * The input manager is the core of the system event processing.
  *
- * The input manager uses two threads.
+ * The input manager has two components.
  *
- * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events,
- *    applies policy, and posts messages to a queue managed by the DispatcherThread.
+ * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies
+ *    policy, and posts messages to a queue managed by the InputDispatcherThread.
  * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
  *    queue and asynchronously dispatches them to applications.
  *
- * By design, the InputReaderThread class and InputDispatcherThread class do not share any
- * internal state.  Moreover, all communication is done one way from the InputReaderThread
+ * By design, the InputReader class and InputDispatcherThread class do not share any
+ * internal state.  Moreover, all communication is done one way from the InputReader
  * into the InputDispatcherThread and never the reverse.  Both classes may interact with the
  * InputDispatchPolicy, however.
  *
@@ -102,7 +102,6 @@
 
 private:
     sp<InputReaderInterface> mReader;
-    sp<InputReaderThread> mReaderThread;
 
     sp<InputClassifierInterface> mClassifier;
 
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 0422d83..2d6f2c1 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -33,20 +33,6 @@
 
 namespace android {
 
-// --- InputReaderThread ---
-
-InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
-        Thread(/*canCallJava*/ true), mReader(reader) {
-}
-
-InputReaderThread::~InputReaderThread() {
-}
-
-bool InputReaderThread::threadLoop() {
-    mReader->loopOnce();
-    return true;
-}
-
 // --- InputReaderConfiguration ---
 
 std::string InputReaderConfiguration::changesToString(uint32_t changes) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index c219941..b9bec44 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -252,6 +252,10 @@
         mDispatchEnabled(false),
         mDispatchFrozen(false),
         mInputFilterEnabled(false),
+        // mInTouchMode will be initialized by the WindowManager to the default device config.
+        // To avoid leaking stack in case that call never comes, and for tests,
+        // initialize it here anyways.
+        mInTouchMode(true),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
         mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
     mLooper = new Looper(false);
@@ -294,6 +298,12 @@
         if (runCommandsLockedInterruptible()) {
             nextWakeupTime = LONG_LONG_MIN;
         }
+
+        // We are about to enter an infinitely long sleep, because we have no commands or
+        // pending or queued events
+        if (nextWakeupTime == LONG_LONG_MAX) {
+            mDispatcherEnteredIdle.notify_all();
+        }
     } // release lock
 
     // Wait for callback or timeout or wake.  (make sure we round up, not down)
@@ -2295,10 +2305,12 @@
                 reportTouchEventForStatistics(*motionEntry);
                 break;
             }
-
-            default:
-                ALOG_ASSERT(false);
+            case EventEntry::Type::CONFIGURATION_CHANGED:
+            case EventEntry::Type::DEVICE_RESET: {
+                LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
+                                 EventEntry::typeToString(eventEntry->type));
                 return;
+            }
         }
 
         // Check the result.
@@ -3030,7 +3042,7 @@
         }
 
         default:
-            ALOGW("Cannot inject event of type %d", event->getType());
+            ALOGW("Cannot inject %s events", inputEventTypeToString(event->getType()));
             return INPUT_EVENT_INJECTION_FAILED;
     }
 
@@ -3536,6 +3548,11 @@
     mLooper->wake();
 }
 
+void InputDispatcher::setInTouchMode(bool inTouchMode) {
+    std::scoped_lock lock(mLock);
+    mInTouchMode = inTouchMode;
+}
+
 bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
     if (fromToken == toToken) {
         if (DEBUG_FOCUS) {
@@ -4571,4 +4588,22 @@
     mDispatcherIsAlive.wait(_l);
 }
 
+/**
+ * Wake up the dispatcher and wait until it processes all events and commands.
+ * The notification of mDispatcherEnteredIdle is guaranteed to happen after wake(), so
+ * this method can be safely called from any thread, as long as you've ensured that
+ * the work you are interested in completing has already been queued.
+ */
+bool InputDispatcher::waitForIdle() {
+    /**
+     * Timeout should represent the longest possible time that a device might spend processing
+     * events and commands.
+     */
+    constexpr std::chrono::duration TIMEOUT = 100ms;
+    std::unique_lock lock(mLock);
+    mLooper->wake();
+    std::cv_status result = mDispatcherEnteredIdle.wait_for(lock, TIMEOUT);
+    return result == std::cv_status::no_timeout;
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index d21b0a1..38f8674 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -81,6 +81,7 @@
 
     virtual void dump(std::string& dump) override;
     virtual void monitor() override;
+    virtual bool waitForIdle() override;
 
     virtual void dispatchOnce() override;
 
@@ -102,6 +103,7 @@
     virtual void setFocusedDisplay(int32_t displayId) override;
     virtual void setInputDispatchMode(bool enabled, bool frozen) override;
     virtual void setInputFilterEnabled(bool enabled) override;
+    virtual void setInTouchMode(bool inTouchMode) override;
 
     virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
                                     const sp<IBinder>& toToken) override;
@@ -128,6 +130,7 @@
     std::mutex mLock;
 
     std::condition_variable mDispatcherIsAlive;
+    std::condition_variable mDispatcherEnteredIdle;
 
     sp<Looper> mLooper;
 
@@ -247,6 +250,7 @@
     bool mDispatchEnabled GUARDED_BY(mLock);
     bool mDispatchFrozen GUARDED_BY(mLock);
     bool mInputFilterEnabled GUARDED_BY(mLock);
+    bool mInTouchMode GUARDED_BY(mLock);
 
     std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
             GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 5acf92b..2e9bca2 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -93,21 +93,22 @@
     sp<InputChannel> inputChannel;
 
     // Flags for the input target.
-    int32_t flags;
+    int32_t flags = 0;
 
     // The x and y offset to add to a MotionEvent as it is delivered.
     // (ignored for KeyEvents)
-    float xOffset, yOffset;
+    float xOffset = 0.0f;
+    float yOffset = 0.0f;
 
     // Scaling factor to apply to MotionEvent as it is delivered.
     // (ignored for KeyEvents)
-    float globalScaleFactor;
+    float globalScaleFactor = 1.0f;
     float windowXScale = 1.0f;
     float windowYScale = 1.0f;
 
     // 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/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index ce7366f..db9fba6 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -63,6 +63,14 @@
     /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
     virtual void monitor() = 0;
 
+    /**
+     * Wait until dispatcher is idle. That means, there are no further events to be processed,
+     * and all of the policy callbacks have been completed.
+     * Return true if the dispatcher is idle.
+     * Return false if the timeout waiting for the dispatcher to become idle has expired.
+     */
+    virtual bool waitForIdle() = 0;
+
     /* Runs a single iteration of the dispatch loop.
      * Nominally processes one queued event, a timeout, or a response from an input consumer.
      *
@@ -116,6 +124,14 @@
      */
     virtual void setInputFilterEnabled(bool enabled) = 0;
 
+    /**
+     * Set the touch mode state.
+     * Touch mode is a global state that apps may enter / exit based on specific
+     * user interactions with input devices.
+     * If true, the device is in touch mode.
+     */
+    virtual void setInTouchMode(bool inTouchMode) = 0;
+
     /* Transfers touch focus from one window to another window.
      *
      * Returns true on success.  False if the window did not actually have touch focus.
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 5d576b9..56c0a73 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -19,12 +19,12 @@
 
 #include "PointerControllerInterface.h"
 
+#include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <input/InputDevice.h>
-#include <input/DisplayViewport.h>
 #include <input/VelocityControl.h>
 #include <input/VelocityTracker.h>
-#include <utils/Thread.h>
+#include <utils/Errors.h>
 #include <utils/RefBase.h>
 
 #include <stddef.h>
@@ -44,7 +44,16 @@
 
 namespace android {
 
-/* Processes raw input events and sends cooked event data to an input listener. */
+// --- InputReaderInterface ---
+
+/* The interface for the InputReader shared library.
+ *
+ * Manages one or more threads that process raw input events and sends cooked event data to an
+ * input listener.
+ *
+ * The implementation must guarantee thread safety for this interface. However, since the input
+ * listener is NOT thread safe, all calls to the listener must happen from the same thread.
+ */
 class InputReaderInterface : public virtual RefBase {
 protected:
     InputReaderInterface() { }
@@ -56,18 +65,17 @@
      * This method may be called on any thread (usually by the input manager). */
     virtual void dump(std::string& dump) = 0;
 
-    /* Called by the heatbeat to ensures that the reader has not deadlocked. */
+    /* Called by the heartbeat to ensures that the reader has not deadlocked. */
     virtual void monitor() = 0;
 
     /* Returns true if the input device is enabled. */
     virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
 
-    /* Runs a single iteration of the processing loop.
-     * Nominally reads and processes one incoming message from the EventHub.
-     *
-     * This method should be called on the input reader thread.
-     */
-    virtual void loopOnce() = 0;
+    /* Makes the reader start processing events from the kernel. */
+    virtual status_t start() = 0;
+
+    /* Makes the reader stop processing any more events. */
+    virtual status_t stop() = 0;
 
     /* Gets information about all input devices.
      *
@@ -104,17 +112,7 @@
     virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
 };
 
-/* Reads raw events from the event hub and processes them, endlessly. */
-class InputReaderThread : public Thread {
-public:
-    explicit InputReaderThread(const sp<InputReaderInterface>& reader);
-    virtual ~InputReaderThread();
-
-private:
-    sp<InputReaderInterface> mReader;
-
-    virtual bool threadLoop();
-};
+// --- InputReaderConfiguration ---
 
 /*
  * Input reader configuration.
@@ -285,6 +283,8 @@
     std::vector<DisplayViewport> mDisplays;
 };
 
+// --- TouchAffineTransformation ---
+
 struct TouchAffineTransformation {
     float x_scale;
     float x_ymix;
@@ -307,6 +307,8 @@
     void applyTo(float& x, float& y) const;
 };
 
+// --- InputReaderPolicyInterface ---
+
 /*
  * Input reader policy interface.
  *
@@ -316,8 +318,8 @@
  * The actual implementation is partially supported by callbacks into the DVM
  * via JNI.  This interface is also mocked in the unit tests.
  *
- * These methods must NOT re-enter the input reader since they may be called while
- * holding the input reader lock.
+ * These methods will NOT re-enter the input reader interface, so they may be called from
+ * any method in the input reader interface.
  */
 class InputReaderPolicyInterface : public virtual RefBase {
 protected:
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 3c16070..23c08b2 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -58,7 +58,6 @@
         "liblog",
         "libui",
         "libutils",
-        "libhardware_legacy",
     ],
 
     header_libs: [
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index c8da0ab..264d287 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -37,8 +37,6 @@
 
 #include "EventHub.h"
 
-#include <hardware_legacy/power.h>
-
 #include <android-base/stringprintf.h>
 #include <cutils/properties.h>
 #include <openssl/sha.h>
@@ -71,7 +69,6 @@
 
 static constexpr bool DEBUG = false;
 
-static const char* WAKE_LOCK_ID = "KeyEvents";
 static const char* DEVICE_PATH = "/dev/input";
 // v4l2 devices go directly into /dev
 static const char* VIDEO_DEVICE_PATH = "/dev";
@@ -296,7 +293,6 @@
         mPendingEventIndex(0),
         mPendingINotify(false) {
     ensureProcessCanBlockSuspend();
-    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
 
     mEpollFd = epoll_create1(EPOLL_CLOEXEC);
     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
@@ -354,8 +350,6 @@
     ::close(mINotifyFd);
     ::close(mWakeReadPipeFd);
     ::close(mWakeWritePipeFd);
-
-    release_wake_lock(WAKE_LOCK_ID);
 }
 
 InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
@@ -1046,26 +1040,24 @@
             break;
         }
 
-        // Poll for events.  Mind the wake lock dance!
-        // We hold a wake lock at all times except during epoll_wait().  This works due to some
-        // subtle choreography.  When a device driver has pending (unread) events, it acquires
-        // a kernel wake lock.  However, once the last pending event has been read, the device
-        // driver will release the kernel wake lock.  To prevent the system from going to sleep
-        // when this happens, the EventHub holds onto its own user wake lock while the client
-        // is processing events.  Thus the system can only sleep if there are no events
-        // pending or currently being processed.
+        // Poll for events.
+        // When a device driver has pending (unread) events, it acquires
+        // a kernel wake lock.  Once the last pending event has been read, the device
+        // driver will release the kernel wake lock, but the epoll will hold the wakelock,
+        // since we are using EPOLLWAKEUP. The wakelock is released by the epoll when epoll_wait
+        // is called again for the same fd that produced the event.
+        // Thus the system can only sleep if there are no events pending or
+        // currently being processed.
         //
         // The timeout is advisory only.  If the device is asleep, it will not wake just to
         // service the timeout.
         mPendingEventIndex = 0;
 
-        mLock.unlock(); // release lock before poll, must be before release_wake_lock
-        release_wake_lock(WAKE_LOCK_ID);
+        mLock.unlock(); // release lock before poll
 
         int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
 
-        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
-        mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock
+        mLock.lock(); // reacquire lock after poll
 
         if (pollResult == 0) {
             // Timed out.
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 1c5adc3..05f0db1 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -38,16 +38,38 @@
 #include <unistd.h>
 
 #include <log/log.h>
+#include <utils/Errors.h>
 
 #include <android-base/stringprintf.h>
 #include <input/Keyboard.h>
 #include <input/VirtualKeyMap.h>
-
+#include <utils/Thread.h>
 
 using android::base::StringPrintf;
 
 namespace android {
 
+// --- InputReader::InputReaderThread ---
+
+/* Thread that reads raw events from the event hub and processes them, endlessly. */
+class InputReader::InputReaderThread : public Thread {
+public:
+    explicit InputReaderThread(InputReader* reader)
+          : Thread(/* canCallJava */ true), mReader(reader) {}
+
+    ~InputReaderThread() {}
+
+private:
+    InputReader* mReader;
+
+    bool threadLoop() override {
+        mReader->loopOnce();
+        return true;
+    }
+};
+
+// --- InputReader ---
+
 InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
                          const sp<InputReaderPolicyInterface>& policy,
                          const sp<InputListenerInterface>& listener)
@@ -61,6 +83,7 @@
         mNextTimeout(LLONG_MAX),
         mConfigurationChangesToRefresh(0) {
     mQueuedListener = new QueuedInputListener(listener);
+    mThread = new InputReaderThread(this);
 
     { // acquire lock
         AutoMutex _l(mLock);
@@ -76,6 +99,28 @@
     }
 }
 
+status_t InputReader::start() {
+    if (mThread->isRunning()) {
+        return ALREADY_EXISTS;
+    }
+    return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+}
+
+status_t InputReader::stop() {
+    if (!mThread->isRunning()) {
+        return OK;
+    }
+    if (gettid() == mThread->getTid()) {
+        ALOGE("InputReader can only be stopped from outside of the InputReaderThread!");
+        return INVALID_OPERATION;
+    }
+    // Directly calling requestExitAndWait() causes the thread to not exit
+    // if mEventHub is waiting for a long timeout.
+    mThread->requestExit();
+    mEventHub->wake();
+    return mThread->requestExitAndWait();
+}
+
 void InputReader::loopOnce() {
     int32_t oldGeneration;
     int32_t timeoutMillis;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 557eb3b..5024906 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -38,12 +38,12 @@
  * that it sends to the input listener.  Some functions of the input reader, such as early
  * event filtering in low power states, are controlled by a separate policy object.
  *
- * The InputReader owns a collection of InputMappers.  Most of the work it does happens
- * on the input reader thread but the InputReader can receive queries from other system
+ * The InputReader owns a collection of InputMappers. InputReader starts its own thread, where
+ * most of the work happens, but the InputReader can receive queries from other system
  * components running on arbitrary threads.  To keep things manageable, the InputReader
  * uses a single Mutex to guard its state.  The Mutex may be held while calling into the
  * EventHub or the InputReaderPolicy but it is never held while calling into the
- * InputListener.
+ * InputListener. All calls to InputListener must happen from InputReader's thread.
  */
 class InputReader : public InputReaderInterface {
 public:
@@ -55,7 +55,8 @@
     virtual void dump(std::string& dump) override;
     virtual void monitor() override;
 
-    virtual void loopOnce() override;
+    virtual status_t start() override;
+    virtual status_t stop() override;
 
     virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
 
@@ -86,6 +87,10 @@
                                             const InputDeviceIdentifier& identifier,
                                             uint32_t classes);
 
+    // With each iteration of the loop, InputReader reads and processes one incoming message from
+    // the EventHub.
+    void loopOnce();
+
     class ContextImpl : public InputReaderContext {
         InputReader* mReader;
 
@@ -111,6 +116,9 @@
     friend class ContextImpl;
 
 private:
+    class InputReaderThread;
+    sp<InputReaderThread> mThread;
+
     Mutex mLock;
 
     Condition mReaderIsAliveCondition;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 8863ec2..c8d39cf 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -19,6 +19,7 @@
 #include <InputDispatcherThread.h>
 
 #include <binder/Binder.h>
+#include <input/Input.h>
 
 #include <gtest/gtest.h>
 #include <linux/input.h>
@@ -50,49 +51,55 @@
 
 public:
     FakeInputDispatcherPolicy() {
-        mOnPointerDownToken.clear();
     }
 
     void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
-        ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
-        ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_KEY);
-
-        const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
-        ASSERT_EQ(keyEvent.getEventTime(), args.eventTime);
-        ASSERT_EQ(keyEvent.getAction(), args.action);
-        ASSERT_EQ(keyEvent.getDisplayId(), args.displayId);
-
-        reset();
+        assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
+                                        args.displayId);
     }
 
     void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) {
-        ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
-        ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_MOTION);
-
-        const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
-        ASSERT_EQ(motionEvent.getEventTime(), args.eventTime);
-        ASSERT_EQ(motionEvent.getAction(), args.action);
-        ASSERT_EQ(motionEvent.getDisplayId(), args.displayId);
-
-        reset();
+        assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_MOTION, args.eventTime, args.action,
+                                        args.displayId);
     }
 
-    void assertFilterInputEventWasNotCalled() {
-        ASSERT_EQ(nullptr, mFilteredEvent)
-                << "Expected filterInputEvent() to not have been called.";
+    void assertFilterInputEventWasNotCalled() { ASSERT_EQ(nullptr, mFilteredEvent); }
+
+    void assertNotifyConfigurationChangedWasCalled(nsecs_t when) {
+        ASSERT_TRUE(mConfigurationChangedTime)
+                << "Timed out waiting for configuration changed call";
+        ASSERT_EQ(*mConfigurationChangedTime, when);
+        mConfigurationChangedTime = std::nullopt;
+    }
+
+    void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) {
+        ASSERT_TRUE(mLastNotifySwitch);
+        // We do not check sequenceNum because it is not exposed to the policy
+        EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime);
+        EXPECT_EQ(args.policyFlags, mLastNotifySwitch->policyFlags);
+        EXPECT_EQ(args.switchValues, mLastNotifySwitch->switchValues);
+        EXPECT_EQ(args.switchMask, mLastNotifySwitch->switchMask);
+        mLastNotifySwitch = std::nullopt;
     }
 
     void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
-        ASSERT_EQ(mOnPointerDownToken, touchedToken)
-                << "Expected token from onPointerDownOutsideFocus was not matched";
-        reset();
+        ASSERT_EQ(touchedToken, mOnPointerDownToken);
+        mOnPointerDownToken.clear();
+    }
+
+    void assertOnPointerDownWasNotCalled() {
+        ASSERT_TRUE(mOnPointerDownToken == nullptr)
+                << "Expected onPointerDownOutsideFocus to not have been called";
     }
 
 private:
     std::unique_ptr<InputEvent> mFilteredEvent;
+    std::optional<nsecs_t> mConfigurationChangedTime;
     sp<IBinder> mOnPointerDownToken;
+    std::optional<NotifySwitchArgs> mLastNotifySwitch;
 
-    virtual void notifyConfigurationChanged(nsecs_t) {
+    virtual void notifyConfigurationChanged(nsecs_t when) override {
+        mConfigurationChangedTime = when;
     }
 
     virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&,
@@ -144,7 +151,13 @@
         return false;
     }
 
-    virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) {
+    virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+                              uint32_t policyFlags) override {
+        /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
+         * essentially a passthrough for notifySwitch.
+         */
+        mLastNotifySwitch =
+                NotifySwitchArgs(1 /*sequenceNum*/, when, policyFlags, switchValues, switchMask);
     }
 
     virtual void pokeUserActivity(nsecs_t, int32_t) {
@@ -158,9 +171,26 @@
         mOnPointerDownToken = newToken;
     }
 
-    void reset() {
+    void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
+                                         int32_t displayId) {
+        ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
+        ASSERT_EQ(mFilteredEvent->getType(), type);
+
+        if (type == AINPUT_EVENT_TYPE_KEY) {
+            const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
+            EXPECT_EQ(keyEvent.getEventTime(), eventTime);
+            EXPECT_EQ(keyEvent.getAction(), action);
+            EXPECT_EQ(keyEvent.getDisplayId(), displayId);
+        } else if (type == AINPUT_EVENT_TYPE_MOTION) {
+            const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
+            EXPECT_EQ(motionEvent.getEventTime(), eventTime);
+            EXPECT_EQ(motionEvent.getAction(), action);
+            EXPECT_EQ(motionEvent.getDisplayId(), displayId);
+        } else {
+            FAIL() << "Unknown type: " << type;
+        }
+
         mFilteredEvent = nullptr;
-        mOnPointerDownToken.clear();
     }
 };
 
@@ -348,9 +378,30 @@
             << "Should reject motion events with duplicate pointer ids.";
 }
 
+/* Test InputDispatcher for notifyConfigurationChanged and notifySwitch events */
+
+TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) {
+    constexpr nsecs_t eventTime = 20;
+    NotifyConfigurationChangedArgs args(10 /*sequenceNum*/, eventTime);
+    mDispatcher->notifyConfigurationChanged(&args);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+
+    mFakePolicy->assertNotifyConfigurationChangedWasCalled(eventTime);
+}
+
+TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) {
+    NotifySwitchArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, 0 /*policyFlags*/,
+                          1 /*switchValues*/, 2 /*switchMask*/);
+    mDispatcher->notifySwitch(&args);
+
+    // InputDispatcher adds POLICY_FLAG_TRUSTED because the event went through InputListener
+    args.policyFlags |= POLICY_FLAG_TRUSTED;
+    mFakePolicy->assertNotifySwitchWasCalled(args);
+}
+
 // --- InputDispatcherTest SetInputWindowTest ---
-static const int32_t INJECT_EVENT_TIMEOUT = 500;
-static const int32_t DISPATCHING_TIMEOUT = 100;
+static constexpr int32_t INJECT_EVENT_TIMEOUT = 500;
+static constexpr int32_t DISPATCHING_TIMEOUT = 100;
 
 class FakeApplicationHandle : public InputApplicationHandle {
 public:
@@ -408,7 +459,8 @@
         ASSERT_NE(nullptr, event) << mName.c_str()
                                   << ": consumer should have returned non-NULL event.";
         ASSERT_EQ(expectedEventType, event->getType())
-                << mName.c_str() << ": event type should match.";
+                << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType)
+                << " event, got " << inputEventTypeToString(event->getType()) << " event";
 
         EXPECT_EQ(expectedDisplayId, event->getDisplayId());
 
@@ -532,9 +584,7 @@
         InputWindowHandle::releaseChannel();
     }
 protected:
-    virtual bool handled() {
-        return true;
-    }
+    virtual bool handled() override { return true; }
 
     bool mFocused;
     Rect mFrame;
@@ -634,10 +684,7 @@
     sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
             ADISPLAY_ID_DEFAULT);
 
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(window);
-
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
             AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -654,11 +701,7 @@
     sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
             ADISPLAY_ID_DEFAULT);
 
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(windowTop);
-    inputWindowHandles.push_back(windowSecond);
-
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
             AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -680,11 +723,8 @@
 
     // Expect one focus window exist in display.
     windowSecond->setFocus();
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(windowTop);
-    inputWindowHandles.push_back(windowSecond);
 
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
 
@@ -706,11 +746,8 @@
     // Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first)
     windowTop->setFocus();
     windowSecond->setFocus();
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(windowTop);
-    inputWindowHandles.push_back(windowSecond);
 
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
 
@@ -732,12 +769,9 @@
 
     windowTop->setFocus();
     windowSecond->setFocus();
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(windowTop);
-    inputWindowHandles.push_back(windowSecond);
     // Release channel for window is no longer valid.
     windowTop->releaseChannel();
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
 
     // Test inject a key down, should dispatch to a valid window.
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
@@ -762,8 +796,7 @@
 
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
-    std::vector<sp<InputWindowHandle>> inputWindowHandles{windowLeft, windowRight};
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({windowLeft, windowRight}, ADISPLAY_ID_DEFAULT);
 
     // Inject an event with coordinate in the area of right window, with mouse cursor in the area of
     // left window. This event should be dispatched to the left window.
@@ -774,6 +807,51 @@
     windowRight->assertNoEvents();
 }
 
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+    window->setFocus();
+
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+
+    // Window should receive key down event.
+    window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+    // When device reset happens, that key stream should be terminated with FLAG_CANCELED
+    // on the app side.
+    NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID);
+    mDispatcher->notifyDeviceReset(&args);
+    window->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
+                         AKEY_EVENT_FLAG_CANCELED);
+}
+
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({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);
+
+    // When device reset happens, that motion stream should be terminated with ACTION_CANCEL
+    // on the app side.
+    NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID);
+    mDispatcher->notifyDeviceReset(&args);
+    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, ADISPLAY_ID_DEFAULT,
+                         0 /*expectedFlags*/);
+}
+
 /* Test InputDispatcher for MultiDisplay */
 class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
 public:
@@ -784,25 +862,22 @@
         application1 = new FakeApplicationHandle();
         windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1",
                 ADISPLAY_ID_DEFAULT);
-        std::vector<sp<InputWindowHandle>> inputWindowHandles;
-        inputWindowHandles.push_back(windowInPrimary);
+
         // Set focus window for primary display, but focused display would be second one.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
         windowInPrimary->setFocus();
-        mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+        mDispatcher->setInputWindows({windowInPrimary}, ADISPLAY_ID_DEFAULT);
 
         application2 = new FakeApplicationHandle();
         windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
                 SECOND_DISPLAY_ID);
         // Set focus to second display window.
-        std::vector<sp<InputWindowHandle>> inputWindowHandles_Second;
-        inputWindowHandles_Second.push_back(windowInSecondary);
         // Set focus display to second one.
         mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
         // Set focus window for second display.
         mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
         windowInSecondary->setFocus();
-        mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
+        mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID);
     }
 
     virtual void TearDown() {
@@ -850,9 +925,8 @@
     windowInPrimary->assertNoEvents();
     windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
 
-    // Remove secondary display.
-    std::vector<sp<InputWindowHandle>> noWindows;
-    mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID);
+    // Remove all windows in secondary display.
+    mDispatcher->setInputWindows({}, SECOND_DISPLAY_ID);
 
     // Expect old focus should receive a cancel event.
     windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
@@ -941,7 +1015,7 @@
         motionArgs = generateMotionArgs(
                 AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
         mDispatcher->notifyMotion(&motionArgs);
-
+        ASSERT_TRUE(mDispatcher->waitForIdle());
         if (expectToBeFiltered) {
             mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
         } else {
@@ -956,6 +1030,7 @@
         mDispatcher->notifyKey(&keyArgs);
         keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP);
         mDispatcher->notifyKey(&keyArgs);
+        ASSERT_TRUE(mDispatcher->waitForIdle());
 
         if (expectToBeFiltered) {
             mFakePolicy->assertFilterInputEventWasCalled(keyArgs);
@@ -1012,34 +1087,31 @@
         // window.
         mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
 
-        mWindowFocused = new FakeWindowHandle(application, mDispatcher, "Second",
-                ADISPLAY_ID_DEFAULT);
-        mWindowFocused->setFrame(Rect(50, 50, 100, 100));
-        mWindowFocused->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
-        mWindowFocusedTouchPoint = 60;
+        mFocusedWindow =
+                new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+        mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
+        mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+        mFocusedWindowTouchPoint = 60;
 
         // Set focused application.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
-        mWindowFocused->setFocus();
+        mFocusedWindow->setFocus();
 
         // Expect one focus window exist in display.
-        std::vector<sp<InputWindowHandle>> inputWindowHandles;
-        inputWindowHandles.push_back(mUnfocusedWindow);
-        inputWindowHandles.push_back(mWindowFocused);
-        mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+        mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT);
     }
 
     virtual void TearDown() {
         InputDispatcherTest::TearDown();
 
         mUnfocusedWindow.clear();
-        mWindowFocused.clear();
+        mFocusedWindow.clear();
     }
 
 protected:
     sp<FakeWindowHandle> mUnfocusedWindow;
-    sp<FakeWindowHandle> mWindowFocused;
-    int32_t mWindowFocusedTouchPoint;
+    sp<FakeWindowHandle> mFocusedWindow;
+    int32_t mFocusedWindowTouchPoint;
 };
 
 // Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
@@ -1049,9 +1121,8 @@
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
             AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    // Call monitor to wait for the command queue to get flushed.
-    mDispatcher->monitor();
 
+    ASSERT_TRUE(mDispatcher->waitForIdle());
     mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken());
 }
 
@@ -1062,10 +1133,9 @@
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
             AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    // Call monitor to wait for the command queue to get flushed.
-    mDispatcher->monitor();
 
-    mFakePolicy->assertOnPointerDownEquals(nullptr);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertOnPointerDownWasNotCalled();
 }
 
 // Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
@@ -1073,10 +1143,9 @@
 TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    // Call monitor to wait for the command queue to get flushed.
-    mDispatcher->monitor();
 
-    mFakePolicy->assertOnPointerDownEquals(nullptr);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertOnPointerDownWasNotCalled();
 }
 
 // Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
@@ -1084,14 +1153,13 @@
 // onPointerDownOutsideFocus callback.
 TEST_F(InputDispatcherOnPointerDownOutsideFocus,
         OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, mWindowFocusedTouchPoint,
-            mWindowFocusedTouchPoint))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               mFocusedWindowTouchPoint, mFocusedWindowTouchPoint))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    // Call monitor to wait for the command queue to get flushed.
-    mDispatcher->monitor();
 
-    mFakePolicy->assertOnPointerDownEquals(nullptr);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertOnPointerDownWasNotCalled();
 }
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index c1c9122..d6624c9 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1113,6 +1113,9 @@
                                classes);
     }
 
+    // Make the protected loopOnce method accessible to tests.
+    using InputReader::loopOnce;
+
 protected:
     virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
                                             const InputDeviceIdentifier& identifier,
@@ -1133,12 +1136,8 @@
 protected:
     sp<FakeInputReaderPolicy> mFakePolicy;
 
-    virtual void SetUp() {
-        mFakePolicy = new FakeInputReaderPolicy();
-    }
-    virtual void TearDown() {
-        mFakePolicy.clear();
-    }
+    virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
+    virtual void TearDown() override { mFakePolicy.clear(); }
 };
 
 /**
@@ -1321,19 +1320,18 @@
     sp<TestInputListener> mFakeListener;
     sp<FakeInputReaderPolicy> mFakePolicy;
     std::shared_ptr<FakeEventHub> mFakeEventHub;
-    sp<InstrumentedInputReader> mReader;
+    std::unique_ptr<InstrumentedInputReader> mReader;
 
-    virtual void SetUp() {
+    virtual void SetUp() override {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
 
-        mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
+        mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+                                                            mFakeListener);
     }
 
-    virtual void TearDown() {
-        mReader.clear();
-
+    virtual void TearDown() override {
         mFakeListener.clear();
         mFakePolicy.clear();
     }
@@ -1354,16 +1352,12 @@
 
     void disableDevice(int32_t deviceId, InputDevice* device) {
         mFakePolicy->addDisabledDevice(deviceId);
-        configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
     }
 
     void enableDevice(int32_t deviceId, InputDevice* device) {
         mFakePolicy->removeDisabledDevice(deviceId);
-        configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
-    }
-
-    void configureDevice(uint32_t changes, InputDevice* device) {
-        device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
     }
 
     FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
@@ -1417,7 +1411,6 @@
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
-    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
     ASSERT_EQ(deviceId, resetArgs.deviceId);
 
     ASSERT_EQ(device->isEnabled(), true);
@@ -1425,7 +1418,6 @@
     mReader->loopOnce();
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
-    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
     ASSERT_EQ(deviceId, resetArgs.deviceId);
     ASSERT_EQ(device->isEnabled(), false);
 
@@ -1438,7 +1430,6 @@
     enableDevice(deviceId, device);
     mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
-    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
     ASSERT_EQ(deviceId, resetArgs.deviceId);
     ASSERT_EQ(device->isEnabled(), true);
 }
@@ -1629,7 +1620,6 @@
     FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN);
     device->addMapper(mapper);
     mReader->setNextDevice(device);
-    addDevice(deviceId, "fake", deviceClass, nullptr);
 
     const uint8_t hdmi1 = 1;
 
@@ -1637,13 +1627,21 @@
     mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
 
     // Add default and second display.
+    mFakePolicy->clearViewports();
     mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
             DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
     mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
             DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
     mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     mReader->loopOnce();
+
+    // Add the device, and make sure all of the callbacks are triggered.
+    // The device is added after the input port associations are processed since
+    // we do not yet support dynamic device-to-display associations.
+    ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled());
 
     // Device should only dispatch to the specified display.
     ASSERT_EQ(deviceId, device->getId());
@@ -1652,6 +1650,7 @@
 
     // Can't dispatch event from a disabled device.
     disableDevice(deviceId, device);
+    mReader->loopOnce();
     ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
 }
 
@@ -1674,7 +1673,7 @@
 
     InputDevice* mDevice;
 
-    virtual void SetUp() {
+    virtual void SetUp() override {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
@@ -1688,7 +1687,7 @@
                 DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
     }
 
-    virtual void TearDown() {
+    virtual void TearDown() override {
         delete mDevice;
 
         delete mFakeContext;
@@ -1912,7 +1911,7 @@
     FakeInputReaderContext* mFakeContext;
     InputDevice* mDevice;
 
-    virtual void SetUp() {
+    virtual void SetUp() override {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
@@ -1926,7 +1925,7 @@
         mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
     }
 
-    virtual void TearDown() {
+    virtual void TearDown() override {
         delete mDevice;
         delete mFakeContext;
         mFakeListener.clear();
@@ -2589,7 +2588,7 @@
 
     sp<FakePointerController> mFakePointerController;
 
-    virtual void SetUp() {
+    virtual void SetUp() override {
         InputMapperTest::SetUp();
 
         mFakePointerController = new FakePointerController();
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 14ed73d..c2e1204 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1173,6 +1173,11 @@
                 return nullptr;
             }
             int fd = resource->data[0];
+            if (!ashmem_valid(fd)) {
+                ALOGE("Supplied Ashmem memory region is invalid");
+                return nullptr;
+            }
+
             int size2 = ashmem_get_size_region(fd);
             // check size consistency
             if (size2 < static_cast<int64_t>(size)) {
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index 1cb0489..caf7f03 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -15,19 +15,20 @@
  */
 
 #include <inttypes.h>
+#include <android/hardware_buffer.h>
 #include <android/sensor.h>
 #include <sensor/Sensor.h>
 #include <sensor/SensorManager.h>
 #include <sensor/SensorEventQueue.h>
 #include <utils/Looper.h>
+#include <vndk/hardware_buffer.h>
 
 using namespace android;
 
 static nsecs_t sStartTime = 0;
 
 
-int receiver(__unused int fd, __unused int events, void* data)
-{
+int receiver(__unused int fd, __unused int events, void* data) {
     sp<SensorEventQueue> q((SensorEventQueue*)data);
     ssize_t n;
     ASensorEvent buffer[8];
@@ -59,11 +60,42 @@
     return 1;
 }
 
+void testInvalidSharedMem_NoCrash(SensorManager &mgr) {
+    AHardwareBuffer *hardwareBuffer;
+    char* buffer;
 
-int main()
-{
+    constexpr size_t kEventSize = sizeof(ASensorEvent);
+    constexpr size_t kNEvent = 4096; // enough to contain 1.5 * 800 * 2.2 events
+    constexpr size_t kMemSize = kEventSize * kNEvent;
+    AHardwareBuffer_Desc desc = {
+            .width = static_cast<uint32_t>(kMemSize),
+            .height = 1,
+            .layers = 1,
+            .format = AHARDWAREBUFFER_FORMAT_BLOB,
+            .usage = AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA
+                        | AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+    };
+
+    AHardwareBuffer_allocate(&desc, &hardwareBuffer);
+    AHardwareBuffer_lock(hardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY,
+                         -1, nullptr, reinterpret_cast<void **>(&buffer));
+
+    const native_handle_t *resourceHandle = AHardwareBuffer_getNativeHandle(hardwareBuffer);
+
+    // Pass in AHardwareBuffer, but with the wrong DIRECT_CHANNEL_TYPE to see
+    // if anything in the Sensor framework crashes
+    int ret = mgr.createDirectChannel(
+            kMemSize, ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY, resourceHandle);
+
+    // Should print -22 (BAD_VALUE) and the device runtime shouldn't restart
+    printf("createInvalidDirectChannel=%d\n", ret);
+}
+
+int main() {
     SensorManager& mgr = SensorManager::getInstanceForPackage(String16("Sensor Service Test"));
 
+    testInvalidSharedMem_NoCrash(mgr);
+
     Sensor const* const* list;
     ssize_t count = mgr.getSensorList(&list);
     printf("numSensors=%d\n", int(count));
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 127a3da..d476f7b4 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -174,6 +174,7 @@
         "Scheduler/VSyncDispatchTimerQueue.cpp",
         "Scheduler/VSyncPredictor.cpp",
         "Scheduler/VSyncModulator.cpp",
+        "Scheduler/VSyncReactor.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
         "SurfaceFlingerDefaultFactory.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 94c4a81..054acc5 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -56,6 +56,9 @@
 
 namespace android {
 
+static constexpr float defaultMaxMasteringLuminance = 1000.0;
+static constexpr float defaultMaxContentLuminance = 1000.0;
+
 BufferLayer::BufferLayer(const LayerCreationArgs& args)
       : Layer(args),
         mTextureName(args.textureName),
@@ -184,6 +187,14 @@
         layer.source.buffer.textureName = mTextureName;
         layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
         layer.source.buffer.isY410BT2020 = isHdrY410();
+        bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
+        bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
+        layer.source.buffer.maxMasteringLuminance = hasSmpte2086
+                ? mBufferInfo.mHdrMetadata.smpte2086.maxLuminance
+                : defaultMaxMasteringLuminance;
+        layer.source.buffer.maxContentLuminance = hasCta861_3
+                ? mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel
+                : defaultMaxContentLuminance;
         // TODO: we could be more subtle with isFixedSize()
         const bool useFiltering = targetSettings.needsFiltering || mNeedsFiltering || isFixedSize();
 
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 8957e90..945abd7 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -35,6 +35,7 @@
 BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
 
 BufferQueueLayer::~BufferQueueLayer() {
+    mContentsChangedListener->abandon();
     mConsumer->abandon();
 }
 
@@ -479,7 +480,9 @@
             mFlinger->getFactory().createBufferLayerConsumer(consumer, mFlinger->getRenderEngine(),
                                                              mTextureName, this);
     mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
-    mConsumer->setContentsChangedListener(this);
+
+    mContentsChangedListener = new ContentsChangedListener(this);
+    mConsumer->setContentsChangedListener(mContentsChangedListener);
     mConsumer->setName(String8(mName.data(), mName.size()));
 
     // BufferQueueCore::mMaxDequeuedBufferCount is default to 1
@@ -551,4 +554,57 @@
     return layer;
 }
 
+// -----------------------------------------------------------------------
+// Interface implementation for BufferLayerConsumer::ContentsChangedListener
+// -----------------------------------------------------------------------
+
+void BufferQueueLayer::ContentsChangedListener::onFrameAvailable(const BufferItem& item) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameAvailable(item);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameReplaced(const BufferItem& item) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameReplaced(item);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onSidebandStreamChanged() {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onSidebandStreamChanged();
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameDequeued(const uint64_t bufferId) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameDequeued(bufferId);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameDetached(const uint64_t bufferId) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameDetached(bufferId);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameCancelled(const uint64_t bufferId) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameCancelled(bufferId);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::abandon() {
+    Mutex::Autolock lock(mMutex);
+    mBufferQueueLayer = nullptr;
+}
+
+// -----------------------------------------------------------------------
+
 } // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 1b1fccd..b040556 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -29,7 +29,7 @@
  * This also implements onFrameAvailable(), which notifies SurfaceFlinger
  * that new data has arrived.
  */
-class BufferQueueLayer : public BufferLayer, public BufferLayerConsumer::ContentsChangedListener {
+class BufferQueueLayer : public BufferLayer {
 public:
     // Only call while mStateLock is held
     explicit BufferQueueLayer(const LayerCreationArgs&);
@@ -84,18 +84,37 @@
     void latchPerFrameState(compositionengine::LayerFECompositionState&) const override;
     sp<Layer> createClone() override;
 
-    // -----------------------------------------------------------------------
-    // Interface implementation for BufferLayerConsumer::ContentsChangedListener
-    // -----------------------------------------------------------------------
+    void onFrameAvailable(const BufferItem& item);
+    void onFrameReplaced(const BufferItem& item);
+    void onSidebandStreamChanged();
+    void onFrameDequeued(const uint64_t bufferId);
+    void onFrameDetached(const uint64_t bufferId);
+    void onFrameCancelled(const uint64_t bufferId);
+
 protected:
     void gatherBufferInfo() override;
 
-    void onFrameAvailable(const BufferItem& item) override;
-    void onFrameReplaced(const BufferItem& item) override;
-    void onSidebandStreamChanged() override;
-    void onFrameDequeued(const uint64_t bufferId) override;
-    void onFrameDetached(const uint64_t bufferId) override;
-    void onFrameCancelled(const uint64_t bufferId) override;
+    // -----------------------------------------------------------------------
+    // Interface implementation for BufferLayerConsumer::ContentsChangedListener
+    // -----------------------------------------------------------------------
+    class ContentsChangedListener : public BufferLayerConsumer::ContentsChangedListener {
+    public:
+        ContentsChangedListener(BufferQueueLayer* bufferQueueLayer)
+              : mBufferQueueLayer(bufferQueueLayer) {}
+        void abandon();
+
+    protected:
+        void onFrameAvailable(const BufferItem& item) override;
+        void onFrameReplaced(const BufferItem& item) override;
+        void onSidebandStreamChanged() override;
+        void onFrameDequeued(const uint64_t bufferId) override;
+        void onFrameDetached(const uint64_t bufferId) override;
+        void onFrameCancelled(const uint64_t bufferId) override;
+
+    private:
+        BufferQueueLayer* mBufferQueueLayer = nullptr;
+        Mutex mMutex;
+    };
     // -----------------------------------------------------------------------
 
 public:
@@ -130,6 +149,8 @@
     // thread-safe
     std::atomic<int32_t> mQueuedFrames{0};
     std::atomic<bool> mSidebandStreamChanged{false};
+
+    sp<ContentsChangedListener> mContentsChangedListener;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 738a2a4..78f8104 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -20,6 +20,7 @@
         "liblayers_proto",
         "liblog",
         "libnativewindow",
+        "libprotobuf-cpp-lite",
         "libsync",
         "libtimestats_proto",
         "libui",
@@ -28,6 +29,7 @@
     static_libs: [
         "libmath",
         "librenderengine",
+        "libtimestats",
         "libtrace_proto",
     ],
     header_libs: [
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 8687d0c..e3650f3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -16,10 +16,11 @@
 
 #pragma once
 
-#include <memory>
-
+#include <TimeStats/TimeStats.h>
 #include <utils/Timers.h>
 
+#include <memory>
+
 namespace android {
 
 class HWComposer;
@@ -55,6 +56,9 @@
     virtual renderengine::RenderEngine& getRenderEngine() const = 0;
     virtual void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) = 0;
 
+    virtual TimeStats& getTimeStats() const = 0;
+    virtual void setTimeStats(const std::shared_ptr<TimeStats>&) = 0;
+
     virtual bool needsAnotherUpdate() const = 0;
     virtual nsecs_t getLastFrameRefreshTimestamp() const = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index e585769..0a70165 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -93,6 +93,13 @@
     virtual std::optional<renderengine::LayerSettings> prepareClientComposition(
             ClientCompositionTargetSettings&) = 0;
 
+    // Returns the LayerSettings used to draw shadows around a layer. It is passed
+    // to RenderEngine::drawLayers. Returns nullopt_t if the layer does not render
+    // shadows.
+    virtual std::optional<renderengine::LayerSettings> prepareShadowClientComposition(
+            const renderengine::LayerSettings& layerSettings, const Rect& displayViewport,
+            ui::Dataspace outputDataspace) = 0;
+
     // Called after the layer is displayed to update the presentation fence
     virtual void onLayerDisplayed(const sp<Fence>&) = 0;
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 5f42ea1..076fdad 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -272,7 +272,7 @@
     virtual bool getSkipColorTransform() const = 0;
     virtual FrameFences presentAndGetFrameFences() = 0;
     virtual std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
-            bool supportsProtectedContent, Region& clearRegion) = 0;
+            bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) = 0;
     virtual void appendRegionFlashRequests(
             const Region& flashRegion,
             std::vector<renderengine::LayerSettings>& clientCompositionLayers) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index f416c9c..450b9ca 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -36,6 +36,9 @@
     renderengine::RenderEngine& getRenderEngine() const override;
     void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override;
 
+    TimeStats& getTimeStats() const override;
+    void setTimeStats(const std::shared_ptr<TimeStats>&) override;
+
     bool needsAnotherUpdate() const override;
     nsecs_t getLastFrameRefreshTimestamp() const override;
 
@@ -56,6 +59,7 @@
 private:
     std::unique_ptr<HWComposer> mHwComposer;
     std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+    std::shared_ptr<TimeStats> mTimeStats;
     bool mNeedsAnotherUpdate = false;
     nsecs_t mRefreshStartTime = 0;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index a2342ae..159e928 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -97,7 +97,8 @@
     bool getSkipColorTransform() const override;
     compositionengine::Output::FrameFences presentAndGetFrameFences() override;
     std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
-            bool supportsProtectedContent, Region& clearRegion) override;
+            bool supportsProtectedContent, Region& clearRegion,
+            ui::Dataspace outputDataspace) override;
     void appendRegionFlashRequests(const Region&,
                                    std::vector<renderengine::LayerSettings>&) override;
     void setExpensiveRenderingExpected(bool enabled) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 8e6f2e2..104e20d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -41,6 +41,9 @@
     MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&());
     MOCK_METHOD1(setRenderEngine, void(std::unique_ptr<renderengine::RenderEngine>));
 
+    MOCK_CONST_METHOD0(getTimeStats, TimeStats&());
+    MOCK_METHOD1(setTimeStats, void(const std::shared_ptr<TimeStats>&));
+
     MOCK_CONST_METHOD0(needsAnotherUpdate, bool());
     MOCK_CONST_METHOD0(getLastFrameRefreshTimestamp, nsecs_t());
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 3eada3c..739490f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -38,6 +38,10 @@
     MOCK_METHOD1(prepareClientComposition,
                  std::optional<renderengine::LayerSettings>(
                          compositionengine::LayerFE::ClientCompositionTargetSettings&));
+    MOCK_METHOD3(prepareShadowClientComposition,
+                 std::optional<renderengine::LayerSettings>(const renderengine::LayerSettings&,
+                                                            const Rect&, ui::Dataspace));
+
     MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
 
     MOCK_CONST_METHOD0(getDebugName, const char*());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 02e68fc..7f5bd06 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -106,8 +106,8 @@
     MOCK_METHOD0(postFramebuffer, void());
     MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
 
-    MOCK_METHOD2(generateClientCompositionRequests,
-                 std::vector<renderengine::LayerSettings>(bool, Region&));
+    MOCK_METHOD3(generateClientCompositionRequests,
+                 std::vector<renderengine::LayerSettings>(bool, Region&, ui::Dataspace));
     MOCK_METHOD2(appendRegionFlashRequests,
                  void(const Region&, std::vector<renderengine::LayerSettings>&));
     MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index be8646c..5eabecd 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -64,6 +64,14 @@
     mRenderEngine = std::move(renderEngine);
 }
 
+TimeStats& CompositionEngine::getTimeStats() const {
+    return *mTimeStats.get();
+}
+
+void CompositionEngine::setTimeStats(const std::shared_ptr<TimeStats>& timeStats) {
+    mTimeStats = timeStats;
+}
+
 bool CompositionEngine::needsAnotherUpdate() const {
     return mNeedsAnotherUpdate;
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 6877f8b..7e5a720 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -799,7 +799,8 @@
     // Generate the client composition requests for the layers on this output.
     std::vector<renderengine::LayerSettings> clientCompositionLayers =
             generateClientCompositionRequests(supportsProtectedContent,
-                                              clientCompositionDisplay.clearRegion);
+                                              clientCompositionDisplay.clearRegion,
+                                              clientCompositionDisplay.outputDataspace);
     appendRegionFlashRequests(debugRegion, clientCompositionLayers);
 
     // If we the display is secure, protected content support is enabled, and at
@@ -838,9 +839,18 @@
         setExpensiveRenderingExpected(true);
     }
 
+    const nsecs_t renderEngineStart = systemTime();
     renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers,
                             buf->getNativeBuffer(), /*useFramebufferCache=*/true, std::move(fd),
                             &readyFence);
+    auto& timeStats = getCompositionEngine().getTimeStats();
+    if (readyFence.get() < 0) {
+        timeStats.recordRenderEngineDuration(renderEngineStart, systemTime());
+    } else {
+        timeStats.recordRenderEngineDuration(renderEngineStart,
+                                             std::make_shared<FenceTime>(
+                                                     new Fence(dup(readyFence.get()))));
+    }
 
     if (expensiveRenderingExpected) {
         setExpensiveRenderingExpected(false);
@@ -850,7 +860,7 @@
 }
 
 std::vector<renderengine::LayerSettings> Output::generateClientCompositionRequests(
-        bool supportsProtectedContent, Region& clearRegion) {
+        bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) {
     std::vector<renderengine::LayerSettings> clientCompositionLayers;
     ALOGV("Rendering client layers");
 
@@ -902,6 +912,13 @@
                     layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
                     layerSettings.alpha = half(0.0);
                     layerSettings.disableBlending = true;
+                } else {
+                    std::optional<renderengine::LayerSettings> shadowLayer =
+                            layerFE.prepareShadowClientComposition(*result, outputState.viewport,
+                                                                   outputDataspace);
+                    if (shadowLayer) {
+                        clientCompositionLayers.push_back(*shadowLayer);
+                    }
                 }
 
                 layer->editState().clientCompositionTimestamp = systemTime();
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 49e7c70..989494d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -25,6 +25,7 @@
 #include <renderengine/mock/RenderEngine.h>
 
 #include "MockHWComposer.h"
+#include "TimeStats/TimeStats.h"
 
 namespace android::compositionengine {
 namespace {
@@ -41,6 +42,8 @@
     android::mock::HWComposer* mHwc = new StrictMock<android::mock::HWComposer>();
     renderengine::mock::RenderEngine* mRenderEngine =
             new StrictMock<renderengine::mock::RenderEngine>();
+    std::shared_ptr<TimeStats> mTimeStats;
+
     impl::CompositionEngine mEngine;
     CompositionRefreshArgs mRefreshArgs;
 
@@ -71,6 +74,12 @@
     EXPECT_EQ(mRenderEngine, &mEngine.getRenderEngine());
 }
 
+TEST_F(CompositionEngineTest, canSetTimeStats) {
+    mEngine.setTimeStats(mTimeStats);
+
+    EXPECT_EQ(mTimeStats.get(), &mEngine.getTimeStats());
+}
+
 /*
  * CompositionEngine::present
  */
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 4d71d43..3a4df74 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -21,8 +21,10 @@
 #include <compositionengine/DisplaySurface.h>
 #include <compositionengine/RenderSurfaceCreationArgs.h>
 #include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/RenderSurface.h>
 #include <compositionengine/mock/CompositionEngine.h>
 #include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/DisplaySurface.h>
 #include <compositionengine/mock/Layer.h>
 #include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/NativeWindow.h>
@@ -39,6 +41,8 @@
 
 using testing::_;
 using testing::DoAll;
+using testing::InSequence;
+using testing::NiceMock;
 using testing::Return;
 using testing::ReturnRef;
 using testing::Sequence;
@@ -46,6 +50,8 @@
 using testing::StrictMock;
 
 constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
 
 struct DisplayTest : public testing::Test {
     class Display : public impl::Display {
@@ -768,5 +774,59 @@
     nonHwcDisplay->finishFrame(refreshArgs);
 }
 
+/*
+ * Display functional tests
+ */
+
+struct DisplayFunctionalTest : public testing::Test {
+    class Display : public impl::Display {
+    public:
+        explicit Display(const compositionengine::DisplayCreationArgs& args)
+              : impl::Display(args) {}
+
+        using impl::Display::injectOutputLayerForTest;
+        virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+    };
+
+    static std::shared_ptr<Display> createDisplay(
+            const compositionengine::CompositionEngine& compositionEngine,
+            compositionengine::DisplayCreationArgs&& args) {
+        return impl::createDisplayTemplated<Display>(compositionEngine, args);
+    }
+
+    DisplayFunctionalTest() {
+        EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+        mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+
+    NiceMock<android::mock::HWComposer> mHwComposer;
+    NiceMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+    NiceMock<mock::CompositionEngine> mCompositionEngine;
+    sp<mock::NativeWindow> mNativeWindow = new NiceMock<mock::NativeWindow>();
+    sp<mock::DisplaySurface> mDisplaySurface = new NiceMock<mock::DisplaySurface>();
+    std::shared_ptr<Display> mDisplay = createDisplay(mCompositionEngine,
+                                                      DisplayCreationArgsBuilder()
+                                                              .setDisplayId(DEFAULT_DISPLAY_ID)
+                                                              .setPowerAdvisor(&mPowerAdvisor)
+                                                              .build());
+    impl::RenderSurface* mRenderSurface =
+            new impl::RenderSurface{mCompositionEngine, *mDisplay,
+                                    RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
+                                                              DEFAULT_DISPLAY_HEIGHT, mNativeWindow,
+                                                              mDisplaySurface}};
+};
+
+TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) {
+    InSequence seq;
+
+    mDisplay->editState().isEnabled = true;
+
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_));
+    EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
+
+    mDisplay->postFramebuffer();
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index a9a735a..37b62d8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -753,8 +753,8 @@
 
 struct OutputPrepareFrameTest : public testing::Test {
     struct OutputPartialMock : public OutputPartialMockBase {
-        // Sets up the helper functions called by prepareFrame to use a mock
-        // implementations.
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
         MOCK_METHOD0(chooseCompositionStrategy, void());
     };
 
@@ -803,14 +803,647 @@
 }
 
 /*
+ * Output::prepare()
+ */
+
+struct OutputPrepareTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD2(rebuildLayerStacks,
+                     void(const compositionengine::CompositionRefreshArgs&,
+                          compositionengine::LayerFESet&));
+    };
+
+    StrictMock<OutputPartialMock> mOutput;
+    CompositionRefreshArgs mRefreshArgs;
+    LayerFESet mGeomSnapshots;
+};
+
+TEST_F(OutputPrepareTest, justInvokesRebuildLayerStacks) {
+    InSequence seq;
+    EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+
+    mOutput.prepare(mRefreshArgs, mGeomSnapshots);
+}
+
+/*
+ * Output::rebuildLayerStacks()
+ */
+
+struct OutputRebuildLayerStacksTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD2(collectVisibleLayers,
+                     void(const compositionengine::CompositionRefreshArgs&,
+                          compositionengine::Output::CoverageState&));
+    };
+
+    OutputRebuildLayerStacksTest() {
+        mOutput.mState.isEnabled = true;
+        mOutput.mState.transform = kIdentityTransform;
+        mOutput.mState.bounds = kOutputBounds;
+
+        mRefreshArgs.updatingOutputGeometryThisFrame = true;
+
+        mCoverageAboveCoveredLayersToSet = Region(Rect(0, 0, 10, 10));
+
+        EXPECT_CALL(mOutput, collectVisibleLayers(Ref(mRefreshArgs), _))
+                .WillRepeatedly(Invoke(this, &OutputRebuildLayerStacksTest::setTestCoverageValues));
+    }
+
+    void setTestCoverageValues(const CompositionRefreshArgs&,
+                               compositionengine::Output::CoverageState& state) {
+        state.aboveCoveredLayers = mCoverageAboveCoveredLayersToSet;
+        state.aboveOpaqueLayers = mCoverageAboveOpaqueLayersToSet;
+        state.dirtyRegion = mCoverageDirtyRegionToSet;
+    }
+
+    static const ui::Transform kIdentityTransform;
+    static const ui::Transform kRotate90Transform;
+    static const Rect kOutputBounds;
+
+    StrictMock<OutputPartialMock> mOutput;
+    CompositionRefreshArgs mRefreshArgs;
+    LayerFESet mGeomSnapshots;
+    Region mCoverageAboveCoveredLayersToSet;
+    Region mCoverageAboveOpaqueLayersToSet;
+    Region mCoverageDirtyRegionToSet;
+};
+
+const ui::Transform OutputRebuildLayerStacksTest::kIdentityTransform{TR_IDENT, 1920, 1080};
+const ui::Transform OutputRebuildLayerStacksTest::kRotate90Transform{TR_ROT_90, 1920, 1080};
+const Rect OutputRebuildLayerStacksTest::kOutputBounds{0, 0, 1920, 1080};
+
+TEST_F(OutputRebuildLayerStacksTest, doesNothingIfNotEnabled) {
+    mOutput.mState.isEnabled = false;
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputRebuildLayerStacksTest, doesNothingIfNotUpdatingGeometryThisFrame) {
+    mRefreshArgs.updatingOutputGeometryThisFrame = false;
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWithNoRotationAndFullCoverage) {
+    mOutput.mState.transform = kIdentityTransform;
+
+    mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1920, 1080));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 0, 0))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWithNoRotationAndPartialCoverage) {
+    mOutput.mState.transform = kIdentityTransform;
+
+    mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 960, 1080));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(960, 0, 1920, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWith90RotationAndFullCoverage) {
+    mOutput.mState.transform = kRotate90Transform;
+
+    mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1080, 1920));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 0, 0))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWith90RotationAndPartialCoverage) {
+    mOutput.mState.transform = kRotate90Transform;
+
+    mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1080, 960));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 960, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, addsToDirtyRegionWithNoRotation) {
+    mOutput.mState.transform = kIdentityTransform;
+    mOutput.mState.dirtyRegion = Region(Rect(960, 0, 1920, 1080));
+
+    mCoverageDirtyRegionToSet = Region(Rect(0, 0, 960, 1080));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.dirtyRegion, RegionEq(Region(Rect(0, 0, 1920, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, addsToDirtyRegionWith90Rotation) {
+    mOutput.mState.transform = kRotate90Transform;
+    mOutput.mState.dirtyRegion = Region(Rect(0, 960, 1080, 1920));
+
+    mCoverageDirtyRegionToSet = Region(Rect(0, 0, 1080, 960));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.dirtyRegion, RegionEq(Region(Rect(0, 0, 1080, 1920))));
+}
+
+/*
+ * Output::collectVisibleLayers()
+ */
+
+struct OutputCollectVisibleLayersTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD2(ensureOutputLayerIfVisible,
+                     void(std::shared_ptr<compositionengine::Layer>,
+                          compositionengine::Output::CoverageState&));
+        MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD0(finalizePendingOutputLayers, void());
+    };
+
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+            EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
+        }
+
+        StrictMock<mock::OutputLayer> outputLayer;
+        std::shared_ptr<StrictMock<mock::Layer>> layer{new StrictMock<mock::Layer>()};
+        impl::OutputLayerCompositionState outputLayerState;
+    };
+
+    OutputCollectVisibleLayersTest() {
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+                .WillRepeatedly(Return(&mLayer1.outputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+                .WillRepeatedly(Return(&mLayer2.outputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2))
+                .WillRepeatedly(Return(&mLayer3.outputLayer));
+
+        mRefreshArgs.layers.push_back(mLayer1.layer);
+        mRefreshArgs.layers.push_back(mLayer2.layer);
+        mRefreshArgs.layers.push_back(mLayer3.layer);
+    }
+
+    StrictMock<OutputPartialMock> mOutput;
+    CompositionRefreshArgs mRefreshArgs;
+    LayerFESet mGeomSnapshots;
+    Output::CoverageState mCoverageState{mGeomSnapshots};
+    Layer mLayer1;
+    Layer mLayer2;
+    Layer mLayer3;
+};
+
+TEST_F(OutputCollectVisibleLayersTest, doesMinimalWorkIfNoLayers) {
+    mRefreshArgs.layers.clear();
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0));
+
+    EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs)));
+    EXPECT_CALL(mOutput, finalizePendingOutputLayers());
+
+    mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState);
+}
+
+TEST_F(OutputCollectVisibleLayersTest, processesCandidateLayersReversedAndSetsOutputLayerZ) {
+    // Enforce a call order sequence for this test.
+    InSequence seq;
+
+    // Layer coverage is evaluated from front to back!
+    EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer3.layer), Ref(mCoverageState)));
+    EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer2.layer), Ref(mCoverageState)));
+    EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer1.layer), Ref(mCoverageState)));
+
+    EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs)));
+    EXPECT_CALL(mOutput, finalizePendingOutputLayers());
+
+    mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState);
+
+    // Ensure all output layers have been assigned a simple/flattened z-order.
+    EXPECT_EQ(0u, mLayer1.outputLayerState.z);
+    EXPECT_EQ(1u, mLayer2.outputLayerState.z);
+    EXPECT_EQ(2u, mLayer3.outputLayerState.z);
+}
+
+/*
+ * Output::ensureOutputLayerIfVisible()
+ */
+
+struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_CONST_METHOD1(belongsInOutput, bool(const compositionengine::Layer*));
+        MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t));
+        MOCK_METHOD3(ensureOutputLayer,
+                     compositionengine::OutputLayer*(
+                             std::optional<size_t>,
+                             const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+    };
+
+    OutputEnsureOutputLayerIfVisibleTest() {
+        EXPECT_CALL(*mLayer, getLayerFE()).WillRepeatedly(Return(mLayerFE));
+        EXPECT_CALL(*mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+        EXPECT_CALL(*mLayer, editFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+
+        EXPECT_CALL(mOutput, belongsInOutput(mLayer.get())).WillRepeatedly(Return(true));
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+                .WillRepeatedly(Return(&mOutputLayer));
+
+        EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+        EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+        EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(*mLayer.get()));
+
+        mOutput.mState.bounds = Rect(0, 0, 200, 300);
+        mOutput.mState.viewport = Rect(0, 0, 200, 300);
+        mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
+
+        mLayerFEState.isVisible = true;
+        mLayerFEState.isOpaque = true;
+        mLayerFEState.contentDirty = true;
+        mLayerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
+        mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+        mLayerFEState.transparentRegionHint = Region(Rect(0, 0, 100, 100));
+
+        mOutputLayerState.visibleRegion = Region(Rect(0, 0, 50, 200));
+        mOutputLayerState.coveredRegion = Region(Rect(50, 0, 100, 200));
+
+        mGeomSnapshots.insert(mLayerFE);
+    }
+
+    static const Region kEmptyRegion;
+    static const Region kFullBoundsNoRotation;
+    static const Region kRightHalfBoundsNoRotation;
+    static const Region kLowerHalfBoundsNoRotation;
+    static const Region kFullBounds90Rotation;
+
+    StrictMock<OutputPartialMock> mOutput;
+    LayerFESet mGeomSnapshots;
+    Output::CoverageState mCoverageState{mGeomSnapshots};
+
+    std::shared_ptr<mock::Layer> mLayer{new StrictMock<mock::Layer>()};
+    sp<StrictMock<mock::LayerFE>> mLayerFE{new StrictMock<mock::LayerFE>()};
+    LayerFECompositionState mLayerFEState;
+    mock::OutputLayer mOutputLayer;
+    impl::OutputLayerCompositionState mOutputLayerState;
+};
+
+const Region OutputEnsureOutputLayerIfVisibleTest::kEmptyRegion = Region(Rect(0, 0, 0, 0));
+const Region OutputEnsureOutputLayerIfVisibleTest::kFullBoundsNoRotation =
+        Region(Rect(0, 0, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kRightHalfBoundsNoRotation =
+        Region(Rect(0, 100, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kLowerHalfBoundsNoRotation =
+        Region(Rect(50, 0, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation =
+        Region(Rect(0, 0, 200, 100));
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, doesNothingIfNoLayerFE) {
+    EXPECT_CALL(*mLayer, getLayerFE).WillOnce(Return(sp<LayerFE>()));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerBelongs) {
+    EXPECT_CALL(mOutput, belongsInOutput(mLayer.get())).WillOnce(Return(false));
+    EXPECT_CALL(*mLayerFE.get(),
+                latchCompositionState(Ref(mLayerFEState),
+                                      compositionengine::LayerFE::StateSubset::BasicGeometry));
+
+    mGeomSnapshots.clear();
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerBelongs) {
+    EXPECT_CALL(mOutput, belongsInOutput(mLayer.get())).WillOnce(Return(false));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerNotVisible) {
+    mLayerFEState.isVisible = false;
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerHasEmptyVisibleRegion) {
+    mLayerFEState.geomLayerBounds = FloatRect{0, 0, 0, 0};
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
+    mOutput.mState.bounds = Rect(0, 0, 0, 0);
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayer) {
+    mLayerFEState.isOpaque = true;
+    mLayerFEState.contentDirty = true;
+    mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayer) {
+    mLayerFEState.isOpaque = true;
+    mLayerFEState.contentDirty = true;
+    mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForTransparentDirtyNotRotatedLayer) {
+    mLayerFEState.isOpaque = false;
+    mLayerFEState.contentDirty = true;
+    mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+    EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+                RegionEq(kRightHalfBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForTransparentDirtyNotRotatedLayer) {
+    mLayerFEState.isOpaque = false;
+    mLayerFEState.contentDirty = true;
+    mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+    EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+                RegionEq(kRightHalfBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) {
+    mLayerFEState.isOpaque = true;
+    mLayerFEState.contentDirty = false;
+    mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) {
+    mLayerFEState.isOpaque = true;
+    mLayerFEState.contentDirty = false;
+    mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kLowerHalfBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueDirtyRotated90Layer) {
+    mLayerFEState.isOpaque = true;
+    mLayerFEState.contentDirty = true;
+    mLayerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
+    mLayerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
+    mOutputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
+    mOutputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForOpaqueDirtyRotated90Layer) {
+    mLayerFEState.isOpaque = true;
+    mLayerFEState.contentDirty = true;
+    mLayerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
+    mLayerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
+    mOutputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
+    mOutputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) {
+    mLayerFEState.isOpaque = true;
+    mLayerFEState.contentDirty = true;
+    mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    mOutput.mState.viewport = Rect(0, 0, 300, 200);
+    mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) {
+    mLayerFEState.isOpaque = true;
+    mLayerFEState.contentDirty = true;
+    mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    mOutput.mState.viewport = Rect(0, 0, 300, 200);
+    mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueDirtyArbitraryTransformLayer) {
+    ui::Transform arbitraryTransform;
+    arbitraryTransform.set(1, 1, -1, 1);
+    arbitraryTransform.set(0, 100);
+
+    mLayerFEState.isOpaque = true;
+    mLayerFEState.contentDirty = true;
+    mLayerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
+    mLayerFEState.geomLayerTransform = arbitraryTransform;
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    const Region kRegion = Region(Rect(0, 0, 300, 300));
+    const Region kRegionClipped = Region(Rect(0, 0, 200, 300));
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kRegion));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kRegion));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+    EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kRegion));
+    EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kRegion));
+    EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kRegionClipped));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesTest) {
+    mLayerFEState.isOpaque = false;
+    mLayerFEState.contentDirty = true;
+    mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+    mCoverageState.aboveCoveredLayers = Region(Rect(50, 0, 150, 200));
+    mCoverageState.aboveOpaqueLayers = Region(Rect(50, 0, 150, 200));
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+            .WillOnce(Return(&mOutputLayer));
+
+    mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+    const Region kExpectedDirtyRegion = Region(Rect(0, 0, 500, 500));
+    const Region kExpectedAboveCoveredRegion = Region(Rect(0, 0, 150, 200));
+    const Region kExpectedAboveOpaqueRegion = Region(Rect(50, 0, 150, 200));
+    const Region kExpectedLayerVisibleRegion = Region(Rect(0, 0, 50, 200));
+    const Region kExpectedLayerCoveredRegion = Region(Rect(50, 0, 100, 200));
+    const Region kExpectedLayerVisibleNonTransparentRegion = Region(Rect(0, 100, 50, 200));
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kExpectedDirtyRegion));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kExpectedAboveCoveredRegion));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kExpectedAboveOpaqueRegion));
+
+    EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+    EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+                RegionEq(kExpectedLayerVisibleNonTransparentRegion));
+    EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion));
+    EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+}
+
+/*
  * Output::present()
  */
 
 struct OutputPresentTest : public testing::Test {
     struct OutputPartialMock : public OutputPartialMockBase {
-        // All child helper functions Output::present() are defined as mocks,
-        // and those are tested separately, allowing the present() test to
-        // just cover the high level flow.
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
         MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
         MOCK_METHOD1(updateAndWriteCompositionState,
                      void(const compositionengine::CompositionRefreshArgs&));
@@ -849,9 +1482,8 @@
     using TestType = OutputUpdateColorProfileTest;
 
     struct OutputPartialMock : public OutputPartialMockBase {
-        // All child helper functions Output::present() are defined as mocks,
-        // and those are tested separately, allowing the present() test to
-        // just cover the high level flow.
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
         MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
     };
 
@@ -1580,8 +2212,8 @@
     using TestType = OutputBeginFrameTest;
 
     struct OutputPartialMock : public OutputPartialMockBase {
-        // Sets up the helper functions called by begiNFrame to use a mock
-        // implementations.
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
         MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
     };
 
@@ -1733,8 +2365,8 @@
 
 struct OutputDevOptRepaintFlashTest : public testing::Test {
     struct OutputPartialMock : public OutputPartialMockBase {
-        // Sets up the helper functions called by composeSurfaces to use a mock
-        // implementations.
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
         MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
         MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
         MOCK_METHOD0(postFramebuffer, void());
@@ -1815,8 +2447,8 @@
 
 struct OutputFinishFrameTest : public testing::Test {
     struct OutputPartialMock : public OutputPartialMockBase {
-        // Sets up the helper functions called by composeSurfaces to use a mock
-        // implementations.
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
         MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
         MOCK_METHOD0(postFramebuffer, void());
     };
@@ -1865,8 +2497,8 @@
 
 struct OutputPostFramebufferTest : public testing::Test {
     struct OutputPartialMock : public OutputPartialMockBase {
-        // Sets up the helper functions called by composeSurfaces to use a mock
-        // implementations.
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
         MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
     };
 
@@ -1940,7 +2572,7 @@
     sp<Fence> layer2Fence = new Fence();
     sp<Fence> layer3Fence = new Fence();
 
-    compositionengine::Output::FrameFences frameFences;
+    Output::FrameFences frameFences;
     frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
     frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
     frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
@@ -1971,7 +2603,7 @@
     sp<Fence> layer1Fence = new Fence();
     sp<Fence> layer2Fence = new Fence();
     sp<Fence> layer3Fence = new Fence();
-    compositionengine::Output::FrameFences frameFences;
+    Output::FrameFences frameFences;
     frameFences.clientTargetAcquireFence = clientTargetAcquireFence;
     frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
     frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
@@ -2010,7 +2642,7 @@
 
     // Set up a fake present fence
     sp<Fence> presentFence = new Fence();
-    compositionengine::Output::FrameFences frameFences;
+    Output::FrameFences frameFences;
     frameFences.presentFence = presentFence;
 
     EXPECT_CALL(*mRenderSurface, flip());
@@ -2045,11 +2677,11 @@
     static const mat4 kDefaultColorTransformMat;
 
     struct OutputPartialMock : public OutputPartialMockBase {
-        // Sets up the helper functions called by composeSurfaces to use a mock
-        // implementations.
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
         MOCK_CONST_METHOD0(getSkipColorTransform, bool());
-        MOCK_METHOD2(generateClientCompositionRequests,
-                     std::vector<renderengine::LayerSettings>(bool, Region&));
+        MOCK_METHOD3(generateClientCompositionRequests,
+                     std::vector<renderengine::LayerSettings>(bool, Region&, ui::Dataspace));
         MOCK_METHOD2(appendRegionFlashRequests,
                      void(const Region&, std::vector<renderengine::LayerSettings>&));
         MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
@@ -2079,10 +2711,14 @@
                 .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()));
     }
 
     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;
@@ -2127,7 +2763,7 @@
     EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer));
 
     EXPECT_CALL(mOutput, getSkipColorTransform()).WillOnce(Return(false));
-    EXPECT_CALL(mOutput, generateClientCompositionRequests(false, _)).Times(1);
+    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);
@@ -2144,9 +2780,10 @@
     struct OutputPartialMock : public OutputPartialMockBase {
         // compositionengine::Output overrides
         std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
-                bool supportsProtectedContent, Region& clearRegion) override {
+                bool supportsProtectedContent, Region& clearRegion,
+                ui::Dataspace dataspace) override {
             return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
-                                                                   clearRegion);
+                                                                   clearRegion, dataspace);
         }
     };
 
@@ -2205,6 +2842,8 @@
     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));
@@ -2214,6 +2853,8 @@
     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));
@@ -2237,8 +2878,8 @@
 
     constexpr bool supportsProtectedContent = false;
     Region clearRegion;
-    auto requests =
-            mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+    auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
+                                                              mOutput.getState().targetDataspace);
 
     ASSERT_EQ(2u, requests.size());
     EXPECT_EQ(leftLayerColor, requests[0].source.solidColor);
@@ -2287,8 +2928,8 @@
 
     constexpr bool supportsProtectedContent = false;
     Region clearRegion;
-    auto requests =
-            mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+    auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
+                                                              mOutput.getState().targetDataspace);
 
     EXPECT_EQ(0u, requests.size());
 }
@@ -2365,8 +3006,8 @@
 
     constexpr bool supportsProtectedContent = false;
     Region clearRegion;
-    auto requests =
-            mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+    auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
+                                                              mOutput.getState().targetDataspace);
 
     const half3 clearColor{0.f, 0.f, 0.f};
 
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 2d0875b..79a1185 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -247,6 +247,7 @@
                       uint32_t reqHeight, ui::Dataspace reqDataSpace,
                       ui::Transform::orientation_flags rotation, bool allowSecureLayers = true)
           : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
+                       device->getViewport(),
                        getDisplayRotation(rotation, device->getInstallOrientation())),
             mDevice(device),
             mSourceCrop(sourceCrop),
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
index 6f91843..4418116 100644
--- a/services/surfaceflinger/FrameTracer/FrameTracer.cpp
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
@@ -21,6 +21,7 @@
 #include "FrameTracer.h"
 
 #include <android-base/stringprintf.h>
+#include <perfetto/trace/clock_snapshot.pbzero.h>
 
 #include <algorithm>
 #include <mutex>
@@ -29,6 +30,7 @@
 
 namespace android {
 
+using Clock = perfetto::protos::pbzero::ClockSnapshot::Clock;
 void FrameTracer::initialize() {
     std::call_once(mInitializationFlag, [this]() {
         perfetto::TracingInitArgs args;
@@ -130,6 +132,7 @@
                               uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
                               FrameEvent::BufferEventType type, nsecs_t duration) {
     auto packet = ctx.NewTracePacket();
+    packet->set_timestamp_clock_id(Clock::MONOTONIC);
     packet->set_timestamp(timestamp);
     auto* event = packet->set_graphics_frame_event()->set_buffer_event();
     event->set_buffer_id(static_cast<uint32_t>(bufferID));
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index ce9aab5..35fc4be 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -571,6 +571,53 @@
     return layerSettings;
 }
 
+std::optional<renderengine::LayerSettings> Layer::prepareShadowClientComposition(
+        const renderengine::LayerSettings& casterLayerSettings, const Rect& displayViewport,
+        ui::Dataspace outputDataspace) {
+    renderengine::ShadowSettings shadow = getShadowSettings(displayViewport);
+    if (shadow.length <= 0.f) {
+        return {};
+    }
+
+    const float casterAlpha = casterLayerSettings.alpha;
+    const bool casterIsOpaque = ((casterLayerSettings.source.buffer.buffer != nullptr) &&
+                                 casterLayerSettings.source.buffer.isOpaque);
+
+    renderengine::LayerSettings shadowLayer = casterLayerSettings;
+    shadowLayer.shadow = shadow;
+
+    // If the casting layer is translucent, we need to fill in the shadow underneath the layer.
+    // Otherwise the generated shadow will only be shown around the casting layer.
+    shadowLayer.shadow.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
+    shadowLayer.shadow.ambientColor *= casterAlpha;
+    shadowLayer.shadow.spotColor *= casterAlpha;
+    shadowLayer.sourceDataspace = outputDataspace;
+    shadowLayer.source.buffer.buffer = nullptr;
+
+    if (shadowLayer.shadow.ambientColor.a <= 0.f && shadowLayer.shadow.spotColor.a <= 0.f) {
+        return {};
+    }
+
+    float casterCornerRadius = shadowLayer.geometry.roundedCornersRadius;
+    const FloatRect& cornerRadiusCropRect = casterLayerSettings.geometry.roundedCornersCrop;
+    const FloatRect& casterRect = shadowLayer.geometry.boundaries;
+
+    // crop used to set the corner radius may be larger than the content rect. Adjust the corner
+    // radius accordingly.
+    if (casterCornerRadius > 0.f) {
+        float cropRectOffset = std::max(std::abs(cornerRadiusCropRect.top - casterRect.top),
+                                        std::abs(cornerRadiusCropRect.left - casterRect.left));
+        if (cropRectOffset > casterCornerRadius) {
+            casterCornerRadius = 0;
+        } else {
+            casterCornerRadius -= cropRectOffset;
+        }
+        shadowLayer.geometry.roundedCornersRadius = casterCornerRadius;
+    }
+
+    return shadowLayer;
+}
+
 Hwc2::IComposerClient::Composition Layer::getCompositionType(
         const sp<const DisplayDevice>& display) const {
     const auto outputLayer = findOutputLayerForDisplay(display);
@@ -1800,6 +1847,18 @@
             : RoundedCornerState();
 }
 
+renderengine::ShadowSettings Layer::getShadowSettings(const Rect& viewport) const {
+    renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings;
+
+    // Shift the spot light x-position to the middle of the display and then
+    // offset it by casting layer's screen pos.
+    state.lightPos.x = (viewport.width() / 2.f) - mScreenBounds.left;
+    state.lightPos.y -= mScreenBounds.top;
+
+    state.length = mEffectiveShadowRadius;
+    return state;
+}
+
 void Layer::commitChildList() {
     for (size_t i = 0; i < mCurrentChildren.size(); i++) {
         const auto& child = mCurrentChildren[i];
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 7abcd0f..843d3ae 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -216,6 +216,9 @@
         std::deque<sp<CallbackHandle>> callbackHandles;
         bool colorSpaceAgnostic;
         nsecs_t desiredPresentTime = -1;
+
+        // Length of the cast shadow. If the radius is > 0, a shadow of length shadowRadius will
+        // be rendered around the layer.
         float shadowRadius;
     };
 
@@ -507,6 +510,9 @@
     void latchCursorCompositionState(compositionengine::LayerFECompositionState&) const override;
     std::optional<renderengine::LayerSettings> prepareClientComposition(
             compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
+    std::optional<renderengine::LayerSettings> prepareShadowClientComposition(
+            const renderengine::LayerSettings& layerSettings, const Rect& displayViewport,
+            ui::Dataspace outputDataspace) override;
     void onLayerDisplayed(const sp<Fence>& releaseFence) override;
     const char* getDebugName() const override;
 
@@ -650,6 +656,8 @@
     // ignored.
     RoundedCornerState getRoundedCornerState() const;
 
+    renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const;
+
     void traverseInReverseZOrder(LayerVector::StateSet stateSet,
                                  const LayerVector::Visitor& visitor);
     void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
@@ -662,6 +670,15 @@
                                   const LayerVector::Visitor& visitor);
 
     size_t getChildrenCount() const;
+
+    // ONLY CALL THIS FROM THE LAYER DTOR!
+    // See b/141111965.  We need to add current children to offscreen layers in
+    // the layer dtor so as not to dangle layers.  Since the layer has not
+    // committed its transaction when the layer is destroyed, we must add
+    // current children.  This is safe in the dtor as we will no longer update
+    // the current state, but should not be called anywhere else!
+    LayerVector& getCurrentChildren() { return mCurrentChildren; }
+
     void addChild(const sp<Layer>& layer);
     // Returns index if removed, or negative value otherwise
     // for symmetry with Vector::remove
@@ -699,6 +716,14 @@
 
     Region debugGetVisibleRegionOnDefaultDisplay() const;
 
+    /**
+     * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
+     * INVALID_RECT if the layer has no buffer and no crop.
+     * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
+     * bounds are constrained by its parent bounds.
+     */
+    Rect getCroppedBufferSize(const Layer::State& s) const;
+
 protected:
     // constant
     sp<SurfaceFlinger> mFlinger;
@@ -907,13 +932,6 @@
                                        const LayerVector::Visitor& visitor);
     LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                           const std::vector<Layer*>& layersInTree);
-    /**
-     * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
-     * INVALID_RECT if the layer has no buffer and no crop.
-     * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
-     * bounds are constrained by its parent bounds.
-     */
-    Rect getCroppedBufferSize(const Layer::State& s) const;
 
     // Cached properties computed from drawing state
     // Effective transform taking into account parent transforms and any parent scaling.
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index edc6442..532572f 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -22,13 +22,14 @@
     static float getCaptureFillValue(CaptureFill captureFill);
 
     RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
-               ui::Dataspace reqDataSpace,
+               ui::Dataspace reqDataSpace, const Rect& displayViewport,
                ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
           : mReqWidth(reqWidth),
             mReqHeight(reqHeight),
             mReqDataSpace(reqDataSpace),
             mCaptureFill(captureFill),
-            mRotationFlags(rotation) {}
+            mRotationFlags(rotation),
+            mDisplayViewport(displayViewport) {}
 
     virtual ~RenderArea() = default;
 
@@ -80,12 +81,16 @@
 
     virtual const sp<const DisplayDevice> getDisplayDevice() const = 0;
 
+    // Returns the source display viewport.
+    const Rect& getDisplayViewport() const { return mDisplayViewport; }
+
 private:
     const uint32_t mReqWidth;
     const uint32_t mReqHeight;
     const ui::Dataspace mReqDataSpace;
     const CaptureFill mCaptureFill;
     const ui::Transform::orientation_flags mRotationFlags;
+    const Rect mDisplayViewport;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/TimeKeeper.h b/services/surfaceflinger/Scheduler/TimeKeeper.h
index 699cd50..38f0708 100644
--- a/services/surfaceflinger/Scheduler/TimeKeeper.h
+++ b/services/surfaceflinger/Scheduler/TimeKeeper.h
@@ -21,10 +21,24 @@
 
 namespace android::scheduler {
 
+class Clock {
+public:
+    virtual ~Clock();
+    /*
+     * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time.
+     */
+    virtual nsecs_t now() const = 0;
+
+protected:
+    Clock() = default;
+    Clock(Clock const&) = delete;
+    Clock& operator=(Clock const&) = delete;
+};
+
 /*
  * TimeKeeper is the interface for a single-shot timer primitive.
  */
-class TimeKeeper {
+class TimeKeeper : public Clock {
 public:
     virtual ~TimeKeeper();
 
@@ -39,11 +53,6 @@
      */
     virtual void alarmCancel() = 0;
 
-    /*
-     * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time.
-     */
-    virtual nsecs_t now() const = 0;
-
 protected:
     TimeKeeper(TimeKeeper const&) = delete;
     TimeKeeper& operator=(TimeKeeper const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 7922484..a79fe98 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -47,22 +47,26 @@
     return {mArmedInfo->mActualWakeupTime};
 }
 
-nsecs_t VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
-                                               VSyncTracker& tracker, nsecs_t now) {
+ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
+                                                      VSyncTracker& tracker, nsecs_t now) {
+    auto const nextVsyncTime =
+            tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
+    if (mLastDispatchTime >= nextVsyncTime) { // already dispatched a callback for this vsync
+        return ScheduleResult::CannotSchedule;
+    }
+
+    auto const nextWakeupTime = nextVsyncTime - workDuration;
+    auto result = mArmedInfo ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
     mWorkDuration = workDuration;
     mEarliestVsync = earliestVsync;
-    arm(tracker, now);
-    return mArmedInfo->mActualWakeupTime;
+    mArmedInfo = {nextWakeupTime, nextVsyncTime};
+    return result;
 }
 
 void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
     if (!mArmedInfo) {
         return;
     }
-    arm(tracker, now);
-}
-
-void VSyncDispatchTimerQueueEntry::arm(VSyncTracker& tracker, nsecs_t now) {
     auto const nextVsyncTime =
             tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
     mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
@@ -214,16 +218,13 @@
             return result;
         }
         auto& callback = it->second;
-        result = callback->wakeupTime() ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
-
         auto const now = mTimeKeeper->now();
-        auto const wakeupTime = callback->schedule(workDuration, earliestVsync, mTracker, now);
-
-        if (wakeupTime < now - mTimerSlack || callback->lastExecutedVsyncTarget() > wakeupTime) {
-            return ScheduleResult::CannotSchedule;
+        result = callback->schedule(workDuration, earliestVsync, mTracker, now);
+        if (result == ScheduleResult::CannotSchedule) {
+            return result;
         }
 
-        if (wakeupTime < mIntendedWakeupTime - mTimerSlack) {
+        if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
             rearmTimerSkippingUpdateFor(now, it);
         }
     }
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 0e12e7f..fc78da3 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -44,8 +44,8 @@
     std::optional<nsecs_t> lastExecutedVsyncTarget() const;
 
     // This moves the state from disarmed->armed and will calculate the wakeupTime.
-    nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
-                     nsecs_t now);
+    ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
+                            nsecs_t now);
     // This will update armed entries with the latest vsync information. Entry remains armed.
     void update(VSyncTracker& tracker, nsecs_t now);
 
@@ -67,7 +67,6 @@
     void ensureNotRunning();
 
 private:
-    void arm(VSyncTracker& tracker, nsecs_t now);
     std::string const mName;
     std::function<void(nsecs_t)> const mCallback;
 
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 643c5d2..3894992 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -54,6 +54,11 @@
     return percent < kOutlierTolerancePercent || percent > (kMaxPercent - kOutlierTolerancePercent);
 }
 
+nsecs_t VSyncPredictor::currentPeriod() const {
+    std::lock_guard<std::mutex> lk(mMutex);
+    return std::get<0>(mRateMap.find(mIdealPeriod)->second);
+}
+
 void VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
     std::lock_guard<std::mutex> lk(mMutex);
 
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 1590f49..4210b3c 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -39,6 +39,7 @@
 
     void addVsyncTimestamp(nsecs_t timestamp) final;
     nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
+    nsecs_t currentPeriod() const final;
 
     /*
      * Inform the model that the period is anticipated to change to a new value.
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
new file mode 100644
index 0000000..f2a7791
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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 "VSyncReactor.h"
+#include "TimeKeeper.h"
+#include "VSyncDispatch.h"
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+
+Clock::~Clock() = default;
+
+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)),
+        mPendingLimit(pendingFenceLimit) {}
+
+bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
+    if (!fence) {
+        return false;
+    }
+
+    nsecs_t const signalTime = fence->getCachedSignalTime();
+    if (signalTime == Fence::SIGNAL_TIME_INVALID) {
+        return true;
+    }
+
+    std::lock_guard<std::mutex> lk(mMutex);
+    if (mIgnorePresentFences) {
+        return true;
+    }
+
+    for (auto it = mUnfiredFences.begin(); it != mUnfiredFences.end();) {
+        auto const time = (*it)->getCachedSignalTime();
+        if (time == Fence::SIGNAL_TIME_PENDING) {
+            it++;
+        } else if (time == Fence::SIGNAL_TIME_INVALID) {
+            it = mUnfiredFences.erase(it);
+        } else {
+            mTracker->addVsyncTimestamp(time);
+            it = mUnfiredFences.erase(it);
+        }
+    }
+
+    if (signalTime == Fence::SIGNAL_TIME_PENDING) {
+        if (mPendingLimit == mUnfiredFences.size()) {
+            mUnfiredFences.erase(mUnfiredFences.begin());
+        }
+        mUnfiredFences.push_back(fence);
+    } else {
+        mTracker->addVsyncTimestamp(signalTime);
+    }
+
+    return false; // TODO(b/144707443): add policy for turning on HWVsync.
+}
+
+void VSyncReactor::setIgnorePresentFences(bool ignoration) {
+    std::lock_guard<std::mutex> lk(mMutex);
+    mIgnorePresentFences = ignoration;
+    if (mIgnorePresentFences == true) {
+        mUnfiredFences.clear();
+    }
+}
+
+nsecs_t VSyncReactor::computeNextRefresh(int periodOffset) const {
+    auto const now = mClock->now();
+    auto const currentPeriod = periodOffset ? mTracker->currentPeriod() : 0;
+    return mTracker->nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod);
+}
+
+nsecs_t VSyncReactor::expectedPresentTime() {
+    return mTracker->nextAnticipatedVSyncTimeFrom(mClock->now());
+}
+
+void VSyncReactor::setPeriod(nsecs_t period) {
+    mTracker->setPeriod(period);
+    {
+        std::lock_guard<std::mutex> lk(mMutex);
+        mPeriodChangeInProgress = true;
+    }
+}
+
+nsecs_t VSyncReactor::getPeriod() {
+    return mTracker->currentPeriod();
+}
+
+void VSyncReactor::beginResync() {}
+
+void VSyncReactor::endResync() {}
+
+bool VSyncReactor::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
+    assert(periodFlushed);
+    mTracker->addVsyncTimestamp(timestamp);
+    {
+        std::lock_guard<std::mutex> lk(mMutex);
+        *periodFlushed = mPeriodChangeInProgress;
+        mPeriodChangeInProgress = false;
+    }
+    return false;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
new file mode 100644
index 0000000..786ee98
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <ui/FenceTime.h>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+namespace android::scheduler {
+
+class Clock;
+class VSyncDispatch;
+class VSyncTracker;
+
+// TODO (b/145217110): consider renaming.
+class VSyncReactor /* TODO (b/140201379): : public android::DispSync */ {
+public:
+    VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
+                 std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit);
+
+    bool addPresentFence(const std::shared_ptr<FenceTime>& fence);
+    void setIgnorePresentFences(bool ignoration);
+
+    nsecs_t computeNextRefresh(int periodOffset) const;
+    nsecs_t expectedPresentTime();
+
+    void setPeriod(nsecs_t period);
+    nsecs_t getPeriod();
+
+    // TODO: (b/145626181) remove begin,endResync functions from DispSync i/f when possible.
+    void beginResync();
+    bool addResyncSample(nsecs_t timestamp, bool* periodFlushed);
+    void endResync();
+
+private:
+    std::unique_ptr<Clock> const mClock;
+    std::unique_ptr<VSyncDispatch> const mDispatch;
+    std::unique_ptr<VSyncTracker> const mTracker;
+    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;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 97b9620..6be63fe 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -47,6 +47,20 @@
      */
     virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0;
 
+    /*
+     * The current period of the vsync signal.
+     *
+     * \return  The current period of the vsync signal
+     */
+    virtual nsecs_t currentPeriod() const = 0;
+
+    /*
+     * Inform the tracker that the period is changing and the tracker needs to recalibrate itself.
+     *
+     * \param [in] period   The period that the system is changing into.
+     */
+    virtual void setPeriod(nsecs_t period) = 0;
+
 protected:
     VSyncTracker(VSyncTracker const&) = delete;
     VSyncTracker& operator=(VSyncTracker const&) = delete;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5aa2447..9f4dd2b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -600,6 +600,7 @@
                         ? renderengine::RenderEngine::ContextPriority::HIGH
                         : renderengine::RenderEngine::ContextPriority::MEDIUM)
                 .build()));
+    mCompositionEngine->setTimeStats(mTimeStats);
 
     LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
             "Starting with vr flinger active is not currently supported.");
@@ -3133,7 +3134,13 @@
         listenerCallbacks.insert(listener);
     }
 
-    sp<Layer> layer(fromHandle(s.surface));
+    sp<Layer> layer = nullptr;
+    if (s.surface) {
+        layer = fromHandle(s.surface);
+    } else {
+        // The client may provide us a null handle. Treat it as if the layer was removed.
+        ALOGW("Attempt to set client state with a null layer handle");
+    }
     if (layer == nullptr) {
         for (auto& [listener, callbackIds] : s.listeners) {
             mTransactionCompletedThread.registerUnpresentedCallbackHandle(
@@ -3736,9 +3743,6 @@
     }
 
     if (currentMode == HWC_POWER_MODE_OFF) {
-        // Turn on the display
-        // TODO: @vhau temp fix only!  See b/141111965
-        mTransactionCompletedThread.clearAllPending();
         getHwComposer().setPowerMode(*displayId, mode);
         if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
             setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
@@ -3975,6 +3979,12 @@
                       static_cast<int32_t>(
                               mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps));
     }
+    StringAppendF(&result,
+                  "DesiredDisplayConfigSpecs: default config ID: %" PRIu32
+                  ", min: %.2f Hz, max: %.2f Hz",
+                  mDesiredDisplayConfigSpecs.defaultModeId,
+                  mDesiredDisplayConfigSpecs.minRefreshRate,
+                  mDesiredDisplayConfigSpecs.maxRefreshRate);
     StringAppendF(&result, "(config override by backdoor: %s)\n\n",
                   mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
 
@@ -4404,6 +4414,8 @@
         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 INJECT_VSYNC:
         case SET_POWER_MODE:
@@ -4920,8 +4932,12 @@
     }
     // Couldn't find display by displayId. Try to get display by layerStack since virtual displays
     // may not have a displayId.
+    return getDisplayByLayerStack(displayOrLayerStack);
+}
+
+const sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
     for (const auto& [token, display] : mDisplays) {
-        if (display->getLayerStack() == displayOrLayerStack) {
+        if (display->getLayerStack() == layerStack) {
             return display;
         }
     }
@@ -4977,8 +4993,8 @@
     public:
         LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
                         int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
-                        bool childrenOnly)
-              : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace),
+                        bool childrenOnly, const Rect& displayViewport)
+              : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace, displayViewport),
                 mLayer(layer),
                 mCrop(crop),
                 mNeedsFiltering(false),
@@ -5062,7 +5078,7 @@
     sp<Layer> parent;
     Rect crop(sourceCrop);
     std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
-
+    Rect displayViewport;
     {
         Mutex::Autolock _l(mStateLock);
 
@@ -5079,14 +5095,21 @@
             return PERMISSION_DENIED;
         }
 
+        Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getCurrentState());
         if (sourceCrop.width() <= 0) {
             crop.left = 0;
-            crop.right = parent->getBufferSize(parent->getCurrentState()).getWidth();
+            crop.right = parentSourceBounds.getWidth();
         }
 
         if (sourceCrop.height() <= 0) {
             crop.top = 0;
-            crop.bottom = parent->getBufferSize(parent->getCurrentState()).getHeight();
+            crop.bottom = parentSourceBounds.getHeight();
+        }
+
+        if (crop.isEmpty() || frameScale <= 0.0f) {
+            // Error out if the layer has no source bounds (i.e. they are boundless) and a source
+            // crop was not specified, or an invalid frame scale was provided.
+            return BAD_VALUE;
         }
         reqWidth = crop.width() * frameScale;
         reqHeight = crop.height() * frameScale;
@@ -5100,6 +5123,13 @@
                 return NAME_NOT_FOUND;
             }
         }
+
+        auto display = getDisplayByLayerStack(parent->getLayerStack());
+        if (!display) {
+            return BAD_VALUE;
+        }
+
+        displayViewport = display->getViewport();
     } // mStateLock
 
     // really small crop or frameScale
@@ -5110,7 +5140,8 @@
         reqHeight = 1;
     }
 
-    LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly);
+    LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly,
+                               displayViewport);
     auto traverseLayers = [parent, childrenOnly,
                            &excludeLayers](const LayerVector::Visitor& visitor) {
         parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
@@ -5236,6 +5267,7 @@
     const auto rotation = renderArea.getRotationFlags();
     const auto transform = renderArea.getTransform();
     const auto sourceCrop = renderArea.getSourceCrop();
+    const auto& displayViewport = renderArea.getDisplayViewport();
 
     renderengine::DisplaySettings clientCompositionDisplay;
     std::vector<renderengine::LayerSettings> clientCompositionLayers;
@@ -5320,6 +5352,12 @@
         };
         auto result = layer->prepareClientComposition(targetSettings);
         if (result) {
+            std::optional<renderengine::LayerSettings> shadowLayer =
+                    layer->prepareShadowClientComposition(*result, displayViewport,
+                                                          clientCompositionDisplay.outputDataspace);
+            if (shadowLayer) {
+                clientCompositionLayers.push_back(*shadowLayer);
+            }
             clientCompositionLayers.push_back(*result);
         }
     });
@@ -5517,6 +5555,66 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                      int32_t defaultModeId, float minRefreshRate,
+                                                      float maxRefreshRate) {
+    ATRACE_CALL();
+
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
+
+    postMessageSync(new LambdaMessage([&]() {
+        const auto display = getDisplayDeviceLocked(displayToken);
+        if (!display) {
+            ALOGE("Attempt to set desired display configs for invalid display token %p",
+                  displayToken.get());
+        } else if (display->isVirtual()) {
+            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;
+        }
+    }));
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                      int32_t* outDefaultModeId,
+                                                      float* outMinRefreshRate,
+                                                      float* outMaxRefreshRate) {
+    ATRACE_CALL();
+
+    if (!displayToken || !outDefaultModeId || !outMinRefreshRate || !outMaxRefreshRate) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        return NAME_NOT_FOUND;
+    }
+
+    if (display->isPrimary()) {
+        *outDefaultModeId = mDesiredDisplayConfigSpecs.defaultModeId;
+        *outMinRefreshRate = mDesiredDisplayConfigSpecs.minRefreshRate;
+        *outMaxRefreshRate = mDesiredDisplayConfigSpecs.maxRefreshRate;
+    }
+
+    return NO_ERROR;
+}
+
 void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() {
     mFlinger->setInputWindowsFinished();
 }
@@ -5540,6 +5638,20 @@
 
 void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
     mNumLayers--;
+    removeFromOffscreenLayers(layer);
+}
+
+// WARNING: ONLY CALL THIS FROM LAYER DTOR
+// Here we add children in the current state to offscreen layers and remove the
+// layer itself from the offscreen layer list.  Since
+// this is the dtor, it is safe to access the current state.  This keeps us
+// from dangling children layers such that they are not reachable from the
+// Drawing state nor the offscreen layer list
+// See b/141111965
+void SurfaceFlinger::removeFromOffscreenLayers(Layer* layer) {
+    for (auto& child : layer->getCurrentChildren()) {
+        mOffscreenLayers.emplace(child.get());
+    }
     mOffscreenLayers.erase(layer);
 }
 
@@ -5547,9 +5659,19 @@
     getRenderEngine().unbindExternalTextureBuffer(clientCacheId.id);
 }
 
-status_t SurfaceFlinger::setGlobalShadowSettings(const half4& /*ambientColor*/,
-                                                 const half4& /*spotColor*/, float /*lightPosY*/,
-                                                 float /*lightPosZ*/, float /*lightRadius*/) {
+status_t SurfaceFlinger::setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+                                                 float lightPosY, float lightPosZ,
+                                                 float lightRadius) {
+    Mutex::Autolock _l(mStateLock);
+    mCurrentState.globalShadowSettings.ambientColor = vec4(ambientColor);
+    mCurrentState.globalShadowSettings.spotColor = vec4(spotColor);
+    mCurrentState.globalShadowSettings.lightPos.y = lightPosY;
+    mCurrentState.globalShadowSettings.lightPos.z = lightPosZ;
+    mCurrentState.globalShadowSettings.lightRadius = lightRadius;
+
+    // these values are overridden when calculating the shadow settings for a layer.
+    mCurrentState.globalShadowSettings.lightPos.x = 0.f;
+    mCurrentState.globalShadowSettings.length = 0.f;
     return NO_ERROR;
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 900c5f7..ae3f2d7 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -37,6 +37,7 @@
 #include <input/ISetInputWindowsListener.h>
 #include <layerproto/LayerProtoHeader.h>
 #include <math/mat4.h>
+#include <renderengine/LayerSettings.h>
 #include <serviceutils/PriorityDumper.h>
 #include <system/graphics.h>
 #include <ui/FenceTime.h>
@@ -314,6 +315,8 @@
     void onLayerFirstRef(Layer*);
     void onLayerDestroyed(Layer*);
 
+    void removeFromOffscreenLayers(Layer* layer);
+
     TransactionCompletedThread& getTransactionCompletedThread() {
         return mTransactionCompletedThread;
     }
@@ -366,6 +369,8 @@
             if (colorMatrixChanged) {
                 colorMatrix = other.colorMatrix;
             }
+            globalShadowSettings = other.globalShadowSettings;
+
             return *this;
         }
 
@@ -376,10 +381,23 @@
         bool colorMatrixChanged = true;
         mat4 colorMatrix;
 
+        renderengine::ShadowSettings globalShadowSettings;
+
         void traverseInZOrder(const LayerVector::Visitor& visitor) const;
         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
      */
@@ -467,6 +485,11 @@
                                       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,
+                                          float* outMaxRefreshRate) override;
     status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
                                          bool* outSupport) const override;
     status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) const override;
@@ -669,6 +692,7 @@
                                  const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
                                  bool& outCapturedSecureLayers);
     const sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack);
+    const sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack);
     status_t captureScreenImplLocked(const RenderArea& renderArea,
                                      TraverseLayersFunction traverseLayers,
                                      ANativeWindowBuffer* buffer, bool useIdentityTransform,
@@ -1120,6 +1144,7 @@
     // 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
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 626efb8..1895777 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -129,6 +129,29 @@
     }
 }
 
+void TimeStats::recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) {
+    if (!mEnabled.load()) return;
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mGlobalRecord.renderEngineDurations.size() == MAX_NUM_TIME_RECORDS) {
+        ALOGE("RenderEngineTimes are already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+        mGlobalRecord.renderEngineDurations.pop_front();
+    }
+    mGlobalRecord.renderEngineDurations.push_back({startTime, endTime});
+}
+
+void TimeStats::recordRenderEngineDuration(nsecs_t startTime,
+                                           const std::shared_ptr<FenceTime>& endTime) {
+    if (!mEnabled.load()) return;
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mGlobalRecord.renderEngineDurations.size() == MAX_NUM_TIME_RECORDS) {
+        ALOGE("RenderEngineTimes are already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+        mGlobalRecord.renderEngineDurations.pop_front();
+    }
+    mGlobalRecord.renderEngineDurations.push_back({startTime, endTime});
+}
+
 bool TimeStats::recordReadyLocked(int32_t layerId, TimeRecord* timeRecord) {
     if (!timeRecord->ready) {
         ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerId,
@@ -501,6 +524,31 @@
         mGlobalRecord.prevPresentTime = curPresentTime;
         mGlobalRecord.presentFences.pop_front();
     }
+    while (!mGlobalRecord.renderEngineDurations.empty()) {
+        const auto duration = mGlobalRecord.renderEngineDurations.front();
+        const auto& endTime = duration.endTime;
+
+        nsecs_t endNs = -1;
+
+        if (auto val = std::get_if<nsecs_t>(&endTime)) {
+            endNs = *val;
+        } else {
+            endNs = std::get<std::shared_ptr<FenceTime>>(endTime)->getSignalTime();
+        }
+
+        if (endNs == Fence::SIGNAL_TIME_PENDING) break;
+
+        if (endNs < 0) {
+            ALOGE("RenderEngineTiming is invalid!");
+            mGlobalRecord.renderEngineDurations.pop_front();
+            continue;
+        }
+
+        const int32_t renderEngineMs = msBetween(duration.startTime, endNs);
+        mTimeStats.renderEngineTiming.insert(renderEngineMs);
+
+        mGlobalRecord.renderEngineDurations.pop_front();
+    }
 }
 
 void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 670bc8e..65e5cf4 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -27,6 +27,7 @@
 #include <mutex>
 #include <optional>
 #include <unordered_map>
+#include <variant>
 
 using namespace android::surfaceflinger;
 
@@ -50,6 +51,13 @@
     // The end time corresponds to when SurfaceFlinger finishes submitting the
     // request to HWC to present a frame.
     virtual void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) = 0;
+    // Records the start time and end times for when RenderEngine begins work.
+    // The start time corresponds to the beginning of RenderEngine::drawLayers.
+    // The end time corresponds to when RenderEngine finishes rendering.
+    virtual void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) = 0;
+    // Same as above, but passes in a fence representing the end time.
+    virtual void recordRenderEngineDuration(nsecs_t startTime,
+                                            const std::shared_ptr<FenceTime>& readyFence) = 0;
 
     virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                              nsecs_t postTime) = 0;
@@ -58,6 +66,8 @@
     virtual void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) = 0;
     virtual void setAcquireFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& acquireFence) = 0;
+    // SetPresent{Time, Fence} are not expected to be called in the critical
+    // rendering path, as they flush prior fences if those fences have fired.
     virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0;
     virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& presentFence) = 0;
@@ -107,9 +117,15 @@
         nsecs_t prevTime = 0;
     };
 
+    struct RenderEngineDuration {
+        nsecs_t startTime;
+        std::variant<nsecs_t, std::shared_ptr<FenceTime>> endTime;
+    };
+
     struct GlobalRecord {
         nsecs_t prevPresentTime = 0;
         std::deque<std::shared_ptr<FenceTime>> presentFences;
+        std::deque<RenderEngineDuration> renderEngineDurations;
     };
 
 public:
@@ -124,6 +140,9 @@
     void incrementClientCompositionFrames() override;
 
     void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) override;
+    void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) override;
+    void recordRenderEngineDuration(nsecs_t startTime,
+                                    const std::shared_ptr<FenceTime>& readyFence) override;
 
     void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                      nsecs_t postTime) override;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 83cd45a..7e43880 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -113,6 +113,8 @@
     result.append(presentToPresent.toString());
     StringAppendF(&result, "frameDuration histogram is as below:\n");
     result.append(frameDuration.toString());
+    StringAppendF(&result, "renderEngineTiming histogram is as below:\n");
+    result.append(renderEngineTiming.toString());
     const auto dumpStats = generateDumpStats(maxLayers);
     for (const auto& ele : dumpStats) {
         result.append(ele->toString());
@@ -165,6 +167,11 @@
         histProto->set_time_millis(histEle.first);
         histProto->set_frame_count(histEle.second);
     }
+    for (const auto& histEle : renderEngineTiming.hist) {
+        SFTimeStatsHistogramBucketProto* histProto = globalProto.add_render_engine_timing();
+        histProto->set_time_millis(histEle.first);
+        histProto->set_frame_count(histEle.second);
+    }
     const auto dumpStats = generateDumpStats(maxLayers);
     for (const auto& ele : dumpStats) {
         SFTimeStatsLayerProto* layerProto = globalProto.add_stats();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 6b28970..bd97ecc 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -62,6 +62,7 @@
         int64_t displayOnTime = 0;
         Histogram presentToPresent;
         Histogram frameDuration;
+        Histogram renderEngineTiming;
         std::unordered_map<std::string, TimeStatsLayer> stats;
         std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
 
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
index 96430b3..5fd4a39 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -25,7 +25,7 @@
 // changes to these messages, and keep google3 side proto messages in sync if
 // the end to end pipeline needs to be updated.
 
-// Next tag: 11
+// Next tag: 12
 message SFTimeStatsGlobalProto {
   // The stats start time in UTC as seconds since January 1, 1970
   optional int64 stats_start = 1;
@@ -45,6 +45,8 @@
   repeated SFTimeStatsHistogramBucketProto present_to_present = 8;
   // Frame CPU duration histogram.
   repeated SFTimeStatsHistogramBucketProto frame_duration = 10;
+  // Frame GPU duration histogram.
+  repeated SFTimeStatsHistogramBucketProto render_engine_timing = 11;
   // Stats per layer. Apps could have multiple layers.
   repeated SFTimeStatsLayerProto stats = 6;
 }
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index c15355d..8db03db 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -189,15 +189,6 @@
     return NO_ERROR;
 }
 
-void TransactionCompletedThread::clearAllPending() {
-    std::lock_guard lock(mMutex);
-    if (!mRunning) {
-        return;
-    }
-    mPendingTransactions.clear();
-    mConditionVariable.notify_all();
-}
-
 status_t TransactionCompletedThread::registerUnpresentedCallbackHandle(
         const sp<CallbackHandle>& handle) {
     std::lock_guard lock(mMutex);
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index cd95bfb..12ea8fe 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -70,8 +70,6 @@
     // Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
     status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
 
-    void clearAllPending();
-
     // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
     // presented this frame.
     status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 6b0737c..ca7e18d 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -22,6 +22,7 @@
         "Credentials_test.cpp",
         "DereferenceSurfaceControl_test.cpp",
         "DisplayActiveConfig_test.cpp",
+        "DisplayConfigs_test.cpp",
         "InvalidHandles_test.cpp",
         "LayerCallback_test.cpp",
         "LayerRenderTypeTransaction_test.cpp",
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index b667a74..b1bb7fd 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -274,7 +274,7 @@
         sp<GraphicBuffer> outBuffer;
         return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
                                                ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
-                                               Rect(), FRAME_SCALE, &outBuffer);
+                                               Rect(0, 0, 1, 1), FRAME_SCALE, &outBuffer);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
new file mode 100644
index 0000000..420fb29
--- /dev/null
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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());
+
+/**
+ * Test class for setting display configs and passing around refresh rate ranges.
+ */
+class RefreshRateRangeTest : public ::testing::Test {
+protected:
+    void SetUp() override { mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); }
+
+    sp<IBinder> mDisplayToken;
+    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);
+
+    res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfigId,
+                                                              &minRefreshRate, &maxRefreshRate);
+    EXPECT_EQ(res, NO_ERROR);
+    EXPECT_EQ(defaultConfigId, 1);
+    EXPECT_EQ(minRefreshRate, 45);
+    EXPECT_EQ(maxRefreshRate, 75);
+}
+
+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/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index 73f563d..0ad122b 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -1443,6 +1443,61 @@
     mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
 }
 
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) {
+    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    sp<GraphicBuffer> outBuffer;
+    Rect sourceCrop(0, 0, 10, 10);
+    ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+    ScreenCapture sc(outBuffer);
+
+    sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
+    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+    Rect layerCrop(0, 0, 10, 10);
+    SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    sp<GraphicBuffer> outBuffer;
+    Rect sourceCrop = Rect();
+    ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+    ScreenCapture sc(outBuffer);
+
+    sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) {
+    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    sp<GraphicBuffer> outBuffer;
+    Rect sourceCrop = Rect();
+
+    ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+}
+
+TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    sp<GraphicBuffer> outBuffer;
+    Rect sourceCrop = Rect();
+    ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+
+    TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
+    SurfaceComposerClient::Transaction().apply(true);
+    ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+    ScreenCapture sc(outBuffer);
+    sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
 // In the following tests we verify successful skipping of a parent layer,
 // so we use the same verification logic and only change how we mutate
 // the parent layer to verify that various properties are ignored.
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 0c4a752..ccfa6c5 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -57,6 +57,7 @@
         "VSyncDispatchTimerQueueTest.cpp",
         "VSyncDispatchRealtimeTest.cpp",
         "VSyncPredictorTest.cpp",
+        "VSyncReactorTest.cpp",
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockDisplay.cpp",
         "mock/DisplayHardware/MockPowerAdvisor.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 32f997f..04991f9 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -33,13 +33,13 @@
 #include "BufferQueueLayer.h"
 #include "ColorLayer.h"
 #include "Layer.h"
-
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/MockDispSync.h"
 #include "mock/MockEventControlThread.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockMessageQueue.h"
+#include "mock/MockTimeStats.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
@@ -100,6 +100,7 @@
                 .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
 
         mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+        mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
         setupComposer(0);
     }
 
@@ -181,6 +182,7 @@
 
     Hwc2::mock::Composer* mComposer = nullptr;
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+    mock::TimeStats* mTimeStats = new mock::TimeStats();
     mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
 
     sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c4b8408..b2210ec 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -189,6 +189,10 @@
                 std::make_unique<impl::HWComposer>(std::move(composer)));
     }
 
+    void setupTimeStats(const std::shared_ptr<TimeStats>& timeStats) {
+        mFlinger->mCompositionEngine->setTimeStats(timeStats);
+    }
+
     void setupScheduler(std::unique_ptr<DispSync> primaryDispSync,
                         std::unique_ptr<EventControlThread> eventControlThread,
                         std::unique_ptr<EventThread> appEventThread,
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 069344a..68e4c58 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -303,6 +303,44 @@
     EXPECT_EQ(3, histogramProto.time_millis());
 }
 
+TEST_F(TimeStatsTest, canInsertGlobalRenderEngineTiming) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    using namespace std::chrono_literals;
+
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms)
+                                                   .count(),
+                                           std::make_shared<FenceTime>(
+                                                   std::chrono::duration_cast<
+                                                           std::chrono::nanoseconds>(3ms)
+                                                           .count()));
+
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+                                                   .count(),
+                                           std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                                   .count());
+
+    // First verify that flushing RenderEngine durations did not occur yet.
+    SFTimeStatsGlobalProto preFlushProto;
+    ASSERT_TRUE(preFlushProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+    ASSERT_EQ(0, preFlushProto.render_engine_timing_size());
+
+    // Push a dummy present fence to trigger flushing the RenderEngine timings.
+    mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+
+    // Now we can verify that RenderEngine durations were flushed now.
+    SFTimeStatsGlobalProto postFlushProto;
+    ASSERT_TRUE(postFlushProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, postFlushProto.render_engine_timing_size());
+    const SFTimeStatsHistogramBucketProto& histogramProto =
+            postFlushProto.render_engine_timing().Get(0);
+    EXPECT_EQ(2, histogramProto.frame_count());
+    EXPECT_EQ(2, histogramProto.time_millis());
+}
+
 TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index c012616..484947d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -47,6 +47,10 @@
         return timePoint - floor + mPeriod;
     }
 
+    nsecs_t currentPeriod() const final { return mPeriod; }
+
+    void setPeriod(nsecs_t) final {}
+
 private:
     nsecs_t const mPeriod;
 };
@@ -73,6 +77,13 @@
         mBase = last_known;
     }
 
+    nsecs_t currentPeriod() const final {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        return mPeriod;
+    }
+
+    void setPeriod(nsecs_t) final {}
+
 private:
     std::mutex mutable mMutex;
     nsecs_t mPeriod;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 82950b5..d668a41 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -39,6 +39,8 @@
 
     MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
     MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+    MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+    MOCK_METHOD1(setPeriod, void(nsecs_t));
 
     nsecs_t nextVSyncTime(nsecs_t timePoint) const {
         if (timePoint % mPeriod == 0) {
@@ -551,6 +553,32 @@
     EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::ReScheduled);
 }
 
+TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
+    CountingCallback cb0(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+    advanceToNextCallback();
+    EXPECT_EQ(mDispatch.schedule(cb0, 1100, 2000), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    CountingCallback cb0(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+    advanceToNextCallback();
+    EXPECT_EQ(mDispatch.schedule(cb0, 1900, 2000), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, cannotScheduleDoesNotAffectSchedulingState) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 600));
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+    advanceToNextCallback();
+    EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::CannotSchedule);
+}
+
 TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
     EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
     EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
@@ -599,11 +627,10 @@
     VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
 
     EXPECT_FALSE(entry.wakeupTime());
-    auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
-    auto const queried = entry.wakeupTime();
-    ASSERT_TRUE(queried);
-    EXPECT_THAT(*queried, Eq(wakeup));
-    EXPECT_THAT(*queried, Eq(900));
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    auto const wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(900));
 
     entry.disarm();
     EXPECT_FALSE(entry.wakeupTime());
@@ -619,11 +646,10 @@
     VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
 
     EXPECT_FALSE(entry.wakeupTime());
-    auto const wakeup = entry.schedule(500, 994, mStubTracker, now);
-    auto const queried = entry.wakeupTime();
-    ASSERT_TRUE(queried);
-    EXPECT_THAT(*queried, Eq(wakeup));
-    EXPECT_THAT(*queried, Eq(9500));
+    EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled));
+    auto const wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(9500));
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) {
@@ -634,8 +660,10 @@
         calledTime = time;
     });
 
-    auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
-    EXPECT_THAT(wakeup, Eq(900));
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    auto const wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(900));
 
     entry.callback(entry.executing());
 
@@ -659,23 +687,40 @@
     entry.update(mStubTracker, 0);
     EXPECT_FALSE(entry.wakeupTime());
 
-    auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    auto wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
     EXPECT_THAT(wakeup, Eq(900));
 
     entry.update(mStubTracker, 0);
-    auto const queried = entry.wakeupTime();
-    ASSERT_TRUE(queried);
-    EXPECT_THAT(*queried, Eq(920));
+    wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(920));
 }
 
 TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
     VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
-    auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
     entry.update(mStubTracker, 0);
 
-    auto const queried = entry.wakeupTime();
-    ASSERT_TRUE(queried);
-    EXPECT_THAT(*queried, Eq(wakeup));
+    auto const wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(wakeup));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, reportsCannotScheduleIfMissedOpportunity) {
+    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+    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));
+    EXPECT_THAT(entry.schedule(200, 1001, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, reportsReScheduleIfStillTime) {
+    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::ReScheduled));
 }
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
new file mode 100644
index 0000000..84df019
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/TimeKeeper.h"
+#include "Scheduler/VSyncDispatch.h"
+#include "Scheduler/VSyncReactor.h"
+#include "Scheduler/VSyncTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <ui/Fence.h>
+#include <ui/FenceTime.h>
+#include <array>
+
+using namespace testing;
+using namespace std::literals;
+namespace android::scheduler {
+
+class MockVSyncTracker : public VSyncTracker {
+public:
+    MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+    MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+    MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+    MOCK_METHOD1(setPeriod, void(nsecs_t));
+};
+
+class VSyncTrackerWrapper : public VSyncTracker {
+public:
+    VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
+
+    void addVsyncTimestamp(nsecs_t timestamp) final { mTracker->addVsyncTimestamp(timestamp); }
+    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
+        return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
+    }
+    nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
+    void setPeriod(nsecs_t period) { mTracker->setPeriod(period); }
+
+private:
+    std::shared_ptr<VSyncTracker> const mTracker;
+};
+
+class MockClock : public Clock {
+public:
+    MOCK_CONST_METHOD0(now, nsecs_t());
+};
+
+class ClockWrapper : public Clock {
+public:
+    ClockWrapper(std::shared_ptr<Clock> const& clock) : mClock(clock) {}
+
+    nsecs_t now() const { return mClock->now(); }
+
+private:
+    std::shared_ptr<Clock> const mClock;
+};
+
+class MockVSyncDispatch : public VSyncDispatch {
+public:
+    MOCK_METHOD2(registerCallback, CallbackToken(std::function<void(nsecs_t)> const&, std::string));
+    MOCK_METHOD1(unregisterCallback, void(CallbackToken));
+    MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
+    MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
+};
+
+class VSyncDispatchWrapper : public VSyncDispatch {
+public:
+    VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {}
+    CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
+                                   std::string callbackName) final {
+        return mDispatch->registerCallback(callbackFn, callbackName);
+    }
+
+    void unregisterCallback(CallbackToken token) final { mDispatch->unregisterCallback(token); }
+
+    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
+                            nsecs_t earliestVsync) final {
+        return mDispatch->schedule(token, workDuration, earliestVsync);
+    }
+
+    CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
+
+private:
+    std::shared_ptr<VSyncDispatch> const mDispatch;
+};
+
+std::shared_ptr<FenceTime> generateInvalidFence() {
+    sp<Fence> fence = new Fence();
+    return std::make_shared<FenceTime>(fence);
+}
+
+std::shared_ptr<FenceTime> generatePendingFence() {
+    sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+    return std::make_shared<FenceTime>(fence);
+}
+
+void signalFenceWithTime(std::shared_ptr<FenceTime> const& fence, nsecs_t time) {
+    FenceTime::Snapshot snap(time);
+    fence->applyTrustedSnapshot(snap);
+}
+
+std::shared_ptr<FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
+    sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+    std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
+    signalFenceWithTime(ft, time);
+    return ft;
+}
+
+class VSyncReactorTest : public testing::Test {
+protected:
+    VSyncReactorTest()
+          : mMockDispatch(std::make_shared<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::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;
+    VSyncReactor mReactor;
+};
+
+TEST_F(VSyncReactorTest, addingNullFenceCheck) {
+    EXPECT_FALSE(mReactor.addPresentFence(nullptr));
+}
+
+TEST_F(VSyncReactorTest, addingInvalidFenceSignalsNeedsMoreInfo) {
+    EXPECT_TRUE(mReactor.addPresentFence(generateInvalidFence()));
+}
+
+TEST_F(VSyncReactorTest, addingSignalledFenceAddsToTracker) {
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+}
+
+TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) {
+    nsecs_t anotherDummyTime = 2919019201;
+
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(0);
+    auto pendingFence = generatePendingFence();
+    EXPECT_FALSE(mReactor.addPresentFence(pendingFence));
+    Mock::VerifyAndClearExpectations(mMockTracker.get());
+
+    signalFenceWithTime(pendingFence, dummyTime);
+
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(anotherDummyTime));
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(anotherDummyTime)));
+}
+
+TEST_F(VSyncReactorTest, limitsPendingFences) {
+    std::array<std::shared_ptr<FenceTime>, kPendingLimit * 2> fences;
+    std::array<nsecs_t, fences.size()> fakeTimes;
+    std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); });
+    std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable {
+        i++;
+        return i * i;
+    });
+
+    for (auto const& fence : fences) {
+        mReactor.addPresentFence(fence);
+    }
+
+    for (auto i = fences.size() - kPendingLimit; i < fences.size(); i++) {
+        EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimes[i]));
+    }
+
+    for (auto i = 0u; i < fences.size(); i++) {
+        signalFenceWithTime(fences[i], fakeTimes[i]);
+    }
+    mReactor.addPresentFence(generatePendingFence());
+}
+
+TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) {
+    static constexpr size_t aFewTimes = 8;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime)).Times(1);
+
+    mReactor.setIgnorePresentFences(true);
+    for (auto i = 0; i < aFewTimes; i++) {
+        mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime));
+    }
+
+    mReactor.setIgnorePresentFences(false);
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
+    nsecs_t const fakeTimestamp = 4839;
+    EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
+    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(1)
+            .WillOnce(Return(fakeTimestamp));
+
+    EXPECT_THAT(mReactor.computeNextRefresh(0), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForExpectedPresentTime) {
+    nsecs_t const fakeTimestamp = 4839;
+    EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
+    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(1)
+            .WillOnce(Return(fakeTimestamp));
+
+    EXPECT_THAT(mReactor.expectedPresentTime(), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
+    nsecs_t const fakeTimestamp = 4839;
+    nsecs_t const fakePeriod = 1010;
+    nsecs_t const fakeNow = 2214;
+    int const numPeriodsOut = 3;
+    EXPECT_CALL(*mMockClock, now()).WillOnce(Return(fakeNow));
+    EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
+    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(fakeNow + numPeriodsOut * fakePeriod))
+            .WillOnce(Return(fakeTimestamp));
+    EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, getPeriod) {
+    nsecs_t const fakePeriod = 1010;
+    EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
+    EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod));
+}
+
+TEST_F(VSyncReactorTest, setPeriod) {
+    nsecs_t const fakePeriod = 4098;
+    EXPECT_CALL(*mMockTracker, setPeriod(fakePeriod));
+    mReactor.setPeriod(fakePeriod);
+}
+
+TEST_F(VSyncReactorTest, addResyncSampleTypical) {
+    nsecs_t const fakeTimestamp = 3032;
+    bool periodFlushed = false;
+
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
+    EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) {
+    bool periodFlushed = false;
+    nsecs_t const fakeTimestamp = 4398;
+    nsecs_t const newPeriod = 3490;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
+    mReactor.setPeriod(newPeriod);
+    EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index e94af49..ec74a42 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -35,6 +35,8 @@
     MOCK_METHOD0(incrementMissedFrames, void());
     MOCK_METHOD0(incrementClientCompositionFrames, void());
     MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
+    MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
+    MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
     MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
     MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
index 22df255..2c63da0 100644
--- a/services/surfaceflinger/tests/utils/TransactionUtils.h
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -136,6 +136,11 @@
         }
     }
 
+    static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const Color& color,
+                                 bool unlock = true) {
+        fillSurfaceRGBA8(sc, color.r, color.g, color.b, unlock);
+    }
+
     // Fill an RGBA_8888 formatted surface with a single color.
     static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b,
                                  bool unlock = true) {
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 8f4667e..a020e74 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1311,7 +1311,14 @@
                                             &img.dequeue_fence);
         if (err != 0) {
             ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err);
-            result = VK_ERROR_SURFACE_LOST_KHR;
+            switch (-err) {
+                case ENOMEM:
+                    result = VK_ERROR_OUT_OF_DEVICE_MEMORY;
+                    break;
+                default:
+                    result = VK_ERROR_SURFACE_LOST_KHR;
+                    break;
+            }
             break;
         }
         img.buffer = buffer;
diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp
index a626e48..8528898 100644
--- a/vulkan/vkjson/Android.bp
+++ b/vulkan/vkjson/Android.bp
@@ -1,4 +1,4 @@
-cc_library_static {
+cc_library_shared {
     name: "libvkjson",
     srcs: [
         "vkjson.cc",
@@ -15,11 +15,14 @@
     export_include_dirs: [
         ".",
     ],
+    shared_libs: [
+        "libvulkan",
+    ],
     whole_static_libs: [
         "libjsoncpp",
     ],
-    header_libs: [
-        "vulkan_headers",
+    export_shared_lib_headers: [
+        "libvulkan",
     ],
 }