Merge "Keeping sp<IBinder> around in ClientCache"
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 40b8dc7..bb92fd3 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -20,9 +20,11 @@
         "android.hardware.vibrator@1.2",
         "android.hardware.vibrator@1.3",
         "libbase",
+        "libbinder",
         "libhidlbase",
         "liblog",
         "libutils",
+        "vintf-vibrator-cpp",
     ],
     cflags: [
         "-DLOG_TAG=\"idlcli\"",
@@ -34,6 +36,7 @@
     defaults: ["idlcli-defaults"],
     srcs: [
         "CommandVibrator.cpp",
+        "vibrator/CommandGetCapabilities.cpp",
         "vibrator/CommandOff.cpp",
         "vibrator/CommandOn.cpp",
         "vibrator/CommandPerform.cpp",
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index bcb207b..2f11923 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -18,6 +18,8 @@
 #define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
 
 #include <android/hardware/vibrator/1.3/IVibrator.h>
+#include <android/hardware/vibrator/IVibrator.h>
+#include <binder/IServiceManager.h>
 
 #include "utils.h"
 
@@ -31,9 +33,25 @@
 
 // Creates a Return<R> with STATUS::EX_NULL_POINTER.
 template <class R>
-inline Return<R> NullptrStatus() {
+inline R NullptrStatus() {
     using ::android::hardware::Status;
-    return Return<R>{Status::fromExceptionCode(Status::EX_NULL_POINTER)};
+    return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+}
+
+template <>
+inline binder::Status NullptrStatus() {
+    using binder::Status;
+    return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+}
+
+template <typename I>
+inline sp<I> getService() {
+    return I::getService();
+}
+
+template <>
+inline sp<hardware::vibrator::IVibrator> getService() {
+    return waitForVintfService<hardware::vibrator::IVibrator>();
 }
 
 template <typename I>
@@ -42,12 +60,12 @@
     static std::unique_ptr<HalWrapper> Create() {
         // Assume that if getService returns a nullptr, HAL is not available on the
         // device.
-        auto hal = I::getService();
+        auto hal = getService<I>();
         return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
     }
 
     template <class R, class... Args0, class... Args1>
-    Return<R> call(Return<R> (I::*fn)(Args0...), Args1&&... args1) {
+    R call(R (I::*fn)(Args0...), Args1&&... args1) {
         return (*mHal.*fn)(std::forward<Args1>(args1)...);
     }
 
@@ -65,7 +83,7 @@
 }
 
 template <class R, class I, class... Args0, class... Args1>
-Return<R> halCall(Return<R> (I::*fn)(Args0...), Args1&&... args1) {
+R halCall(R (I::*fn)(Args0...), Args1&&... args1) {
     auto hal = getHal<I>();
     return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>();
 }
@@ -77,6 +95,7 @@
 namespace V1_1 = ::android::hardware::vibrator::V1_1;
 namespace V1_2 = ::android::hardware::vibrator::V1_2;
 namespace V1_3 = ::android::hardware::vibrator::V1_3;
+namespace aidl = ::android::hardware::vibrator;
 
 } // namespace vibrator
 } // namespace idlcli
diff --git a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
new file mode 100644
index 0000000..30d8587
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 CommandGetCapabilities : public Command {
+    std::string getDescription() const override { return "Retrieves vibrator capabilities."; }
+
+    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 cap;
+        Status ret;
+
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::getCapabilities, &cap);
+            statusStr = status.toString8();
+            ret = status.isOk() ? OK : ERROR;
+        } else {
+            return UNAVAILABLE;
+        }
+
+        std::cout << "Status: " << statusStr << std::endl;
+        std::cout << "Capabilities: " << std::bitset<32>(cap) << std::endl;
+
+        return ret;
+    }
+};
+
+static const auto Command =
+        CommandRegistry<CommandVibrator>::Register<CommandGetCapabilities>("getCapabilities");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandOff.cpp b/cmds/idlcli/vibrator/CommandOff.cpp
index a674f01..53fada0 100644
--- a/cmds/idlcli/vibrator/CommandOff.cpp
+++ b/cmds/idlcli/vibrator/CommandOff.cpp
@@ -42,15 +42,24 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        auto ret = halCall(&V1_0::IVibrator::off);
+        std::string statusStr;
+        Status ret;
 
-        if (!ret.isOk()) {
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::off);
+            statusStr = status.toString8();
+            ret = status.isOk() ? OK : ERROR;
+        } else if (auto hal = getHal<V1_0::IVibrator>()) {
+            auto status = hal->call(&V1_0::IVibrator::off);
+            statusStr = toString(status);
+            ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
+        } else {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << toString(ret) << std::endl;
+        std::cout << "Status: " << statusStr << std::endl;
 
-        return ret == V1_0::Status::OK ? OK : ERROR;
+        return ret;
     }
 };
 
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index 2164b7d..ccb3c19 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -50,15 +50,24 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        auto ret = halCall(&V1_0::IVibrator::on, mDuration);
+        std::string statusStr;
+        Status ret;
 
-        if (!ret.isOk()) {
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr);
+            statusStr = status.toString8();
+            ret = status.isOk() ? OK : ERROR;
+        } else if (auto hal = getHal<V1_0::IVibrator>()) {
+            auto status = hal->call(&V1_0::IVibrator::on, mDuration);
+            statusStr = toString(status);
+            ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
+        } else {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << toString(ret) << std::endl;
+        std::cout << "Status: " << statusStr << std::endl;
 
-        return ret == V1_0::Status::OK ? OK : ERROR;
+        return ret;
     }
 
     uint32_t mDuration;
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index 688cbd8..58d4e0a 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -23,6 +23,34 @@
 
 namespace vibrator {
 
+/*
+ * The following static asserts are only relevant here because the argument
+ * parser uses a single implementation for determining the string names.
+ */
+static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
+              static_cast<uint8_t>(aidl::EffectStrength::LIGHT));
+static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) ==
+              static_cast<uint8_t>(aidl::EffectStrength::MEDIUM));
+static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) ==
+              static_cast<uint8_t>(aidl::EffectStrength::STRONG));
+static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) ==
+              static_cast<uint8_t>(aidl::Effect::CLICK));
+static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) ==
+              static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK));
+static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == static_cast<uint8_t>(aidl::Effect::TICK));
+static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == static_cast<uint8_t>(aidl::Effect::THUD));
+static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == static_cast<uint8_t>(aidl::Effect::POP));
+static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) ==
+              static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK));
+static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) ==
+              static_cast<uint8_t>(aidl::Effect::RINGTONE_1));
+static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) ==
+              static_cast<uint8_t>(aidl::Effect::RINGTONE_2));
+static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) ==
+              static_cast<uint8_t>(aidl::Effect::RINGTONE_15));
+static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
+              static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
+
 using V1_0::EffectStrength;
 using V1_3::Effect;
 
@@ -62,38 +90,50 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        Return<void> ret;
-        V1_0::Status status;
+        std::string statusStr;
         uint32_t lengthMs;
-        auto callback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
-            status = retStatus;
-            lengthMs = retLengthMs;
-        };
+        Status ret;
 
-        if (auto hal = getHal<V1_3::IVibrator>()) {
-            ret = hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect),
-                            mStrength, callback);
-        } else if (auto hal = getHal<V1_2::IVibrator>()) {
-            ret = hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect),
-                            mStrength, callback);
-        } else if (auto hal = getHal<V1_1::IVibrator>()) {
-            ret = hal->call(&V1_1::IVibrator::perform_1_1, static_cast<V1_1::Effect_1_1>(mEffect),
-                            mStrength, callback);
-        } else if (auto hal = getHal<V1_0::IVibrator>()) {
-            ret = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect),
-                            mStrength, callback);
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            int32_t aidlLengthMs;
+            auto status =
+                    hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect),
+                              static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs);
+            statusStr = status.toString8();
+            lengthMs = static_cast<uint32_t>(aidlLengthMs);
+            ret = status.isOk() ? OK : ERROR;
         } else {
-            ret = NullptrStatus<void>();
+            Return<void> hidlRet;
+            V1_0::Status status;
+            auto callback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) {
+                status = retStatus;
+                lengthMs = retLengthMs;
+            };
+
+            if (auto hal = getHal<V1_3::IVibrator>()) {
+                hidlRet = hal->call(&V1_3::IVibrator::perform_1_3,
+                                    static_cast<V1_3::Effect>(mEffect), mStrength, callback);
+            } else if (auto hal = getHal<V1_2::IVibrator>()) {
+                hidlRet = hal->call(&V1_2::IVibrator::perform_1_2,
+                                    static_cast<V1_2::Effect>(mEffect), mStrength, callback);
+            } else if (auto hal = getHal<V1_1::IVibrator>()) {
+                hidlRet = hal->call(&V1_1::IVibrator::perform_1_1,
+                                    static_cast<V1_1::Effect_1_1>(mEffect), mStrength, callback);
+            } else if (auto hal = getHal<V1_0::IVibrator>()) {
+                hidlRet = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect),
+                                    mStrength, callback);
+            } else {
+                return UNAVAILABLE;
+            }
+
+            statusStr = toString(status);
+            ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR;
         }
 
-        if (!ret.isOk()) {
-            return UNAVAILABLE;
-        }
-
-        std::cout << "Status: " << toString(status) << std::endl;
+        std::cout << "Status: " << statusStr << std::endl;
         std::cout << "Length: " << lengthMs << std::endl;
 
-        return status == V1_0::Status::OK ? OK : ERROR;
+        return ret;
     }
 
     Effect mEffect;
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
index 38a1dc2..6e2261f 100644
--- a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -50,15 +50,24 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        auto ret = halCall(&V1_0::IVibrator::setAmplitude, mAmplitude);
+        std::string statusStr;
+        Status ret;
 
-        if (!ret.isOk()) {
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::setAmplitude, mAmplitude);
+            statusStr = status.toString8();
+            ret = status.isOk() ? OK : ERROR;
+        } else if (auto hal = getHal<V1_0::IVibrator>()) {
+            auto status = hal->call(&V1_0::IVibrator::setAmplitude, mAmplitude);
+            statusStr = toString(status);
+            ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
+        } else {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << toString(ret) << std::endl;
+        std::cout << "Status: " << statusStr << std::endl;
 
-        return ret == V1_0::Status::OK ? OK : ERROR;
+        return ret;
     }
 
     uint8_t mAmplitude;
diff --git a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
index 5fb1fac..5bc827e 100644
--- a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
+++ b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
@@ -48,15 +48,24 @@
     }
 
     Status doMain(Args && /*args*/) override {
-        auto ret = halCall(&V1_3::IVibrator::setExternalControl, mEnable);
+        std::string statusStr;
+        Status ret;
 
-        if (!ret.isOk()) {
+        if (auto hal = getHal<aidl::IVibrator>()) {
+            auto status = hal->call(&aidl::IVibrator::setExternalControl, mEnable);
+            statusStr = status.toString8();
+            ret = status.isOk() ? OK : ERROR;
+        } else if (auto hal = getHal<V1_3::IVibrator>()) {
+            auto status = hal->call(&V1_3::IVibrator::setExternalControl, mEnable);
+            statusStr = toString(status);
+            ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR;
+        } else {
             return UNAVAILABLE;
         }
 
-        std::cout << "Status: " << toString(ret) << std::endl;
+        std::cout << "Status: " << statusStr << std::endl;
 
-        return ret == V1_0::Status::OK ? OK : ERROR;
+        return ret;
     }
 
     bool mEnable;
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 838d11d..616c3b2 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -708,11 +708,13 @@
     }
 }
 
-static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0;
-static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1;
-static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2;
-static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
-static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
+static constexpr int PROFMAN_BIN_RETURN_CODE_SUCCESS = 0;
+static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 1;
+static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 2;
+static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 3;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 4;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 5;
+static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_DIFFERENT_VERSIONS = 6;
 
 class RunProfman : public ExecVHelper {
   public:
@@ -720,7 +722,9 @@
                   const unique_fd& reference_profile_fd,
                   const std::vector<unique_fd>& apk_fds,
                   const std::vector<std::string>& dex_locations,
-                  bool copy_and_update) {
+                  bool copy_and_update,
+                  bool for_snapshot,
+                  bool for_boot_image) {
 
         // TODO(calin): Assume for now we run in the bg compile job (which is in
         // most of the invocation). With the current data flow, is not very easy or
@@ -752,6 +756,14 @@
             AddArg("--copy-and-update-profile-key");
         }
 
+        if (for_snapshot) {
+            AddArg("--force-merge");
+        }
+
+        if (for_boot_image) {
+            AddArg("--boot-image-merge");
+        }
+
         // Do not add after dex2oat_flags, they should override others for debugging.
         PrepareArgs(profman_bin);
     }
@@ -759,12 +771,16 @@
     void SetupMerge(const std::vector<unique_fd>& profiles_fd,
                     const unique_fd& reference_profile_fd,
                     const std::vector<unique_fd>& apk_fds = std::vector<unique_fd>(),
-                    const std::vector<std::string>& dex_locations = std::vector<std::string>()) {
+                    const std::vector<std::string>& dex_locations = std::vector<std::string>(),
+                    bool for_snapshot = false,
+                    bool for_boot_image = false) {
         SetupArgs(profiles_fd,
                   reference_profile_fd,
                   apk_fds,
                   dex_locations,
-                  /*copy_and_update=*/false);
+                  /*copy_and_update=*/ false,
+                  for_snapshot,
+                  for_boot_image);
     }
 
     void SetupCopyAndUpdate(unique_fd&& profile_fd,
@@ -781,7 +797,9 @@
                   reference_profile_fd_,
                   apk_fds_,
                   dex_locations,
-                  /*copy_and_update=*/true);
+                  /*copy_and_update=*/true,
+                  /*for_snapshot*/false,
+                  /*for_boot_image*/false);
     }
 
     void SetupDump(const std::vector<unique_fd>& profiles_fd,
@@ -795,7 +813,9 @@
                   reference_profile_fd,
                   apk_fds,
                   dex_locations,
-                  /*copy_and_update=*/false);
+                  /*copy_and_update=*/false,
+                  /*for_snapshot*/false,
+                  /*for_boot_image*/false);
     }
 
     void Exec() {
@@ -870,9 +890,14 @@
                 should_clear_current_profiles = false;
                 should_clear_reference_profile = false;
                 break;
+            case PROFMAN_BIN_RETURN_CODE_ERROR_DIFFERENT_VERSIONS:
+                need_to_compile = false;
+                should_clear_current_profiles = true;
+                should_clear_reference_profile = true;
+                break;
            default:
                 // Unknown return code or error. Unlink profiles.
-                LOG(WARNING) << "Unknown error code while processing profiles for location "
+                LOG(WARNING) << "Unexpected error code while processing profiles for location "
                         << location << ": " << return_code;
                 need_to_compile = false;
                 should_clear_current_profiles = true;
@@ -2741,7 +2766,7 @@
     }
 
     RunProfman args;
-    args.SetupMerge(profiles_fd, snapshot_fd, apk_fds, dex_locations);
+    args.SetupMerge(profiles_fd, snapshot_fd, apk_fds, dex_locations, /*for_snapshot=*/true);
     pid_t pid = fork();
     if (pid == 0) {
         /* child -- drop privileges before continuing */
@@ -2756,6 +2781,13 @@
         return false;
     }
 
+    // Verify that profman finished successfully.
+    int profman_code = WEXITSTATUS(return_code);
+    if (profman_code != PROFMAN_BIN_RETURN_CODE_SUCCESS) {
+        LOG(WARNING) << "profman error for " << package_name << ":" << profile_name
+                << ":" << profman_code;
+        return false;
+    }
     return true;
 }
 
@@ -2818,19 +2850,29 @@
     // We do this to avoid opening a huge a amount of files.
     static constexpr size_t kAggregationBatchSize = 10;
 
-    std::vector<unique_fd> profiles_fd;
     for (size_t i = 0; i < profiles.size(); )  {
+        std::vector<unique_fd> profiles_fd;
         for (size_t k = 0; k < kAggregationBatchSize && i < profiles.size(); k++, i++) {
             unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY);
             if (fd.get() >= 0) {
                 profiles_fd.push_back(std::move(fd));
             }
         }
+
+        // We aggregate (read & write) into the same fd multiple times in a row.
+        // We need to reset the cursor every time to ensure we read the whole file every time.
+        if (TEMP_FAILURE_RETRY(lseek(snapshot_fd, 0, SEEK_SET)) == static_cast<off_t>(-1)) {
+            PLOG(ERROR) << "Cannot reset position for snapshot profile";
+            return false;
+        }
+
         RunProfman args;
         args.SetupMerge(profiles_fd,
                         snapshot_fd,
                         apk_fds,
-                        dex_locations);
+                        dex_locations,
+                        /*for_snapshot=*/true,
+                        /*for_boot_image=*/true);
         pid_t pid = fork();
         if (pid == 0) {
             /* child -- drop privileges before continuing */
@@ -2843,12 +2885,21 @@
 
         /* parent */
         int return_code = wait_child(pid);
+
         if (!WIFEXITED(return_code)) {
             PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name;
             return false;
         }
-        return true;
+
+        // Verify that profman finished successfully.
+        int profman_code = WEXITSTATUS(return_code);
+        if (profman_code != PROFMAN_BIN_RETURN_CODE_SUCCESS) {
+            LOG(WARNING) << "profman error for " << package_name << ":" << profile_name
+                    << ":" << profman_code;
+            return false;
+        }
     }
+
     return true;
 }
 
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 0212bc5..69fefa1 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -897,7 +897,9 @@
         std::string expected_profile_content = snap_profile_ + ".expected";
         run_cmd("rm -f " + expected_profile_content);
         run_cmd("touch " + expected_profile_content);
-        run_cmd("profman --profile-file=" + cur_profile_ +
+        // We force merging when creating the expected profile to make sure
+        // that the random profiles do not affect the output.
+        run_cmd("profman --force-merge --profile-file=" + cur_profile_ +
                 " --profile-file=" + ref_profile_ +
                 " --reference-profile-file=" + expected_profile_content +
                 " --apk=" + apk_path_);
@@ -1130,16 +1132,60 @@
 
 class BootProfileTest : public ProfileTest {
   public:
-    virtual void setup() {
+    std::vector<const std::string> extra_apps_;
+    std::vector<int64_t> extra_ce_data_inodes_;
+
+    virtual void SetUp() {
+
         ProfileTest::SetUp();
         intial_android_profiles_dir = android_profiles_dir;
+        // Generate profiles for some extra apps.
+        // When merging boot profile we split profiles into small groups to avoid
+        // opening a lot of file descriptors at the same time.
+        // (Currently the group size for aggregation is 10)
+        //
+        // To stress test that works fine, create profile for more apps.
+        createAppProfilesForBootMerge(21);
     }
 
     virtual void TearDown() {
         android_profiles_dir = intial_android_profiles_dir;
+        deleteAppProfilesForBootMerge();
         ProfileTest::TearDown();
     }
 
+    void createAppProfilesForBootMerge(size_t number_of_profiles) {
+        for (size_t i = 0; i < number_of_profiles; i++) {
+            int64_t ce_data_inode;
+            std::string package_name = "dummy_test_pkg" + std::to_string(i);
+            LOG(INFO) << package_name;
+            ASSERT_BINDER_SUCCESS(service_->createAppData(
+                    volume_uuid_,
+                    package_name,
+                    kTestUserId,
+                    kAppDataFlags,
+                    kTestAppUid,
+                    se_info_,
+                    kOSdkVersion,
+                    &ce_data_inode));
+            extra_apps_.push_back(package_name);
+            extra_ce_data_inodes_.push_back(ce_data_inode);
+            std::string profile = create_current_profile_path(
+                    kTestUserId, package_name, kPrimaryProfile, /*is_secondary_dex*/ false);
+            SetupProfile(profile, kTestAppUid, kTestAppGid, 0600, 1);
+        }
+    }
+
+    void deleteAppProfilesForBootMerge() {
+        if (kDebug) {
+            return;
+        }
+        for (size_t i = 0; i < extra_apps_.size(); i++) {
+            service_->destroyAppData(
+                volume_uuid_, extra_apps_[i], kTestUserId, kAppDataFlags, extra_ce_data_inodes_[i]);
+        }
+    }
+
     void UpdateAndroidProfilesDir(const std::string& profile_dir) {
         android_profiles_dir = profile_dir;
         // We need to create the reference profile directory in the new profile dir.
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 861401c..2d2af3c 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -68,7 +68,15 @@
 }
 #endif  // !VENDORSERVICEMANAGER
 
-ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) {}
+ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) {
+#ifndef VENDORSERVICEMANAGER
+    // can process these at any times, don't want to delay first VINTF client
+    std::thread([] {
+        vintf::VintfObject::GetDeviceHalManifest();
+        vintf::VintfObject::GetFrameworkHalManifest();
+    }).detach();
+#endif  // !VENDORSERVICEMANAGER
+}
 ServiceManager::~ServiceManager() {
     // this should only happen in tests
 
diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h
index 7f5177b..6efa4f7 100644
--- a/include/android/sharedmem.h
+++ b/include/android/sharedmem.h
@@ -21,7 +21,7 @@
 
 /**
  * @file sharedmem.h
- * @brief Shared memory buffers that can be shared across process.
+ * @brief Shared memory buffers that can be shared between processes.
  */
 
 #ifndef ANDROID_SHARED_MEMORY_H
@@ -61,11 +61,15 @@
  *
  * Use close() to release the shared memory region.
  *
+ * Use {@link android.os.ParcelFileDescriptor} to pass the file descriptor to
+ * another process. File descriptors may also be sent to other processes over a Unix domain
+ * socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and cmsg(3) man pages for more information.
+ *
  * Available since API level 26.
  *
  * \param name an optional name.
  * \param size size of the shared memory region
- * \return file descriptor that denotes the shared memory; error code on failure.
+ * \return file descriptor that denotes the shared memory; -1 and sets errno on failure, or -EINVAL if the error is that size was 0.
  */
 int ASharedMemory_create(const char *name, size_t size) __INTRODUCED_IN(26);
 
@@ -109,7 +113,7 @@
  * \param fd   file descriptor of the shared memory region.
  * \param prot any bitwise-or'ed combination of PROT_READ, PROT_WRITE, PROT_EXEC denoting
  *             updated access. Note access can only be removed, but not added back.
- * \return 0 for success, error code on failure.
+ * \return 0 for success, -1 and sets errno on failure.
  */
 int ASharedMemory_setProt(int fd, int prot) __INTRODUCED_IN(26);
 
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 3f47f3b..37c0d77 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -385,6 +385,12 @@
     , mThreadPoolSeq(1)
     , mCallRestriction(CallRestriction::NONE)
 {
+
+// TODO(b/139016109): enforce in build system
+#if defined(__ANDROID_APEX__) && !defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__)
+    LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.");
+#endif
+
     if (mDriverFD >= 0) {
         // mmap the binder, providing a chunk of virtual address space to receive transactions.
         mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index 8c7ebba..b965881 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -58,7 +58,7 @@
      * Returns null if the service does not exist.
      */
     @UnsupportedAppUsage
-    IBinder getService(@utf8InCpp String name);
+    @nullable IBinder getService(@utf8InCpp String name);
 
     /**
      * Retrieve an existing service called @a name from the service
@@ -66,7 +66,7 @@
      * exist.
      */
     @UnsupportedAppUsage
-    IBinder checkService(@utf8InCpp String name);
+    @nullable IBinder checkService(@utf8InCpp String name);
 
     /**
      * Place a new @a service called @a name into the service
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 2a4ded8..56d95a7 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -56,9 +56,11 @@
 /**
  * This interface has the stability of the system image.
  */
-void AIBinder_markSystemStability(AIBinder* binder);
+__attribute__((weak)) void AIBinder_markSystemStability(AIBinder* binder);
 
 static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) {
+    if (AIBinder_markSystemStability == nullptr) return;
+
     AIBinder_markSystemStability(binder);
 }
 
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 250f902..56b94c1 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -62,6 +62,9 @@
         "android.hardware.sensors@1.0::ISensors",
         "android.hardware.thermal@2.0::IThermal",
         "android.hardware.vr@1.0::IVr",
+        "android.hardware.automotive.audiocontrol@1.0::IAudioControl",
+        "android.hardware.automotive.vehicle@2.0::IVehicle",
+        "android.hardware.automotive.evs@1.0::IEvsCamera",
         NULL,
 };
 
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 2859111..354703b 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -124,12 +124,8 @@
     return env;
 }
 
-int GraphicsEnv::getCanLoadSystemLibraries() {
-    if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
-        // Return an integer value since this crosses library boundaries
-        return 1;
-    }
-    return 0;
+bool GraphicsEnv::isDebuggable() {
+    return prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0;
 }
 
 void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path,
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 83448d4..c6dc1f8 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -33,8 +33,16 @@
 public:
     static GraphicsEnv& getInstance();
 
-    // Check if device is debuggable.
-    int getCanLoadSystemLibraries();
+    // Check if the process is debuggable. It returns false except in any of the
+    // following circumstances:
+    // 1. ro.debuggable=1 (global debuggable enabled).
+    // 2. android:debuggable="true" in the manifest for an individual app.
+    // 3. An app which explicitly calls prctl(PR_SET_DUMPABLE, 1).
+    // 4. GraphicsEnv calls prctl(PR_SET_DUMPABLE, 1) in the presence of
+    //    <meta-data android:name="com.android.graphics.injectLayers.enable"
+    //               android:value="true"/>
+    //    in the application manifest.
+    bool isDebuggable();
 
     /*
      * Apis for updatable driver
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
new file mode 100644
index 0000000..63e0734
--- /dev/null
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -0,0 +1,225 @@
+/*
+ * 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 "Choreographer"
+//#define LOG_NDEBUG 0
+
+#include <cinttypes>
+#include <queue>
+#include <thread>
+
+#include <android/choreographer.h>
+#include <androidfw/DisplayEventDispatcher.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <utils/Looper.h>
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+static inline const char* toString(bool value) {
+    return value ? "true" : "false";
+}
+
+struct FrameCallback {
+    AChoreographer_frameCallback callback;
+    AChoreographer_frameCallback64 callback64;
+    void* data;
+    nsecs_t dueTime;
+
+    inline bool operator<(const FrameCallback& rhs) const {
+        // Note that this is intentionally flipped because we want callbacks due sooner to be at
+        // the head of the queue
+        return dueTime > rhs.dueTime;
+    }
+};
+
+
+class Choreographer : public DisplayEventDispatcher, public MessageHandler {
+public:
+    void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+                                  AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
+
+    enum {
+        MSG_SCHEDULE_CALLBACKS = 0,
+        MSG_SCHEDULE_VSYNC = 1
+    };
+    virtual void handleMessage(const Message& message) override;
+
+    static Choreographer* getForThread();
+
+protected:
+    virtual ~Choreographer() = default;
+
+private:
+    explicit Choreographer(const sp<Looper>& looper);
+    Choreographer(const Choreographer&) = delete;
+
+    void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
+    void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+    void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+                               int32_t configId) override;
+
+    void scheduleCallbacks();
+
+    // Protected by mLock
+    std::priority_queue<FrameCallback> mCallbacks;
+
+    mutable Mutex mLock;
+
+    const sp<Looper> mLooper;
+    const std::thread::id mThreadId;
+};
+
+
+static thread_local Choreographer* gChoreographer;
+Choreographer* Choreographer::getForThread() {
+    if (gChoreographer == nullptr) {
+        sp<Looper> looper = Looper::getForThread();
+        if (!looper.get()) {
+            ALOGW("No looper prepared for thread");
+            return nullptr;
+        }
+        gChoreographer = new Choreographer(looper);
+        status_t result = gChoreographer->initialize();
+        if (result != OK) {
+            ALOGW("Failed to initialize");
+            return nullptr;
+        }
+    }
+    return gChoreographer;
+}
+
+Choreographer::Choreographer(const sp<Looper>& looper) :
+    DisplayEventDispatcher(looper), mLooper(looper), mThreadId(std::this_thread::get_id()) {
+}
+
+void Choreographer::postFrameCallbackDelayed(
+        AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    FrameCallback callback{cb, cb64, data, now + delay};
+    {
+        AutoMutex _l{mLock};
+        mCallbacks.push(callback);
+    }
+    if (callback.dueTime <= now) {
+        if (std::this_thread::get_id() != mThreadId) {
+            Message m{MSG_SCHEDULE_VSYNC};
+            mLooper->sendMessage(this, m);
+        } else {
+            scheduleVsync();
+        }
+    } else {
+        Message m{MSG_SCHEDULE_CALLBACKS};
+        mLooper->sendMessageDelayed(delay, this, m);
+    }
+}
+
+void Choreographer::scheduleCallbacks() {
+    AutoMutex _{mLock};
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    if (mCallbacks.top().dueTime <= now) {
+        ALOGV("choreographer %p ~ scheduling vsync", this);
+        scheduleVsync();
+        return;
+    }
+}
+
+// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
+// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
+// the internal display implicitly.
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
+    std::vector<FrameCallback> callbacks{};
+    {
+        AutoMutex _l{mLock};
+        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+        while (!mCallbacks.empty() && mCallbacks.top().dueTime < now) {
+            callbacks.push_back(mCallbacks.top());
+            mCallbacks.pop();
+        }
+    }
+    for (const auto& cb : callbacks) {
+        if (cb.callback64 != nullptr) {
+            cb.callback64(timestamp, cb.data);
+        } else if (cb.callback != nullptr) {
+            cb.callback(timestamp, cb.data);
+        }
+    }
+}
+
+void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
+    ALOGV("choreographer %p ~ received hotplug event (displayId=%"
+            ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.",
+            this, displayId, toString(connected));
+}
+
+void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId,
+                                          int32_t configId) {
+    ALOGV("choreographer %p ~ received config changed event (displayId=%"
+            ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%s), ignoring.",
+            this, displayId, toString(configId));
+}
+
+void Choreographer::handleMessage(const Message& message) {
+    switch (message.what) {
+    case MSG_SCHEDULE_CALLBACKS:
+        scheduleCallbacks();
+        break;
+    case MSG_SCHEDULE_VSYNC:
+        scheduleVsync();
+        break;
+    }
+}
+
+}
+
+/* Glue for the NDK interface */
+
+using android::Choreographer;
+
+static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
+    return reinterpret_cast<Choreographer*>(choreographer);
+}
+
+static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) {
+    return reinterpret_cast<AChoreographer*>(choreographer);
+}
+
+AChoreographer* AChoreographer_getInstance() {
+    return Choreographer_to_AChoreographer(Choreographer::getForThread());
+}
+
+void AChoreographer_postFrameCallback(AChoreographer* choreographer,
+        AChoreographer_frameCallback callback, void* data) {
+    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+            callback, nullptr, data, 0);
+}
+void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
+        AChoreographer_frameCallback callback, void* data, long delayMillis) {
+    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+            callback, nullptr, data, ms2ns(delayMillis));
+}
+void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
+        AChoreographer_frameCallback64 callback, void* data) {
+    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+            nullptr, callback, data, 0);
+}
+void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
+        AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) {
+    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+            nullptr, callback, data, ms2ns(delayMillis));
+}
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index 66ebdfd..45b935a 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -12,6 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+ndk_headers {
+    name: "libachoreographer_ndk_headers",
+    from: "include/android",
+    to: "android",
+    srcs: ["include/android/*.h"],
+    license: "NOTICE",
+}
+
 cc_library_headers {
     name: "libnativedisplay_headers",
     export_include_dirs: ["include"],
@@ -33,10 +41,12 @@
     ],
 
     srcs: [
+        "AChoreographer.cpp",
         "ADisplay.cpp",
     ],
 
     shared_libs: [
+        "libandroidfw",
         "libgui",
         "liblog",
         "libui",
diff --git a/libs/nativedisplay/MODULE_LICENSE_APACHE2 b/libs/nativedisplay/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/nativedisplay/MODULE_LICENSE_APACHE2
diff --git a/libs/nativedisplay/NOTICE b/libs/nativedisplay/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/nativedisplay/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/include/android/choreographer.h b/libs/nativedisplay/include/android/choreographer.h
similarity index 100%
rename from include/android/choreographer.h
rename to libs/nativedisplay/include/android/choreographer.h
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
new file mode 100644
index 0000000..3b29c18
--- /dev/null
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -0,0 +1,10 @@
+LIBNATIVEDISPLAY {
+  global:
+    AChoreographer_getInstance; # apex # introduced=30
+    AChoreographer_postFrameCallback; # apex # introduced=30
+    AChoreographer_postFrameCallbackDelayed; # apex # introduced=30
+    AChoreographer_postFrameCallback64; # apex # introduced=30
+    AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30
+  local:
+    *;
+};
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 27ab482..55400c7 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -85,6 +85,11 @@
     export_header_lib_headers: [
         "libnativebase_headers",
     ],
+
+    stubs: {
+        symbol_file: "libnativewindow.map.txt",
+        versions: ["29"],
+    },
 }
 
 llndk_library {
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index daf1dcc..f59e8f0 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -2,9 +2,9 @@
   global:
     AHardwareBuffer_acquire;
     AHardwareBuffer_allocate;
-    AHardwareBuffer_createFromHandle; # llndk
+    AHardwareBuffer_createFromHandle; # llndk # apex
     AHardwareBuffer_describe;
-    AHardwareBuffer_getNativeHandle; # llndk
+    AHardwareBuffer_getNativeHandle; # llndk # apex
     AHardwareBuffer_isSupported; # introduced=29
     AHardwareBuffer_lock;
     AHardwareBuffer_lockAndGetInfo; # introduced=29
diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp
index cc2731d..71b1f9f 100644
--- a/libs/nativewindow/tests/AHardwareBufferTest.cpp
+++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp
@@ -20,6 +20,7 @@
 #include <android/hardware_buffer.h>
 #include <private/android/AHardwareBufferHelpers.h>
 #include <android/hardware/graphics/common/1.0/types.h>
+#include <vndk/hardware_buffer.h>
 
 #include <gtest/gtest.h>
 
@@ -100,9 +101,33 @@
             (uint64_t)BufferUsage::CPU_WRITE_RARELY,
             AHARDWAREBUFFER_USAGE_CPU_READ_RARELY | AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY));
 
-EXPECT_TRUE(TestUsageConversion(
+    EXPECT_TRUE(TestUsageConversion(
         (uint64_t)BufferUsage::GPU_RENDER_TARGET | (uint64_t)BufferUsage::GPU_TEXTURE |
-            1ull << 29 | 1ull << 57,
+        1ull << 29 | 1ull << 57,
         AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
         AHARDWAREBUFFER_USAGE_VENDOR_1 | AHARDWAREBUFFER_USAGE_VENDOR_13));
 }
+
+TEST(AHardwareBufferTest, GetCreateHandleTest) {
+    AHardwareBuffer_Desc desc{
+            .width = 64,
+            .height = 1,
+            .layers = 1,
+            .format = AHARDWAREBUFFER_FORMAT_BLOB,
+            .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN,
+            .stride = 64,
+    };
+
+    AHardwareBuffer* buffer = nullptr;
+    EXPECT_EQ(0, AHardwareBuffer_allocate(&desc, &buffer));
+    const native_handle_t* handle = AHardwareBuffer_getNativeHandle(buffer);
+    EXPECT_NE(nullptr, handle);
+
+    AHardwareBuffer* otherBuffer = nullptr;
+    EXPECT_EQ(0, AHardwareBuffer_createFromHandle(
+        &desc, handle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, &otherBuffer));
+    EXPECT_NE(nullptr, otherBuffer);
+
+    AHardwareBuffer_release(buffer);
+    AHardwareBuffer_release(otherBuffer);
+}
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
index ac01dc8..44a1c0b 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -151,7 +151,7 @@
 const char kSystemLayerLibraryDir[] = "/data/local/debug/gles";
 
 std::string LayerLoader::GetDebugLayers() {
-    // Layers can be specified at the Java level in GraphicsEnvironemnt
+    // Layers can be specified at the Java level in GraphicsEnvironment
     // gpu_debug_layers_gles = layer1:layer2:layerN
     std::string debug_layers = android::GraphicsEnv::getInstance().getDebugLayersGLES();
 
@@ -337,7 +337,7 @@
 
     // Only enable the system search path for non-user builds
     std::string system_path;
-    if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+    if (android::GraphicsEnv::getInstance().isDebuggable()) {
         system_path = kSystemLayerLibraryDir;
     }
 
@@ -379,14 +379,12 @@
                 // any symbol dependencies will be resolved by system libraries. They
                 // can't safely use libc++_shared, for example. Which is one reason
                 // (among several) we only allow them in non-user builds.
-                void* handle = nullptr;
                 auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace();
                 if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) {
-                    bool native_bridge = false;
                     char* error_message = nullptr;
-                    handle = OpenNativeLibraryInNamespace(
-                        app_namespace, layer.c_str(), &native_bridge, &error_message);
-                    if (!handle) {
+                    dlhandle_ = OpenNativeLibraryInNamespace(
+                        app_namespace, layer.c_str(), &native_bridge_, &error_message);
+                    if (!dlhandle_) {
                         ALOGE("Failed to load layer %s with error: %s", layer.c_str(),
                               error_message);
                         android::NativeLoaderFreeErrorMessage(error_message);
@@ -394,11 +392,11 @@
                     }
 
                 } else {
-                    handle = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL);
+                    dlhandle_ = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL);
                 }
 
-                if (handle) {
-                    ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)handle,
+                if (dlhandle_) {
+                    ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)dlhandle_,
                           layers[i].c_str());
                 } else {
                     // If the layer is found but can't be loaded, try setenforce 0
@@ -411,8 +409,7 @@
                 std::string init_func = "AndroidGLESLayer_Initialize";
                 ALOGV("Looking for entrypoint %s", init_func.c_str());
 
-                layer_init_func LayerInit =
-                        reinterpret_cast<layer_init_func>(dlsym(handle, init_func.c_str()));
+                layer_init_func LayerInit = GetTrampoline<layer_init_func>(init_func.c_str());
                 if (LayerInit) {
                     ALOGV("Found %s for layer %s", init_func.c_str(), layer.c_str());
                     layer_init_.push_back(LayerInit);
@@ -425,8 +422,7 @@
                 std::string setup_func = "AndroidGLESLayer_GetProcAddress";
                 ALOGV("Looking for entrypoint %s", setup_func.c_str());
 
-                layer_setup_func LayerSetup =
-                        reinterpret_cast<layer_setup_func>(dlsym(handle, setup_func.c_str()));
+                layer_setup_func LayerSetup = GetTrampoline<layer_setup_func>(setup_func.c_str());
                 if (LayerSetup) {
                     ALOGV("Found %s for layer %s", setup_func.c_str(), layer.c_str());
                     layer_setup_.push_back(LayerSetup);
diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h
index e401b44..1e2783f 100644
--- a/opengl/libs/EGL/egl_layers.h
+++ b/opengl/libs/EGL/egl_layers.h
@@ -21,10 +21,15 @@
 #include <unordered_map>
 #include <vector>
 
-#include <EGL/egldefs.h>
+#include <android/dlext.h>
+#include <dlfcn.h>
 
+#include <EGL/egldefs.h>
 #include "egl_platform_entries.h"
 
+#include <nativebridge/native_bridge.h>
+#include <nativeloader/native_loader.h>
+
 typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer;
 
 namespace android {
@@ -54,10 +59,21 @@
     std::vector<layer_setup_func> layer_setup_;
 
 private:
-    LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0){};
+    LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0), dlhandle_(nullptr), native_bridge_(false){};
     bool layers_loaded_;
     bool initialized_;
     unsigned current_layer_;
+    void* dlhandle_;
+    bool native_bridge_;
+
+    template<typename Func = void*>
+    Func GetTrampoline(const char* name) const {
+        if (native_bridge_) {
+            return reinterpret_cast<Func>(android::NativeBridgeGetTrampoline(
+                dlhandle_, name, nullptr, 0));
+        }
+        return reinterpret_cast<Func>(dlsym(dlhandle_, name));
+    }
 };
 
 }; // namespace android
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 1043390..e7640dd 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -46,6 +46,7 @@
 }
 
 void InputManager::initialize() {
+    mReaderThread = new InputReaderThread(mReader);
     mDispatcherThread = new InputDispatcherThread(mDispatcher);
 }
 
@@ -56,9 +57,9 @@
         return result;
     }
 
-    result = mReader->start();
+    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
     if (result) {
-        ALOGE("Could not start InputReader due to error %d.", result);
+        ALOGE("Could not start InputReader thread due to error %d.", result);
 
         mDispatcherThread->requestExit();
         return result;
@@ -68,9 +69,9 @@
 }
 
 status_t InputManager::stop() {
-    status_t result = mReader->stop();
+    status_t result = mReaderThread->requestExitAndWait();
     if (result) {
-        ALOGW("Could not stop InputReader due to error %d.", result);
+        ALOGW("Could not stop InputReader thread due to error %d.", result);
     }
 
     result = mDispatcherThread->requestExitAndWait();
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 2a7ed0f..40f66d8 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 has two components.
+ * The input manager uses two threads.
  *
- * 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.
+ * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events,
+ *    applies policy, and posts messages to a queue managed by the DispatcherThread.
  * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
  *    queue and asynchronously dispatches them to applications.
  *
- * By design, the InputReader class and InputDispatcherThread class do not share any
- * internal state.  Moreover, all communication is done one way from the InputReader
+ * By design, the InputReaderThread class and InputDispatcherThread class do not share any
+ * internal state.  Moreover, all communication is done one way from the InputReaderThread
  * into the InputDispatcherThread and never the reverse.  Both classes may interact with the
  * InputDispatchPolicy, however.
  *
@@ -102,6 +102,7 @@
 
 private:
     sp<InputReaderInterface> mReader;
+    sp<InputReaderThread> mReaderThread;
 
     sp<InputClassifierInterface> mClassifier;
 
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 2d6f2c1..0422d83 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -33,6 +33,20 @@
 
 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/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 56c0a73..5d576b9 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/Errors.h>
+#include <utils/Thread.h>
 #include <utils/RefBase.h>
 
 #include <stddef.h>
@@ -44,16 +44,7 @@
 
 namespace android {
 
-// --- 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.
- */
+/* Processes raw input events and sends cooked event data to an input listener. */
 class InputReaderInterface : public virtual RefBase {
 protected:
     InputReaderInterface() { }
@@ -65,17 +56,18 @@
      * This method may be called on any thread (usually by the input manager). */
     virtual void dump(std::string& dump) = 0;
 
-    /* Called by the heartbeat to ensures that the reader has not deadlocked. */
+    /* Called by the heatbeat 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;
 
-    /* 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;
+    /* 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;
 
     /* Gets information about all input devices.
      *
@@ -112,7 +104,17 @@
     virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
 };
 
-// --- InputReaderConfiguration ---
+/* 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();
+};
 
 /*
  * Input reader configuration.
@@ -283,8 +285,6 @@
     std::vector<DisplayViewport> mDisplays;
 };
 
-// --- TouchAffineTransformation ---
-
 struct TouchAffineTransformation {
     float x_scale;
     float x_ymix;
@@ -307,8 +307,6 @@
     void applyTo(float& x, float& y) const;
 };
 
-// --- InputReaderPolicyInterface ---
-
 /*
  * Input reader policy interface.
  *
@@ -318,8 +316,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 will NOT re-enter the input reader interface, so they may be called from
- * any method in the input reader interface.
+ * These methods must NOT re-enter the input reader since they may be called while
+ * holding the input reader lock.
  */
 class InputReaderPolicyInterface : public virtual RefBase {
 protected:
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 05f0db1..1c5adc3 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -38,38 +38,16 @@
 #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)
@@ -83,7 +61,6 @@
         mNextTimeout(LLONG_MAX),
         mConfigurationChangesToRefresh(0) {
     mQueuedListener = new QueuedInputListener(listener);
-    mThread = new InputReaderThread(this);
 
     { // acquire lock
         AutoMutex _l(mLock);
@@ -99,28 +76,6 @@
     }
 }
 
-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 0a4e808..557eb3b 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. InputReader starts its own thread, where
- * most of the work happens, but the InputReader can receive queries from other system
+ * 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
  * 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. All calls to InputListener must happen from InputReader's thread.
+ * InputListener.
  */
 class InputReader : public InputReaderInterface {
 public:
@@ -55,8 +55,7 @@
     virtual void dump(std::string& dump) override;
     virtual void monitor() override;
 
-    virtual status_t start() override;
-    virtual status_t stop() override;
+    virtual void loopOnce() override;
 
     virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
 
@@ -112,9 +111,6 @@
     friend class ContextImpl;
 
 private:
-    class InputReaderThread;
-    sp<InputReaderThread> mThread;
-
     Mutex mLock;
 
     Condition mReaderIsAliveCondition;
@@ -137,10 +133,6 @@
 
     std::unordered_map<int32_t /*deviceId*/, InputDevice*> mDevices;
 
-    // With each iteration of the loop, InputReader reads and processes one incoming message from
-    // the EventHub.
-    void loopOnce();
-
     // low-level input event decoding and device management
     void processEventsLocked(const RawEvent* rawEvents, size_t count);
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index b706a74..2a29e0d 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -50,43 +50,35 @@
 
 public:
     FakeInputDispatcherPolicy() {
-        mInputEventFiltered = false;
-        mTime = -1;
-        mAction = -1;
-        mDisplayId = -1;
         mOnPointerDownToken.clear();
     }
 
-    void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyMotionArgs* args) {
-        ASSERT_TRUE(mInputEventFiltered)
-                << "Expected filterInputEvent() to have been called.";
+    void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
+        ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
+        ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_KEY);
 
-        ASSERT_EQ(mTime, args->eventTime)
-                << "Expected time of filtered event was not matched";
-        ASSERT_EQ(mAction, args->action)
-                << "Expected action of filtered event was not matched";
-        ASSERT_EQ(mDisplayId, args->displayId)
-                << "Expected displayId of filtered event was not matched";
+        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();
     }
 
-    void assertFilterInputEventWasCalledWithExpectedArgs(const NotifyKeyArgs* args) {
-        ASSERT_TRUE(mInputEventFiltered)
-                << "Expected filterInputEvent() to have been called.";
+    void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) {
+        ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
+        ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_MOTION);
 
-        ASSERT_EQ(mTime, args->eventTime)
-                << "Expected time of filtered event was not matched";
-        ASSERT_EQ(mAction, args->action)
-                << "Expected action of filtered event was not matched";
-        ASSERT_EQ(mDisplayId, args->displayId)
-                << "Expected displayId of filtered event was not matched";
+        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();
     }
 
     void assertFilterInputEventWasNotCalled() {
-        ASSERT_FALSE(mInputEventFiltered)
+        ASSERT_EQ(nullptr, mFilteredEvent)
                 << "Expected filterInputEvent() to not have been called.";
     }
 
@@ -97,10 +89,7 @@
     }
 
 private:
-    bool mInputEventFiltered;
-    nsecs_t mTime;
-    int32_t mAction;
-    int32_t mDisplayId;
+    std::unique_ptr<InputEvent> mFilteredEvent;
     sp<IBinder> mOnPointerDownToken;
 
     virtual void notifyConfigurationChanged(nsecs_t) {
@@ -122,26 +111,20 @@
         *outConfig = mConfig;
     }
 
-    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
         switch (inputEvent->getType()) {
             case AINPUT_EVENT_TYPE_KEY: {
                 const KeyEvent* keyEvent = static_cast<const KeyEvent*>(inputEvent);
-                mTime = keyEvent->getEventTime();
-                mAction = keyEvent->getAction();
-                mDisplayId = keyEvent->getDisplayId();
+                mFilteredEvent = std::make_unique<KeyEvent>(*keyEvent);
                 break;
             }
 
             case AINPUT_EVENT_TYPE_MOTION: {
                 const MotionEvent* motionEvent = static_cast<const MotionEvent*>(inputEvent);
-                mTime = motionEvent->getEventTime();
-                mAction = motionEvent->getAction();
-                mDisplayId = motionEvent->getDisplayId();
+                mFilteredEvent = std::make_unique<MotionEvent>(*motionEvent);
                 break;
             }
         }
-
-        mInputEventFiltered = true;
         return true;
     }
 
@@ -176,10 +159,7 @@
     }
 
     void reset() {
-        mInputEventFiltered = false;
-        mTime = -1;
-        mAction = -1;
-        mDisplayId = -1;
+        mFilteredEvent = nullptr;
         mOnPointerDownToken.clear();
     }
 };
@@ -385,8 +365,8 @@
 
 class FakeInputReceiver {
 public:
-    void consumeEvent(int32_t expectedEventType, int32_t expectedDisplayId,
-            int32_t expectedFlags = 0) {
+    void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
+                      int32_t expectedFlags) {
         uint32_t consumeSeq;
         InputEvent* event;
         status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
@@ -399,33 +379,41 @@
         ASSERT_EQ(expectedEventType, event->getType())
                 << mName.c_str() << ": event type should match.";
 
-        ASSERT_EQ(expectedDisplayId, event->getDisplayId())
-                << mName.c_str() << ": event displayId should be the same as expected.";
+        EXPECT_EQ(expectedDisplayId, event->getDisplayId());
 
-        int32_t flags;
         switch (expectedEventType) {
             case AINPUT_EVENT_TYPE_KEY: {
-                KeyEvent* typedEvent = static_cast<KeyEvent*>(event);
-                flags = typedEvent->getFlags();
+                const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event);
+                EXPECT_EQ(expectedAction, keyEvent.getAction());
+                EXPECT_EQ(expectedFlags, keyEvent.getFlags());
                 break;
             }
             case AINPUT_EVENT_TYPE_MOTION: {
-                MotionEvent* typedEvent = static_cast<MotionEvent*>(event);
-                flags = typedEvent->getFlags();
+                const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+                EXPECT_EQ(expectedAction, motionEvent.getAction());
+                EXPECT_EQ(expectedFlags, motionEvent.getFlags());
                 break;
             }
             default: {
                 FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
             }
         }
-        ASSERT_EQ(expectedFlags, flags)
-                << mName.c_str() << ": event flags should be the same as expected.";
 
         status = mConsumer->sendFinishedSignal(consumeSeq, handled());
         ASSERT_EQ(OK, status)
                 << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
     }
 
+    void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
+                     expectedFlags);
+    }
+
+    void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
+                     expectedFlags);
+    }
+
     void assertNoEvents() {
         uint32_t consumeSeq;
         InputEvent* event;
@@ -631,7 +619,7 @@
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
 
     // Window should receive motion event.
-    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
 }
 
 // The foreground window should receive the first touch down event.
@@ -652,7 +640,7 @@
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
 
     // Top window should receive the touch down event. Second window should not receive anything.
-    windowTop->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    windowTop->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowSecond->assertNoEvents();
 }
 
@@ -678,7 +666,7 @@
 
     // Focused window should receive event.
     windowTop->assertNoEvents();
-    windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
 }
 
 TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) {
@@ -703,7 +691,7 @@
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
 
     // Top focused window should receive event.
-    windowTop->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    windowTop->consumeKeyDown(ADISPLAY_ID_NONE);
     windowSecond->assertNoEvents();
 }
 
@@ -733,7 +721,7 @@
 
     // Top window is invalid, so it should not receive any input event.
     windowTop->assertNoEvents();
-    windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
 }
 
 TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
@@ -758,7 +746,7 @@
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
               injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
                                 ADISPLAY_ID_DEFAULT, 610, 400, 599, 400));
-    windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowRight->assertNoEvents();
 }
 
@@ -814,7 +802,7 @@
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
             AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowInSecondary->assertNoEvents();
 
     // Test touch down on second display.
@@ -822,29 +810,29 @@
             AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     windowInPrimary->assertNoEvents();
-    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+    windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
 }
 
 TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
     // Test inject a key down with display id specified.
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_DEFAULT);
+    windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
     windowInSecondary->assertNoEvents();
 
     // Test inject a key down without display id specified.
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     windowInPrimary->assertNoEvents();
-    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
 
     // Remove secondary display.
     std::vector<sp<InputWindowHandle>> noWindows;
     mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID);
 
     // Expect old focus should receive a cancel event.
-    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE,
-            AKEY_EVENT_FLAG_CANCELED);
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
+                                    AKEY_EVENT_FLAG_CANCELED);
 
     // Test inject a key down, should timeout because of no target window.
     ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher))
@@ -873,8 +861,8 @@
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
             AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
-    monitorInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT);
+    windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitorInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowInSecondary->assertNoEvents();
     monitorInSecondary->assertNoEvents();
 
@@ -884,8 +872,8 @@
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     windowInPrimary->assertNoEvents();
     monitorInPrimary->assertNoEvents();
-    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
-    monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID);
+    windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
+    monitorInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
 
     // Test inject a non-pointer motion event.
     // If specific a display, it will dispatch to the focused window of particular display,
@@ -895,8 +883,8 @@
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     windowInPrimary->assertNoEvents();
     monitorInPrimary->assertNoEvents();
-    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
-    monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE);
+    windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
+    monitorInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
 }
 
 // Test per-display input monitors for key event.
@@ -912,8 +900,8 @@
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
     windowInPrimary->assertNoEvents();
     monitorInPrimary->assertNoEvents();
-    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
-    monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
+    monitorInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
 }
 
 class InputFilterTest : public InputDispatcherTest {
@@ -931,7 +919,7 @@
         mDispatcher->notifyMotion(&motionArgs);
 
         if (expectToBeFiltered) {
-            mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&motionArgs);
+            mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
         } else {
             mFakePolicy->assertFilterInputEventWasNotCalled();
         }
@@ -946,7 +934,7 @@
         mDispatcher->notifyKey(&keyArgs);
 
         if (expectToBeFiltered) {
-            mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&keyArgs);
+            mFakePolicy->assertFilterInputEventWasCalled(keyArgs);
         } else {
             mFakePolicy->assertFilterInputEventWasNotCalled();
         }
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 8d4ab6a..c1c9122 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1133,8 +1133,12 @@
 protected:
     sp<FakeInputReaderPolicy> mFakePolicy;
 
-    virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
-    virtual void TearDown() override { mFakePolicy.clear(); }
+    virtual void SetUp() {
+        mFakePolicy = new FakeInputReaderPolicy();
+    }
+    virtual void TearDown() {
+        mFakePolicy.clear();
+    }
 };
 
 /**
@@ -1317,20 +1321,18 @@
     sp<TestInputListener> mFakeListener;
     sp<FakeInputReaderPolicy> mFakePolicy;
     std::shared_ptr<FakeEventHub> mFakeEventHub;
-    std::unique_ptr<InstrumentedInputReader> mReader;
+    sp<InstrumentedInputReader> mReader;
 
-    virtual void SetUp() override {
+    virtual void SetUp() {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
 
-        mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
-                                                            mFakeListener);
-        ASSERT_EQ(OK, mReader->start());
+        mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
     }
 
-    virtual void TearDown() override {
-        ASSERT_EQ(OK, mReader->stop());
+    virtual void TearDown() {
+        mReader.clear();
 
         mFakeListener.clear();
         mFakePolicy.clear();
@@ -1344,18 +1346,24 @@
             mFakeEventHub->addConfigurationMap(deviceId, configuration);
         }
         mFakeEventHub->finishDeviceScan();
+        mReader->loopOnce();
+        mReader->loopOnce();
         ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
         ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
     }
 
     void disableDevice(int32_t deviceId, InputDevice* device) {
         mFakePolicy->addDisabledDevice(deviceId);
-        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
+        configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
     }
 
     void enableDevice(int32_t deviceId, InputDevice* device) {
         mFakePolicy->removeDisabledDevice(deviceId);
-        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
+        configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
+    }
+
+    void configureDevice(uint32_t changes, InputDevice* device) {
+        device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
     }
 
     FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
@@ -1409,22 +1417,28 @@
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
+    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
     ASSERT_EQ(deviceId, resetArgs.deviceId);
 
     ASSERT_EQ(device->isEnabled(), true);
     disableDevice(deviceId, device);
+    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);
 
     disableDevice(deviceId, device);
+    mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled());
     ASSERT_EQ(device->isEnabled(), false);
 
     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);
 }
@@ -1546,7 +1560,7 @@
     ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]);
 }
 
-TEST_F(InputReaderTest, WhenDeviceScanFinished_SendsConfigurationChanged) {
+TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
     addDevice(1, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr);
 
     NotifyConfigurationChangedArgs args;
@@ -1555,12 +1569,13 @@
     ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
 }
 
-TEST_F(InputReaderTest, ForwardsRawEventsToMappers) {
+TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
     FakeInputMapper* mapper = nullptr;
     ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
             INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
 
     mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1);
+    mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
 
     RawEvent event;
@@ -1587,16 +1602,19 @@
     uint32_t prevSequenceNum = resetArgs.sequenceNum;
 
     disableDevice(deviceId, device);
+    mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
     prevSequenceNum = resetArgs.sequenceNum;
 
     enableDevice(deviceId, device);
+    mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
     prevSequenceNum = resetArgs.sequenceNum;
 
     disableDevice(deviceId, device);
+    mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
     prevSequenceNum = resetArgs.sequenceNum;
@@ -1611,6 +1629,7 @@
     FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN);
     device->addMapper(mapper);
     mReader->setNextDevice(device);
+    addDevice(deviceId, "fake", deviceClass, nullptr);
 
     const uint8_t hdmi1 = 1;
 
@@ -1618,20 +1637,13 @@
     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);
-
-    // 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));
+    mReader->loopOnce();
     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());
@@ -1640,8 +1652,6 @@
 
     // Can't dispatch event from a disabled device.
     disableDevice(deviceId, device);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
-    ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled());
     ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
 }
 
@@ -1664,7 +1674,7 @@
 
     InputDevice* mDevice;
 
-    virtual void SetUp() override {
+    virtual void SetUp() {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
@@ -1678,7 +1688,7 @@
                 DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
     }
 
-    virtual void TearDown() override {
+    virtual void TearDown() {
         delete mDevice;
 
         delete mFakeContext;
@@ -1902,7 +1912,7 @@
     FakeInputReaderContext* mFakeContext;
     InputDevice* mDevice;
 
-    virtual void SetUp() override {
+    virtual void SetUp() {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
@@ -1916,7 +1926,7 @@
         mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
     }
 
-    virtual void TearDown() override {
+    virtual void TearDown() {
         delete mDevice;
         delete mFakeContext;
         mFakeListener.clear();
@@ -2579,7 +2589,7 @@
 
     sp<FakePointerController> mFakePointerController;
 
-    virtual void SetUp() override {
+    virtual void SetUp() {
         InputMapperTest::SetUp();
 
         mFakePointerController = new FakePointerController();
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 4226e9a..f529a44 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -165,7 +165,7 @@
         "Scheduler/PhaseOffsets.cpp",
         "Scheduler/Scheduler.cpp",
         "Scheduler/SchedulerUtils.cpp",
-        "Scheduler/VSyncDispatch.cpp",
+        "Scheduler/VSyncDispatchTimerQueue.cpp",
         "Scheduler/VSyncModulator.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index a25709c..94c4a81 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -77,9 +77,9 @@
         // with the clone layer trying to use the deleted texture.
         mFlinger->deleteTextureAsync(mTextureName);
     }
-    const int32_t layerID = getSequence();
-    mFlinger->mTimeStats->onDestroy(layerID);
-    mFlinger->mFrameTracer->onDestroy(layerID);
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->onDestroy(layerId);
+    mFlinger->mFrameTracer->onDestroy(layerId);
 }
 
 void BufferLayer::useSurfaceDamage() {
@@ -286,7 +286,7 @@
     return hasReadyFrame();
 }
 
-bool BufferLayer::onPostComposition(const std::optional<DisplayId>& displayId,
+bool BufferLayer::onPostComposition(sp<const DisplayDevice> displayDevice,
                                     const std::shared_ptr<FenceTime>& glDoneFence,
                                     const std::shared_ptr<FenceTime>& presentFence,
                                     const CompositorTiming& compositorTiming) {
@@ -305,8 +305,16 @@
     nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
     mFrameTracker.setDesiredPresentTime(desiredPresentTime);
 
-    const int32_t layerID = getSequence();
-    mFlinger->mTimeStats->setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
+
+    const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+    if (outputLayer && outputLayer->requiresClientComposition()) {
+        nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
+        mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+                                               clientCompositionTimestamp,
+                                               FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
+    }
 
     std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
     if (frameReadyFence->isValid()) {
@@ -317,17 +325,18 @@
         mFrameTracker.setFrameReadyTime(desiredPresentTime);
     }
 
+    const auto displayId = displayDevice->getId();
     if (presentFence->isValid()) {
-        mFlinger->mTimeStats->setPresentFence(layerID, mCurrentFrameNumber, presentFence);
-        mFlinger->mFrameTracer->traceFence(layerID, getCurrentBufferId(), mCurrentFrameNumber,
+        mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence);
+        mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                            presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
         mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
     } else if (displayId && mFlinger->getHwComposer().isConnected(*displayId)) {
         // The HWC doesn't support present fences, so use the refresh
         // timestamp instead.
         const nsecs_t actualPresentTime = mFlinger->getHwComposer().getRefreshTimestamp(*displayId);
-        mFlinger->mTimeStats->setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
-        mFlinger->mFrameTracer->traceTimestamp(layerID, getCurrentBufferId(), mCurrentFrameNumber,
+        mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime);
+        mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
                                                actualPresentTime,
                                                FrameTracer::FrameEvent::PRESENT_FENCE);
         mFrameTracker.setActualPresentTime(actualPresentTime);
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 656ba12..16855d2 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -78,7 +78,7 @@
 
     bool isHdrY410() const override;
 
-    bool onPostComposition(const std::optional<DisplayId>& displayId,
+    bool onPostComposition(sp<const DisplayDevice> displayDevice,
                            const std::shared_ptr<FenceTime>& glDoneFence,
                            const std::shared_ptr<FenceTime>& presentFence,
                            const CompositorTiming& compositorTiming) override;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index dc61e49..d51d34b 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -223,7 +223,7 @@
     // BufferItem's that weren't actually queued. This can happen in shared
     // buffer mode.
     bool queuedBuffer = false;
-    const int32_t layerID = getSequence();
+    const int32_t layerId = getSequence();
     LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
                     getProducerStickyTransform() != 0, mName, mOverrideScalingMode,
                     getTransformToDisplayInverse());
@@ -264,7 +264,7 @@
         if (queuedBuffer) {
             Mutex::Autolock lock(mQueueItemLock);
             mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
-            mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
             mQueueItems.removeAt(0);
             mQueuedFrames--;
         }
@@ -278,8 +278,8 @@
             Mutex::Autolock lock(mQueueItemLock);
             mQueueItems.clear();
             mQueuedFrames = 0;
-            mFlinger->mTimeStats->onDestroy(layerID);
-            mFlinger->mFrameTracer->onDestroy(layerID);
+            mFlinger->mTimeStats->onDestroy(layerId);
+            mFlinger->mFrameTracer->onDestroy(layerId);
         }
 
         // Once we have hit this state, the shadow queue may no longer
@@ -301,19 +301,17 @@
         // updateTexImage
         while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
             mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
-            mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
             mQueueItems.removeAt(0);
             mQueuedFrames--;
         }
 
         uint64_t bufferID = mQueueItems[0].mGraphicBuffer->getId();
-        mFlinger->mTimeStats->setAcquireFence(layerID, currentFrameNumber,
-                                              mQueueItems[0].mFenceTime);
-        mFlinger->mFrameTracer->traceFence(layerID, bufferID, currentFrameNumber,
+        mFlinger->mFrameTracer->traceFence(layerId, bufferID, currentFrameNumber,
                                            mQueueItems[0].mFenceTime,
                                            FrameTracer::FrameEvent::ACQUIRE_FENCE);
-        mFlinger->mTimeStats->setLatchTime(layerID, currentFrameNumber, latchTime);
-        mFlinger->mFrameTracer->traceTimestamp(layerID, bufferID, currentFrameNumber, latchTime,
+        mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime);
+        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime,
                                                FrameTracer::FrameEvent::LATCH);
 
         mQueueItems.removeAt(0);
@@ -373,38 +371,36 @@
 // -----------------------------------------------------------------------
 
 void BufferQueueLayer::onFrameDequeued(const uint64_t bufferId) {
-    const int32_t layerID = getSequence();
-    mFlinger->mFrameTracer->traceNewLayer(layerID, getName().c_str());
-    mFlinger->mFrameTracer->traceTimestamp(layerID, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+    const int32_t layerId = getSequence();
+    mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
                                            systemTime(), FrameTracer::FrameEvent::DEQUEUE);
 }
 
 void BufferQueueLayer::onFrameDetached(const uint64_t bufferId) {
-    const int32_t layerID = getSequence();
-    mFlinger->mFrameTracer->traceNewLayer(layerID, getName().c_str());
-    mFlinger->mFrameTracer->traceTimestamp(layerID, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+    const int32_t layerId = getSequence();
+    mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
                                            systemTime(), FrameTracer::FrameEvent::DETACH);
 }
 
 void BufferQueueLayer::onFrameCancelled(const uint64_t bufferId) {
-    const int32_t layerID = getSequence();
-    mFlinger->mFrameTracer->traceTimestamp(layerID, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+    const int32_t layerId = getSequence();
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
                                            systemTime(), FrameTracer::FrameEvent::CANCEL);
 }
 
 void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
-    const int32_t layerID = getSequence();
-    mFlinger->mFrameTracer->traceTimestamp(layerID, item.mGraphicBuffer->getId(), item.mFrameNumber,
+    const int32_t layerId = getSequence();
+    mFlinger->mFrameTracer->traceTimestamp(layerId, item.mGraphicBuffer->getId(), item.mFrameNumber,
                                            systemTime(), FrameTracer::FrameEvent::QUEUE);
 
     ATRACE_CALL();
     // Add this buffer from our internal queue tracker
     { // Autolock scope
-        if (mFlinger->mUseSmart90ForVideo) {
-            const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
-            mFlinger->mScheduler->recordLayerHistory(this, presentTime,
-                                                     item.mHdrMetadata.validTypes != 0);
-        }
+        const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
+        const bool isHDR = item.mHdrMetadata.validTypes != 0;
+        mFlinger->mScheduler->recordLayerHistory(this, presentTime, isHDR);
 
         Mutex::Autolock lock(mQueueItemLock);
         // Reset the frame number tracker when we receive the first buffer after
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 5768edd..d68fe8e 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -240,18 +240,15 @@
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
 
-    const int32_t layerID = getSequence();
-    mFlinger->mTimeStats->setPostTime(layerID, mFrameNumber, getName().c_str(), postTime);
-    mFlinger->mFrameTracer->traceNewLayer(layerID, getName().c_str());
-    mFlinger->mFrameTracer->traceTimestamp(layerID, buffer->getId(), mFrameNumber, postTime,
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->setPostTime(layerId, mFrameNumber, getName().c_str(), postTime);
+    mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+    mFlinger->mFrameTracer->traceTimestamp(layerId, buffer->getId(), mFrameNumber, postTime,
                                            FrameTracer::FrameEvent::POST);
     mCurrentState.desiredPresentTime = desiredPresentTime;
 
-    if (mFlinger->mUseSmart90ForVideo) {
-        const nsecs_t presentTime = (desiredPresentTime == -1) ? 0 : desiredPresentTime;
-        mFlinger->mScheduler->recordLayerHistory(this, presentTime,
-                                                 mCurrentState.hdrMetadata.validTypes != 0);
-    }
+    const bool isHDR = mCurrentState.hdrMetadata.validTypes != 0;
+    mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime, isHDR);
 
     return true;
 }
@@ -461,7 +458,7 @@
         return NO_ERROR;
     }
 
-    const int32_t layerID = getSequence();
+    const int32_t layerId = getSequence();
 
     // Reject if the layer is invalid
     uint32_t bufferWidth = s.buffer->width;
@@ -483,7 +480,7 @@
         ALOGE("[%s] rejecting buffer: "
               "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}",
               getDebugName(), bufferWidth, bufferHeight, s.active.w, s.active.h);
-        mFlinger->mTimeStats->removeTimeRecord(layerID, mFrameNumber);
+        mFlinger->mTimeStats->removeTimeRecord(layerId, mFrameNumber);
         return BAD_VALUE;
     }
 
@@ -500,18 +497,18 @@
         // a GL-composited layer) not at all.
         status_t err = bindTextureImage();
         if (err != NO_ERROR) {
-            mFlinger->mTimeStats->onDestroy(layerID);
-            mFlinger->mFrameTracer->onDestroy(layerID);
+            mFlinger->mTimeStats->onDestroy(layerId);
+            mFlinger->mFrameTracer->onDestroy(layerId);
             return BAD_VALUE;
         }
     }
 
     const uint64_t bufferID = getCurrentBufferId();
-    mFlinger->mTimeStats->setAcquireFence(layerID, mFrameNumber, mBufferInfo.mFenceTime);
-    mFlinger->mFrameTracer->traceFence(layerID, bufferID, mFrameNumber, mBufferInfo.mFenceTime,
+    mFlinger->mTimeStats->setAcquireFence(layerId, mFrameNumber, mBufferInfo.mFenceTime);
+    mFlinger->mFrameTracer->traceFence(layerId, bufferID, mFrameNumber, mBufferInfo.mFenceTime,
                                        FrameTracer::FrameEvent::ACQUIRE_FENCE);
-    mFlinger->mTimeStats->setLatchTime(layerID, mFrameNumber, latchTime);
-    mFlinger->mFrameTracer->traceTimestamp(layerID, bufferID, mFrameNumber, latchTime,
+    mFlinger->mTimeStats->setLatchTime(layerId, mFrameNumber, latchTime);
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, mFrameNumber, latchTime,
                                            FrameTracer::FrameEvent::LATCH);
 
     mCurrentStateModified = false;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 1347449..11cfccc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -98,6 +98,9 @@
 
     // Debugging
     void dump(std::string& result) const;
+
+    // Timestamp for when the layer is queued for client composition
+    nsecs_t clientCompositionTimestamp;
 };
 
 } // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index aa638b7..6877f8b 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -551,6 +551,10 @@
     ATRACE_CALL();
     ALOGV(__FUNCTION__);
 
+    if (!getState().isEnabled) {
+        return;
+    }
+
     for (auto* layer : getOutputLayersOrderedByZ()) {
         layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame,
                                       refreshArgs.devOptForceClientComposition);
@@ -900,6 +904,7 @@
                     layerSettings.disableBlending = true;
                 }
 
+                layer->editState().clientCompositionTimestamp = systemTime();
                 clientCompositionLayers.push_back(*result);
             }
         }
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 0dbf8f0..49e7c70 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -15,10 +15,12 @@
  */
 
 #include <compositionengine/CompositionRefreshArgs.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/impl/CompositionEngine.h>
 #include <compositionengine/mock/Layer.h>
 #include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/Output.h>
+#include <compositionengine/mock/OutputLayer.h>
 #include <gtest/gtest.h>
 #include <renderengine/mock/RenderEngine.h>
 
@@ -28,16 +30,28 @@
 namespace {
 
 using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Ref;
 using ::testing::Return;
+using ::testing::ReturnRef;
 using ::testing::SaveArg;
 using ::testing::StrictMock;
 
-class CompositionEngineTest : public testing::Test {
-public:
+struct CompositionEngineTest : public testing::Test {
     android::mock::HWComposer* mHwc = new StrictMock<android::mock::HWComposer>();
     renderengine::mock::RenderEngine* mRenderEngine =
             new StrictMock<renderengine::mock::RenderEngine>();
     impl::CompositionEngine mEngine;
+    CompositionRefreshArgs mRefreshArgs;
+
+    std::shared_ptr<mock::Output> mOutput1{std::make_shared<StrictMock<mock::Output>>()};
+    std::shared_ptr<mock::Output> mOutput2{std::make_shared<StrictMock<mock::Output>>()};
+    std::shared_ptr<mock::Output> mOutput3{std::make_shared<StrictMock<mock::Output>>()};
+
+    std::shared_ptr<mock::Layer> mLayer1{std::make_shared<StrictMock<mock::Layer>>()};
+    std::shared_ptr<mock::Layer> mLayer2{std::make_shared<StrictMock<mock::Layer>>()};
+    std::shared_ptr<mock::Layer> mLayer3{std::make_shared<StrictMock<mock::Layer>>()};
+    std::shared_ptr<mock::Layer> mLayer4{std::make_shared<StrictMock<mock::Layer>>()};
 };
 
 TEST_F(CompositionEngineTest, canInstantiateCompositionEngine) {
@@ -58,38 +72,165 @@
 }
 
 /*
+ * CompositionEngine::present
+ */
+
+struct CompositionEnginePresentTest : public CompositionEngineTest {
+    struct CompositionEnginePartialMock : public impl::CompositionEngine {
+        // These are the overridable functions CompositionEngine::present() may
+        // call, and have separate test coverage.
+        MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
+    };
+
+    StrictMock<CompositionEnginePartialMock> mEngine;
+};
+
+TEST_F(CompositionEnginePresentTest, worksWithEmptyRequest) {
+    // present() always calls preComposition()
+    EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs)));
+
+    mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEnginePresentTest, worksAsExpected) {
+    // Expect calls to in a certain sequence
+    InSequence seq;
+
+    // present() always calls preComposition()
+    EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs)));
+
+    // The first step in presenting is to make sure all outputs are prepared.
+    EXPECT_CALL(*mOutput1, prepare(Ref(mRefreshArgs), _));
+    EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _));
+    EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _));
+
+    // The next step in presenting is to make sure all outputs have the latest
+    // state from the front-end (SurfaceFlinger).
+    EXPECT_CALL(*mOutput1, updateLayerStateFromFE(Ref(mRefreshArgs)));
+    EXPECT_CALL(*mOutput2, updateLayerStateFromFE(Ref(mRefreshArgs)));
+    EXPECT_CALL(*mOutput3, updateLayerStateFromFE(Ref(mRefreshArgs)));
+
+    // The last step is to actually present each output.
+    EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs)));
+    EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs)));
+    EXPECT_CALL(*mOutput3, present(Ref(mRefreshArgs)));
+
+    mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
+    mEngine.present(mRefreshArgs);
+}
+
+/*
+ * CompositionEngine::updateCursorAsync
+ */
+
+struct CompositionEngineUpdateCursorAsyncTest : public CompositionEngineTest {
+public:
+    CompositionEngineUpdateCursorAsyncTest() {
+        EXPECT_CALL(*mOutput1, getOutputLayerCount()).WillRepeatedly(Return(0));
+        EXPECT_CALL(*mOutput1, getOutputLayerOrderedByZByIndex(_)).Times(0);
+
+        EXPECT_CALL(*mOutput2, getOutputLayerCount()).WillRepeatedly(Return(1));
+        EXPECT_CALL(*mOutput2, getOutputLayerOrderedByZByIndex(0))
+                .WillRepeatedly(Return(&mOutput2OutputLayer1));
+
+        EXPECT_CALL(*mOutput3, getOutputLayerCount()).WillRepeatedly(Return(2));
+        EXPECT_CALL(*mOutput3, getOutputLayerOrderedByZByIndex(0))
+                .WillRepeatedly(Return(&mOutput3OutputLayer1));
+        EXPECT_CALL(*mOutput3, getOutputLayerOrderedByZByIndex(1))
+                .WillRepeatedly(Return(&mOutput3OutputLayer2));
+
+        EXPECT_CALL(mOutput2OutputLayer1, getLayerFE()).WillRepeatedly(ReturnRef(mOutput2Layer1FE));
+        EXPECT_CALL(mOutput3OutputLayer1, getLayerFE()).WillRepeatedly(ReturnRef(mOutput3Layer1FE));
+        EXPECT_CALL(mOutput3OutputLayer2, getLayerFE()).WillRepeatedly(ReturnRef(mOutput3Layer2FE));
+
+        EXPECT_CALL(mOutput2OutputLayer1, getLayer()).WillRepeatedly(ReturnRef(mOutput2Layer1));
+        EXPECT_CALL(mOutput3OutputLayer1, getLayer()).WillRepeatedly(ReturnRef(mOutput3Layer1));
+        EXPECT_CALL(mOutput3OutputLayer2, getLayer()).WillRepeatedly(ReturnRef(mOutput3Layer2));
+
+        EXPECT_CALL(mOutput2Layer1, editFEState()).WillRepeatedly(ReturnRef(mOutput2Layer1FEState));
+        EXPECT_CALL(mOutput3Layer1, editFEState()).WillRepeatedly(ReturnRef(mOutput3Layer1FEState));
+        EXPECT_CALL(mOutput3Layer2, editFEState()).WillRepeatedly(ReturnRef(mOutput3Layer2FEState));
+    }
+
+    StrictMock<mock::OutputLayer> mOutput2OutputLayer1;
+    StrictMock<mock::OutputLayer> mOutput3OutputLayer1;
+    StrictMock<mock::OutputLayer> mOutput3OutputLayer2;
+
+    StrictMock<mock::LayerFE> mOutput2Layer1FE;
+    StrictMock<mock::LayerFE> mOutput3Layer1FE;
+    StrictMock<mock::LayerFE> mOutput3Layer2FE;
+
+    StrictMock<mock::Layer> mOutput2Layer1;
+    StrictMock<mock::Layer> mOutput3Layer1;
+    StrictMock<mock::Layer> mOutput3Layer2;
+
+    LayerFECompositionState mOutput2Layer1FEState;
+    LayerFECompositionState mOutput3Layer1FEState;
+    LayerFECompositionState mOutput3Layer2FEState;
+};
+
+TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesNoOutputs) {
+    mEngine.updateCursorAsync(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesNoLayersBeingCursorLayers) {
+    EXPECT_CALL(mOutput2OutputLayer1, isHardwareCursor()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput3OutputLayer1, isHardwareCursor()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput3OutputLayer2, isHardwareCursor()).WillRepeatedly(Return(false));
+
+    mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
+
+    mEngine.updateCursorAsync(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesMultipleLayersBeingCursorLayers) {
+    {
+        InSequence seq;
+        EXPECT_CALL(mOutput2OutputLayer1, isHardwareCursor()).WillRepeatedly(Return(true));
+        EXPECT_CALL(mOutput2Layer1FE, latchCursorCompositionState(Ref(mOutput2Layer1FEState)));
+        EXPECT_CALL(mOutput2OutputLayer1, writeCursorPositionToHWC());
+    }
+
+    {
+        InSequence seq;
+        EXPECT_CALL(mOutput3OutputLayer1, isHardwareCursor()).WillRepeatedly(Return(true));
+        EXPECT_CALL(mOutput3Layer1FE, latchCursorCompositionState(Ref(mOutput3Layer1FEState)));
+        EXPECT_CALL(mOutput3OutputLayer1, writeCursorPositionToHWC());
+    }
+
+    {
+        InSequence seq;
+        EXPECT_CALL(mOutput3OutputLayer2, isHardwareCursor()).WillRepeatedly(Return(true));
+        EXPECT_CALL(mOutput3Layer2FE, latchCursorCompositionState(Ref(mOutput3Layer2FEState)));
+        EXPECT_CALL(mOutput3OutputLayer2, writeCursorPositionToHWC());
+    }
+
+    mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
+
+    mEngine.updateCursorAsync(mRefreshArgs);
+}
+
+/*
  * CompositionEngine::preComposition
  */
 
-class PreCompositionTest : public CompositionEngineTest {
-public:
-    PreCompositionTest() {
+struct CompositionTestPreComposition : public CompositionEngineTest {
+    CompositionTestPreComposition() {
         EXPECT_CALL(*mLayer1, getLayerFE()).WillRepeatedly(Return(mLayer1FE));
         EXPECT_CALL(*mLayer2, getLayerFE()).WillRepeatedly(Return(mLayer2FE));
         EXPECT_CALL(*mLayer3, getLayerFE()).WillRepeatedly(Return(mLayer3FE));
         // getLayerFE() can return nullptr. Ensure that this is handled.
         EXPECT_CALL(*mLayer4, getLayerFE()).WillRepeatedly(Return(nullptr));
-
-        mRefreshArgs.outputs = {mOutput};
-        mRefreshArgs.layers = {mLayer1, mLayer2, mLayer3, mLayer4};
     }
 
-    std::shared_ptr<mock::Output> mOutput{std::make_shared<StrictMock<mock::Output>>()};
-    std::shared_ptr<mock::Layer> mLayer1{std::make_shared<StrictMock<mock::Layer>>()};
-    std::shared_ptr<mock::Layer> mLayer2{std::make_shared<StrictMock<mock::Layer>>()};
-    std::shared_ptr<mock::Layer> mLayer3{std::make_shared<StrictMock<mock::Layer>>()};
-    std::shared_ptr<mock::Layer> mLayer4{std::make_shared<StrictMock<mock::Layer>>()};
     sp<StrictMock<mock::LayerFE>> mLayer1FE{new StrictMock<mock::LayerFE>()};
     sp<StrictMock<mock::LayerFE>> mLayer2FE{new StrictMock<mock::LayerFE>()};
     sp<StrictMock<mock::LayerFE>> mLayer3FE{new StrictMock<mock::LayerFE>()};
-
-    CompositionRefreshArgs mRefreshArgs;
 };
 
-TEST_F(PreCompositionTest, preCompositionSetsFrameTimestamp) {
+TEST_F(CompositionTestPreComposition, preCompositionSetsFrameTimestamp) {
     const nsecs_t before = systemTime(SYSTEM_TIME_MONOTONIC);
-    CompositionRefreshArgs emptyArgs;
-    mEngine.preComposition(emptyArgs);
+    mEngine.preComposition(mRefreshArgs);
     const nsecs_t after = systemTime(SYSTEM_TIME_MONOTONIC);
 
     // The frame timestamp should be between the before and after timestamps
@@ -97,7 +238,7 @@
     EXPECT_LE(mEngine.getLastFrameRefreshTimestamp(), after);
 }
 
-TEST_F(PreCompositionTest, preCompositionInvokesLayerPreCompositionWithFrameTimestamp) {
+TEST_F(CompositionTestPreComposition, preCompositionInvokesLayerPreCompositionWithFrameTimestamp) {
     nsecs_t ts1 = 0;
     nsecs_t ts2 = 0;
     nsecs_t ts3 = 0;
@@ -105,6 +246,9 @@
     EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
     EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
 
+    mRefreshArgs.outputs = {mOutput1};
+    mRefreshArgs.layers = {mLayer1, mLayer2, mLayer3, mLayer4};
+
     mEngine.preComposition(mRefreshArgs);
 
     // Each of the onPreComposition calls should used the same refresh timestamp
@@ -113,24 +257,31 @@
     EXPECT_EQ(ts3, mEngine.getLastFrameRefreshTimestamp());
 }
 
-TEST_F(PreCompositionTest, preCompositionDefaultsToNoUpdateNeeded) {
+TEST_F(CompositionTestPreComposition, preCompositionDefaultsToNoUpdateNeeded) {
     EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(false));
     EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
     EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
 
     mEngine.setNeedsAnotherUpdateForTest(true);
 
+    mRefreshArgs.outputs = {mOutput1};
+    mRefreshArgs.layers = {mLayer1, mLayer2, mLayer3, mLayer4};
+
     mEngine.preComposition(mRefreshArgs);
 
     // The call should have cleared the needsAnotherUpdate flag
     EXPECT_FALSE(mEngine.needsAnotherUpdate());
 }
 
-TEST_F(PreCompositionTest, preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) {
+TEST_F(CompositionTestPreComposition,
+       preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) {
     EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(true));
     EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
     EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
 
+    mRefreshArgs.outputs = {mOutput1};
+    mRefreshArgs.layers = {mLayer1, mLayer2, mLayer3, mLayer4};
+
     mEngine.preComposition(mRefreshArgs);
 
     EXPECT_TRUE(mEngine.needsAnotherUpdate());
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 95ae888..bac7c75 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -483,6 +483,31 @@
 }
 
 /*
+ * Output::updateAndWriteCompositionState()
+ */
+
+TEST_F(OutputTest, updateAndWriteCompositionState_takesEarlyOutIfNotEnabled) {
+    mOutput->editState().isEnabled = false;
+
+    CompositionRefreshArgs args;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputTest, updateAndWriteCompositionState_updatesLayers) {
+    mOutput->editState().isEnabled = true;
+    mock::OutputLayer* outputLayer = new StrictMock<mock::OutputLayer>();
+    mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(outputLayer));
+
+    EXPECT_CALL(*outputLayer, updateCompositionState(true, true)).Times(1);
+    EXPECT_CALL(*outputLayer, writeStateToHWC(true)).Times(1);
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = true;
+    args.devOptForceClientComposition = true;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+/*
  * Output::prepareFrame()
  */
 
@@ -780,6 +805,7 @@
     EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
     EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
     EXPECT_CALL(leftLayerFE, prepareClientComposition(_)).WillOnce(Return(leftLayerRESettings));
+    EXPECT_CALL(leftOutputLayer, editState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
 
     EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
     EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
@@ -788,6 +814,7 @@
     EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
     EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
     EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
+    EXPECT_CALL(rightOutputLayer, editState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
 
     EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
     EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
@@ -840,6 +867,7 @@
     EXPECT_CALL(outputLayer, needsFiltering()).WillRepeatedly(Return(false));
     EXPECT_CALL(layer, getFEState()).WillRepeatedly(ReturnRef(layerFEState));
     EXPECT_CALL(layerFE, prepareClientComposition(_)).Times(0);
+    EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
 
     EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
     EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)).WillRepeatedly(Return(&outputLayer));
@@ -905,6 +933,7 @@
     EXPECT_CALL(leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
     EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
     EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
+    EXPECT_CALL(leftOutputLayer, editState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
 
     EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
     EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
@@ -913,6 +942,7 @@
     EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
     EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
     EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
+    EXPECT_CALL(rightOutputLayer, editState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
 
     EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
     EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
index 3a0408e..6f91843 100644
--- a/services/surfaceflinger/FrameTracer/FrameTracer.cpp
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
@@ -44,52 +44,52 @@
     FrameTracerDataSource::Register(dsd);
 }
 
-void FrameTracer::traceNewLayer(int32_t layerID, const std::string& layerName) {
-    FrameTracerDataSource::Trace([this, layerID, &layerName](FrameTracerDataSource::TraceContext) {
-        if (mTraceTracker.find(layerID) == mTraceTracker.end()) {
+void FrameTracer::traceNewLayer(int32_t layerId, const std::string& layerName) {
+    FrameTracerDataSource::Trace([this, layerId, &layerName](FrameTracerDataSource::TraceContext) {
+        if (mTraceTracker.find(layerId) == mTraceTracker.end()) {
             std::lock_guard<std::mutex> lock(mTraceMutex);
-            mTraceTracker[layerID].layerName = layerName;
+            mTraceTracker[layerId].layerName = layerName;
         }
     });
 }
 
-void FrameTracer::traceTimestamp(int32_t layerID, uint64_t bufferID, uint64_t frameNumber,
+void FrameTracer::traceTimestamp(int32_t layerId, uint64_t bufferID, uint64_t frameNumber,
                                  nsecs_t timestamp, FrameEvent::BufferEventType type,
                                  nsecs_t duration) {
-    FrameTracerDataSource::Trace([this, layerID, bufferID, frameNumber, timestamp, type,
+    FrameTracerDataSource::Trace([this, layerId, bufferID, frameNumber, timestamp, type,
                                   duration](FrameTracerDataSource::TraceContext ctx) {
         std::lock_guard<std::mutex> lock(mTraceMutex);
-        if (mTraceTracker.find(layerID) == mTraceTracker.end()) {
+        if (mTraceTracker.find(layerId) == mTraceTracker.end()) {
             return;
         }
 
         // Handle any pending fences for this buffer.
-        tracePendingFencesLocked(ctx, layerID, bufferID);
+        tracePendingFencesLocked(ctx, layerId, bufferID);
 
         // Complete current trace.
-        traceLocked(ctx, layerID, bufferID, frameNumber, timestamp, type, duration);
+        traceLocked(ctx, layerId, bufferID, frameNumber, timestamp, type, duration);
     });
 }
 
-void FrameTracer::traceFence(int32_t layerID, uint64_t bufferID, uint64_t frameNumber,
+void FrameTracer::traceFence(int32_t layerId, uint64_t bufferID, uint64_t frameNumber,
                              const std::shared_ptr<FenceTime>& fence,
                              FrameEvent::BufferEventType type, nsecs_t startTime) {
-    FrameTracerDataSource::Trace([this, layerID, bufferID, frameNumber, &fence, type,
+    FrameTracerDataSource::Trace([this, layerId, bufferID, frameNumber, &fence, type,
                                   startTime](FrameTracerDataSource::TraceContext ctx) {
         const nsecs_t signalTime = fence->getSignalTime();
         if (signalTime != Fence::SIGNAL_TIME_INVALID) {
             std::lock_guard<std::mutex> lock(mTraceMutex);
-            if (mTraceTracker.find(layerID) == mTraceTracker.end()) {
+            if (mTraceTracker.find(layerId) == mTraceTracker.end()) {
                 return;
             }
 
             // Handle any pending fences for this buffer.
-            tracePendingFencesLocked(ctx, layerID, bufferID);
+            tracePendingFencesLocked(ctx, layerId, bufferID);
 
             if (signalTime != Fence::SIGNAL_TIME_PENDING) {
-                traceSpanLocked(ctx, layerID, bufferID, frameNumber, type, startTime, signalTime);
+                traceSpanLocked(ctx, layerId, bufferID, frameNumber, type, startTime, signalTime);
             } else {
-                mTraceTracker[layerID].pendingFences[bufferID].push_back(
+                mTraceTracker[layerId].pendingFences[bufferID].push_back(
                         {.frameNumber = frameNumber,
                          .type = type,
                          .fence = fence,
@@ -100,9 +100,9 @@
 }
 
 void FrameTracer::tracePendingFencesLocked(FrameTracerDataSource::TraceContext& ctx,
-                                           int32_t layerID, uint64_t bufferID) {
-    if (mTraceTracker[layerID].pendingFences.count(bufferID)) {
-        auto& pendingFences = mTraceTracker[layerID].pendingFences[bufferID];
+                                           int32_t layerId, uint64_t bufferID) {
+    if (mTraceTracker[layerId].pendingFences.count(bufferID)) {
+        auto& pendingFences = mTraceTracker[layerId].pendingFences[bufferID];
         for (size_t i = 0; i < pendingFences.size(); ++i) {
             auto& pendingFence = pendingFences[i];
 
@@ -116,7 +116,7 @@
 
             if (signalTime != Fence::SIGNAL_TIME_INVALID &&
                 systemTime() - signalTime < kFenceSignallingDeadline) {
-                traceSpanLocked(ctx, layerID, bufferID, pendingFence.frameNumber, pendingFence.type,
+                traceSpanLocked(ctx, layerId, bufferID, pendingFence.frameNumber, pendingFence.type,
                                 pendingFence.startTime, signalTime);
             }
 
@@ -126,7 +126,7 @@
     }
 }
 
-void FrameTracer::traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID,
+void FrameTracer::traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
                               uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
                               FrameEvent::BufferEventType type, nsecs_t duration) {
     auto packet = ctx.NewTracePacket();
@@ -138,9 +138,9 @@
     }
     event->set_type(type);
 
-    if (mTraceTracker.find(layerID) != mTraceTracker.end() &&
-        !mTraceTracker[layerID].layerName.empty()) {
-        const std::string& layerName = mTraceTracker[layerID].layerName;
+    if (mTraceTracker.find(layerId) != mTraceTracker.end() &&
+        !mTraceTracker[layerId].layerName.empty()) {
+        const std::string& layerName = mTraceTracker[layerId].layerName;
         event->set_layer_name(layerName.c_str(), layerName.size());
     }
 
@@ -149,7 +149,7 @@
     }
 }
 
-void FrameTracer::traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID,
+void FrameTracer::traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
                                   uint64_t bufferID, uint64_t frameNumber,
                                   FrameEvent::BufferEventType type, nsecs_t startTime,
                                   nsecs_t endTime) {
@@ -159,12 +159,12 @@
         timestamp = startTime;
         duration = endTime - startTime;
     }
-    traceLocked(ctx, layerID, bufferID, frameNumber, timestamp, type, duration);
+    traceLocked(ctx, layerId, bufferID, frameNumber, timestamp, type, duration);
 }
 
-void FrameTracer::onDestroy(int32_t layerID) {
+void FrameTracer::onDestroy(int32_t layerId) {
     std::lock_guard<std::mutex> traceLock(mTraceMutex);
-    mTraceTracker.erase(layerID);
+    mTraceTracker.erase(layerId);
 }
 
 std::string FrameTracer::miniDump() {
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.h b/services/surfaceflinger/FrameTracer/FrameTracer.h
index e91a750..ef5df90 100644
--- a/services/surfaceflinger/FrameTracer/FrameTracer.h
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.h
@@ -47,21 +47,21 @@
     void registerDataSource();
     // Starts tracking a new layer for tracing. Needs to be called once before traceTimestamp() or
     // traceFence() for each layer.
-    void traceNewLayer(int32_t layerID, const std::string& layerName);
+    void traceNewLayer(int32_t layerId, const std::string& layerName);
     // Creates a trace point at the timestamp provided.
-    void traceTimestamp(int32_t layerID, uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
+    void traceTimestamp(int32_t layerId, uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
                         FrameEvent::BufferEventType type, nsecs_t duration = 0);
     // Creates a trace point after the provided fence has been signalled. If a startTime is provided
     // the trace will have be timestamped from startTime until fence signalling time. If no
     // startTime is provided, a durationless trace point will be created timestamped at fence
     // signalling time. If the fence hasn't signalled yet, the trace point will be created the next
     // time after signalling a trace call for this buffer occurs.
-    void traceFence(int32_t layerID, uint64_t bufferID, uint64_t frameNumber,
+    void traceFence(int32_t layerId, uint64_t bufferID, uint64_t frameNumber,
                     const std::shared_ptr<FenceTime>& fence, FrameEvent::BufferEventType type,
                     nsecs_t startTime = 0);
 
     // Takes care of cleanup when a layer is destroyed.
-    void onDestroy(int32_t layerID);
+    void onDestroy(int32_t layerId);
 
     std::string miniDump();
 
@@ -88,15 +88,15 @@
 
     // Checks if any pending fences for a layer and buffer have signalled and, if they have, creates
     // trace points for them.
-    void tracePendingFencesLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID,
+    void tracePendingFencesLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
                                   uint64_t bufferID);
     // Creates a trace point by translating a start time and an end time to a timestamp and
     // duration. If startTime is later than end time it sets end time as the timestamp and the
     // duration to 0. Used by traceFence().
-    void traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID,
+    void traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
                          uint64_t bufferID, uint64_t frameNumber, FrameEvent::BufferEventType type,
                          nsecs_t startTime, nsecs_t endTime);
-    void traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID, uint64_t bufferID,
+    void traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId, uint64_t bufferID,
                      uint64_t frameNumber, nsecs_t timestamp, FrameEvent::BufferEventType type,
                      nsecs_t duration = 0);
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d5a9ae1..ce9aab5 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -109,6 +109,7 @@
     mCurrentState.hasColorTransform = false;
     mCurrentState.colorSpaceAgnostic = false;
     mCurrentState.metadata = args.metadata;
+    mCurrentState.shadowRadius = 0.f;
 
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
@@ -120,7 +121,10 @@
 
     mCallingPid = args.callingPid;
     mCallingUid = args.callingUid;
-    mFlinger->onLayerCreated(this);
+}
+
+void Layer::onFirstRef() {
+    mFlinger->onLayerFirstRef(this);
 }
 
 Layer::~Layer() {
@@ -1356,9 +1360,9 @@
 void Layer::onDisconnect() {
     Mutex::Autolock lock(mFrameEventHistoryMutex);
     mFrameEventHistory.onDisconnect();
-    const int32_t layerID = getSequence();
-    mFlinger->mTimeStats->onDestroy(layerID);
-    mFlinger->mFrameTracer->onDestroy(layerID);
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->onDestroy(layerId);
+    mFlinger->mFrameTracer->onDestroy(layerId);
 }
 
 void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
@@ -1366,6 +1370,8 @@
     if (newTimestamps) {
         mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
                                           getName().c_str(), newTimestamps->postedTime);
+        mFlinger->mTimeStats->setAcquireFence(getSequence(), newTimestamps->frameNumber,
+                                              newTimestamps->acquireFence);
     }
 
     Mutex::Autolock lock(mFrameEventHistoryMutex);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index fb72391..286311b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -222,6 +222,8 @@
     explicit Layer(const LayerCreationArgs& args);
     virtual ~Layer();
 
+    void onFirstRef() override;
+
     int getWindowType() const { return mWindowType; }
 
     void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
@@ -529,7 +531,7 @@
      * called after composition.
      * returns true if the layer latched a new buffer this frame.
      */
-    virtual bool onPostComposition(const std::optional<DisplayId>& /*displayId*/,
+    virtual bool onPostComposition(sp<const DisplayDevice> /*displayDevice*/,
                                    const std::shared_ptr<FenceTime>& /*glDoneFence*/,
                                    const std::shared_ptr<FenceTime>& /*presentFence*/,
                                    const CompositorTiming& /*compositorTiming*/) {
@@ -599,6 +601,8 @@
 
     virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
 
+    virtual uint64_t getCurrentFrameNumber() const { return mCurrentFrameNumber; }
+
     /*
      * Returns if a frame is ready
      */
@@ -948,7 +952,7 @@
     // The inherited shadow radius after taking into account the layer hierarchy. This is the
     // final shadow radius for this layer. If a shadow is specified for a layer, then effective
     // shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
-    float mEffectiveShadowRadius;
+    float mEffectiveShadowRadius = 0.f;
 
     // Returns true if the layer can draw shadows on its border.
     virtual bool canDrawShadows() const { return true; }
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 15ac8ca..bd9aca1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -28,6 +28,7 @@
 namespace android {
 
 class Layer;
+class TestableScheduler;
 
 namespace scheduler {
 
@@ -59,6 +60,7 @@
 
 private:
     friend class LayerHistoryTest;
+    friend TestableScheduler;
 
     using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
     using LayerInfos = std::vector<LayerPair>;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index f3b0d56..6d9dd43 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -25,6 +25,8 @@
       : mLowRefreshRate(lowRefreshRate), mHighRefreshRate(highRefreshRate) {}
 
 void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+    lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
+
     // Buffers can come with a present time far in the future. That keeps them relevant.
     mLastUpdatedTime = std::max(lastPresentTime, now);
     mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index 4870a3b..a90d05e 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -17,6 +17,7 @@
 #include "OneShotTimer.h"
 
 #include <chrono>
+#include <sstream>
 #include <thread>
 
 namespace android {
@@ -98,7 +99,7 @@
             mTimeoutCallback();
         }
     }
-} // namespace scheduler
+}
 
 void OneShotTimer::reset() {
     {
@@ -108,5 +109,11 @@
     mCondition.notify_all();
 }
 
+std::string OneShotTimer::dump() const {
+    std::ostringstream stream;
+    stream << mInterval.count() << " ms";
+    return stream.str();
+}
+
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index 921631e..b005754 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -39,8 +39,6 @@
                  const TimeoutCallback& timeoutCallback);
     ~OneShotTimer();
 
-    const Interval& interval() const { return mInterval; }
-
     // Initializes and turns on the idle timer.
     void start();
     // Stops the idle timer and any held resources.
@@ -48,6 +46,8 @@
     // Resets the wakeup time and fires the reset callback.
     void reset();
 
+    std::string dump() const;
+
 private:
     // Enum to track in what state is the timer.
     enum class TimerState {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 71b3500..55fd603 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -20,6 +20,7 @@
 
 #include "Scheduler.h"
 
+#include <android-base/stringprintf.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
@@ -66,9 +67,11 @@
         mRefreshRateConfigs(refreshRateConfig) {
     using namespace sysprop;
 
-    char value[PROPERTY_VALUE_MAX];
-    property_get("debug.sf.set_idle_timer_ms", value, "0");
-    const int setIdleTimerMs = atoi(value);
+    if (property_get_bool("debug.sf.use_smart_90_for_video", 0) || use_smart_90_for_video(false)) {
+        mLayerHistory.emplace();
+    }
+
+    const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
 
     if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
         const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback
@@ -327,26 +330,28 @@
 }
 
 void Scheduler::registerLayer(Layer* layer) {
-    uint32_t defaultFps, performanceFps;
-    if (mRefreshRateConfigs.refreshRateSwitchingSupported()) {
-        defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
-        const auto type = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
-                ? RefreshRateType::DEFAULT
-                : RefreshRateType::PERFORMANCE;
-        performanceFps = mRefreshRateConfigs.getRefreshRateFromType(type).fps;
-    } else {
-        defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps;
-        performanceFps = defaultFps;
-    }
-    mLayerHistory.registerLayer(layer, defaultFps, performanceFps);
+    if (!mLayerHistory) return;
+
+    const auto type = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
+            ? RefreshRateType::DEFAULT
+            : RefreshRateType::PERFORMANCE;
+
+    const auto lowFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
+    const auto highFps = mRefreshRateConfigs.getRefreshRateFromType(type).fps;
+
+    mLayerHistory->registerLayer(layer, lowFps, highFps);
 }
 
 void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, bool isHDR) {
-    mLayerHistory.record(layer, presentTime, isHDR, systemTime());
+    if (mLayerHistory) {
+        mLayerHistory->record(layer, presentTime, isHDR, systemTime());
+    }
 }
 
-void Scheduler::updateFpsBasedOnContent() {
-    auto [refreshRate, isHDR] = mLayerHistory.summarize(systemTime());
+void Scheduler::chooseRefreshRateForContent() {
+    if (!mLayerHistory) return;
+
+    auto [refreshRate, isHDR] = mLayerHistory->summarize(systemTime());
     const uint32_t refreshRateRound = std::round(refreshRate);
     RefreshRateType newRefreshRateType;
     {
@@ -393,7 +398,9 @@
 
     // Touch event will boost the refresh rate to performance.
     // Clear Layer History to get fresh FPS detection
-    mLayerHistory.clear();
+    if (mLayerHistory) {
+        mLayerHistory->clear();
+    }
 }
 
 void Scheduler::setDisplayPowerState(bool normal) {
@@ -408,7 +415,9 @@
 
     // Display Power event will boost the refresh rate to performance.
     // Clear Layer History to get fresh FPS detection
-    mLayerHistory.clear();
+    if (mLayerHistory) {
+        mLayerHistory->clear();
+    }
 }
 
 void Scheduler::kernelIdleTimerCallback(TimerState state) {
@@ -446,15 +455,17 @@
 }
 
 void Scheduler::dump(std::string& result) const {
-    std::ostringstream stream;
-    if (mIdleTimer) {
-        stream << "+  Idle timer interval: " << mIdleTimer->interval().count() << " ms\n";
-    }
-    if (mTouchTimer) {
-        stream << "+  Touch timer interval: " << mTouchTimer->interval().count() << " ms\n";
-    }
+    using base::StringAppendF;
+    const char* const states[] = {"off", "on"};
 
-    result.append(stream.str());
+    const bool supported = mRefreshRateConfigs.refreshRateSwitchingSupported();
+    StringAppendF(&result, "+  Refresh rate switching: %s\n", states[supported]);
+    StringAppendF(&result, "+  Content detection: %s\n", states[mLayerHistory.has_value()]);
+
+    StringAppendF(&result, "+  Idle timer: %s\n",
+                  mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
+    StringAppendF(&result, "+  Touch timer: %s\n\n",
+                  mTouchTimer ? mTouchTimer->dump().c_str() : states[0]);
 }
 
 template <class T>
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index c983475..346896c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -105,8 +105,8 @@
     void registerLayer(Layer*);
     void recordLayerHistory(Layer*, nsecs_t presentTime, bool isHDR);
 
-    // Updates FPS based on the most content presented.
-    void updateFpsBasedOnContent();
+    // Detects content using layer history, and selects a matching refresh rate.
+    void chooseRefreshRateForContent();
 
     // Called by Scheduler to change refresh rate.
     void setChangeRefreshRateCallback(ChangeRefreshRateCallback&&);
@@ -184,8 +184,8 @@
     std::unique_ptr<DispSync> mPrimaryDispSync;
     std::unique_ptr<EventControlThread> mEventControlThread;
 
-    // Historical information about individual layers. Used for predicting the refresh rate.
-    scheduler::LayerHistory mLayerHistory;
+    // Used to choose refresh rate if content detection is enabled.
+    std::optional<scheduler::LayerHistory> mLayerHistory;
 
     // Whether to use idle timer callbacks that support the kernel timer.
     const bool mSupportKernelTimer;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 0050495..4a4bef8 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -16,14 +16,9 @@
 
 #pragma once
 
-#include <android-base/thread_annotations.h>
 #include <utils/Timers.h>
 #include <functional>
-#include <memory>
-#include <mutex>
 #include <string>
-#include <string_view>
-#include <unordered_map>
 
 #include "StrongTyping.h"
 
@@ -34,68 +29,6 @@
 enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error };
 enum class CancelResult { Cancelled, TooLate, Error };
 
-namespace impl {
-
-// VSyncDispatchEntry is a helper class representing internal state for each entry in VSyncDispatch
-// hoisted to public for unit testing.
-class VSyncDispatchEntry {
-public:
-    // This is the state of the entry. There are 3 states, armed, running, disarmed.
-    // Valid transition: disarmed -> armed ( when scheduled )
-    // Valid transition: armed -> running -> disarmed ( when timer is called)
-    // Valid transition: armed -> disarmed ( when cancelled )
-    VSyncDispatchEntry(std::string const& name, std::function<void(nsecs_t)> const& fn);
-    std::string_view name() const;
-
-    // Start: functions that are not threadsafe.
-    // Return the last vsync time this callback was invoked.
-    std::optional<nsecs_t> lastExecutedVsyncTarget() const;
-
-    // This moves the state from disarmed->armed and will calculate the wakeupTime.
-    nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
-                     nsecs_t now);
-    // This will update armed entries with the latest vsync information. Entry remains armed.
-    void update(VSyncTracker& tracker, nsecs_t now);
-
-    // This will return empty if not armed, or the next calculated wakeup time if armed.
-    // It will not update the wakeupTime.
-    std::optional<nsecs_t> wakeupTime() const;
-
-    // This moves state from armed->disarmed.
-    void disarm();
-
-    // This moves the state from armed->running.
-    // Store the timestamp that this was intended for as the last called timestamp.
-    nsecs_t executing();
-    // End: functions that are not threadsafe.
-
-    // Invoke the callback with the timestamp, moving the state from running->disarmed.
-    void callback(nsecs_t timestamp);
-    // Block calling thread while the callback is executing.
-    void ensureNotRunning();
-
-private:
-    void arm(VSyncTracker& tracker, nsecs_t now);
-    std::string const mName;
-    std::function<void(nsecs_t)> const mCallback;
-
-    nsecs_t mWorkDuration;
-    nsecs_t mEarliestVsync;
-
-    struct ArmingInfo {
-        nsecs_t mActualWakeupTime;
-        nsecs_t mActualVsyncTime;
-    };
-    std::optional<ArmingInfo> mArmedInfo;
-    std::optional<nsecs_t> mLastDispatchTime;
-
-    std::mutex mRunningMutex;
-    std::condition_variable mCv;
-    bool mRunning GUARDED_BY(mRunningMutex) = false;
-};
-
-} // namespace impl
-
 /*
  * VSyncDispatch is a class that will dispatch callbacks relative to system vsync events.
  */
@@ -103,15 +36,7 @@
 public:
     using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare>;
 
-    /* creates a VsyncDispatch.
-     * \param [in]  a timekeeper object for dispatching events.
-     * \param [in]  a tracker object that is monitoring expected vsync events.
-     * \param [in]  a tunable in nanoseconds that indicates when events that fall close together
-     *              should be dispatched in one timer wakeup.
-     */
-    explicit VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
-                           nsecs_t timerSlack);
-    ~VSyncDispatch();
+    virtual ~VSyncDispatch();
 
     /*
      * Registers a callback that will be called at designated points on the vsync timeline.
@@ -126,8 +51,8 @@
      *                          invocation of callbackFn.
      *
      */
-    CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
-                                   std::string callbackName);
+    virtual CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
+                                           std::string callbackName) = 0;
 
     /*
      * Unregisters a callback.
@@ -135,7 +60,7 @@
      * \param [in] token        The callback to unregister.
      *
      */
-    void unregisterCallback(CallbackToken token);
+    virtual void unregisterCallback(CallbackToken token) = 0;
 
     /*
      * Schedules the registered callback to be dispatched.
@@ -164,7 +89,8 @@
      *                             if a callback was dispatched for the predictedVsync already.
      *                             A ScheduleResult::Error if there was another error.
      */
-    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync);
+    virtual ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
+                                    nsecs_t earliestVsync) = 0;
 
     /* Cancels a scheduled callback, if possible.
      *
@@ -173,31 +99,12 @@
      *                      A CancelResult::Cancelled if the callback was successfully cancelled.
      *                      A CancelResult::Error if there was an pre-condition violation.
      */
-    CancelResult cancel(CallbackToken token);
+    virtual CancelResult cancel(CallbackToken token) = 0;
 
-private:
+protected:
+    VSyncDispatch() = default;
     VSyncDispatch(VSyncDispatch const&) = delete;
     VSyncDispatch& operator=(VSyncDispatch const&) = delete;
-
-    using CallbackMap = std::unordered_map<size_t, std::shared_ptr<impl::VSyncDispatchEntry>>;
-
-    void timerCallback();
-    void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
-    void rearmTimer(nsecs_t now) REQUIRES(mMutex);
-    void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
-            REQUIRES(mMutex);
-    void cancelTimer() REQUIRES(mMutex);
-
-    static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
-    std::unique_ptr<TimeKeeper> const mTimeKeeper;
-    VSyncTracker& mTracker;
-    nsecs_t const mTimerSlack;
-
-    std::mutex mutable mMutex;
-    size_t mCallbackToken GUARDED_BY(mMutex) = 0;
-
-    CallbackMap mCallbacks GUARDED_BY(mMutex);
-    nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
 };
 
 /*
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
similarity index 74%
rename from services/surfaceflinger/Scheduler/VSyncDispatch.cpp
rename to services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index c9b2e77..7922484 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -19,65 +19,66 @@
 #include <vector>
 
 #include "TimeKeeper.h"
-#include "VSyncDispatch.h"
+#include "VSyncDispatchTimerQueue.h"
 #include "VSyncTracker.h"
 
 namespace android::scheduler {
 
+VSyncDispatch::~VSyncDispatch() = default;
 VSyncTracker::~VSyncTracker() = default;
 TimeKeeper::~TimeKeeper() = default;
 
-impl::VSyncDispatchEntry::VSyncDispatchEntry(std::string const& name,
-                                             std::function<void(nsecs_t)> const& cb)
+VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name,
+                                                           std::function<void(nsecs_t)> const& cb)
       : mName(name), mCallback(cb), mWorkDuration(0), mEarliestVsync(0) {}
 
-std::optional<nsecs_t> impl::VSyncDispatchEntry::lastExecutedVsyncTarget() const {
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
     return mLastDispatchTime;
 }
 
-std::string_view impl::VSyncDispatchEntry::name() const {
+std::string_view VSyncDispatchTimerQueueEntry::name() const {
     return mName;
 }
 
-std::optional<nsecs_t> impl::VSyncDispatchEntry::wakeupTime() const {
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const {
     if (!mArmedInfo) {
         return {};
     }
     return {mArmedInfo->mActualWakeupTime};
 }
 
-nsecs_t impl::VSyncDispatchEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
-                                           VSyncTracker& tracker, nsecs_t now) {
+nsecs_t VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
+                                               VSyncTracker& tracker, nsecs_t now) {
     mWorkDuration = workDuration;
     mEarliestVsync = earliestVsync;
     arm(tracker, now);
     return mArmedInfo->mActualWakeupTime;
 }
 
-void impl::VSyncDispatchEntry::update(VSyncTracker& tracker, nsecs_t now) {
+void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
     if (!mArmedInfo) {
         return;
     }
     arm(tracker, now);
 }
 
-void impl::VSyncDispatchEntry::arm(VSyncTracker& tracker, nsecs_t now) {
+void VSyncDispatchTimerQueueEntry::arm(VSyncTracker& tracker, nsecs_t now) {
     auto const nextVsyncTime =
             tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
     mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
 }
 
-void impl::VSyncDispatchEntry::disarm() {
+void VSyncDispatchTimerQueueEntry::disarm() {
     mArmedInfo.reset();
 }
 
-nsecs_t impl::VSyncDispatchEntry::executing() {
+nsecs_t VSyncDispatchTimerQueueEntry::executing() {
     mLastDispatchTime = mArmedInfo->mActualVsyncTime;
     disarm();
     return *mLastDispatchTime;
 }
 
-void impl::VSyncDispatchEntry::callback(nsecs_t t) {
+void VSyncDispatchTimerQueueEntry::callback(nsecs_t t) {
     {
         std::lock_guard<std::mutex> lk(mRunningMutex);
         mRunning = true;
@@ -90,36 +91,37 @@
     mCv.notify_all();
 }
 
-void impl::VSyncDispatchEntry::ensureNotRunning() {
+void VSyncDispatchTimerQueueEntry::ensureNotRunning() {
     std::unique_lock<std::mutex> lk(mRunningMutex);
     mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
 }
 
-VSyncDispatch::VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
-                             nsecs_t timerSlack)
+VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
+                                                 VSyncTracker& tracker, nsecs_t timerSlack)
       : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {}
 
-VSyncDispatch::~VSyncDispatch() {
+VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
     std::lock_guard<decltype(mMutex)> lk(mMutex);
     cancelTimer();
 }
 
-void VSyncDispatch::cancelTimer() {
+void VSyncDispatchTimerQueue::cancelTimer() {
     mIntendedWakeupTime = kInvalidTime;
     mTimeKeeper->alarmCancel();
 }
 
-void VSyncDispatch::setTimer(nsecs_t targetTime, nsecs_t now) {
+void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) {
     mIntendedWakeupTime = targetTime;
-    mTimeKeeper->alarmIn(std::bind(&VSyncDispatch::timerCallback, this), targetTime - now);
+    mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
+                         targetTime - now);
 }
 
-void VSyncDispatch::rearmTimer(nsecs_t now) {
+void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) {
     rearmTimerSkippingUpdateFor(now, mCallbacks.end());
 }
 
-void VSyncDispatch::rearmTimerSkippingUpdateFor(nsecs_t now,
-                                                CallbackMap::iterator const& skipUpdateIt) {
+void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
+        nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
     std::optional<nsecs_t> min;
     for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
         auto& callback = it->second;
@@ -143,9 +145,9 @@
     }
 }
 
-void VSyncDispatch::timerCallback() {
+void VSyncDispatchTimerQueue::timerCallback() {
     struct Invocation {
-        std::shared_ptr<impl::VSyncDispatchEntry> callback;
+        std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
         nsecs_t timestamp;
     };
     std::vector<Invocation> invocations;
@@ -174,18 +176,19 @@
     }
 }
 
-VSyncDispatch::CallbackToken VSyncDispatch::registerCallback(
+VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
         std::function<void(nsecs_t)> const& callbackFn, std::string callbackName) {
     std::lock_guard<decltype(mMutex)> lk(mMutex);
     return CallbackToken{
             mCallbacks
                     .emplace(++mCallbackToken,
-                             std::make_shared<impl::VSyncDispatchEntry>(callbackName, callbackFn))
+                             std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
+                                                                            callbackFn))
                     .first->first};
 }
 
-void VSyncDispatch::unregisterCallback(CallbackToken token) {
-    std::shared_ptr<impl::VSyncDispatchEntry> entry = nullptr;
+void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
+    std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
     {
         std::lock_guard<decltype(mMutex)> lk(mMutex);
         auto it = mCallbacks.find(token);
@@ -200,8 +203,8 @@
     }
 }
 
-ScheduleResult VSyncDispatch::schedule(CallbackToken token, nsecs_t workDuration,
-                                       nsecs_t earliestVsync) {
+ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration,
+                                                 nsecs_t earliestVsync) {
     auto result = ScheduleResult::Error;
     {
         std::lock_guard<decltype(mMutex)> lk(mMutex);
@@ -228,7 +231,7 @@
     return result;
 }
 
-CancelResult VSyncDispatch::cancel(CallbackToken token) {
+CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
     std::lock_guard<decltype(mMutex)> lk(mMutex);
 
     auto it = mCallbacks.find(token);
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
new file mode 100644
index 0000000..f058099
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -0,0 +1,130 @@
+/*
+ * 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 <functional>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+
+#include "VSyncDispatch.h"
+
+namespace android::scheduler {
+
+// VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in
+// VSyncDispatchTimerQueue hoisted to public for unit testing.
+class VSyncDispatchTimerQueueEntry {
+public:
+    // This is the state of the entry. There are 3 states, armed, running, disarmed.
+    // Valid transition: disarmed -> armed ( when scheduled )
+    // Valid transition: armed -> running -> disarmed ( when timer is called)
+    // Valid transition: armed -> disarmed ( when cancelled )
+    VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn);
+    std::string_view name() const;
+
+    // Start: functions that are not threadsafe.
+    // Return the last vsync time this callback was invoked.
+    std::optional<nsecs_t> lastExecutedVsyncTarget() const;
+
+    // This moves the state from disarmed->armed and will calculate the wakeupTime.
+    nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
+                     nsecs_t now);
+    // This will update armed entries with the latest vsync information. Entry remains armed.
+    void update(VSyncTracker& tracker, nsecs_t now);
+
+    // This will return empty if not armed, or the next calculated wakeup time if armed.
+    // It will not update the wakeupTime.
+    std::optional<nsecs_t> wakeupTime() const;
+
+    // This moves state from armed->disarmed.
+    void disarm();
+
+    // This moves the state from armed->running.
+    // Store the timestamp that this was intended for as the last called timestamp.
+    nsecs_t executing();
+    // End: functions that are not threadsafe.
+
+    // Invoke the callback with the timestamp, moving the state from running->disarmed.
+    void callback(nsecs_t timestamp);
+    // Block calling thread while the callback is executing.
+    void ensureNotRunning();
+
+private:
+    void arm(VSyncTracker& tracker, nsecs_t now);
+    std::string const mName;
+    std::function<void(nsecs_t)> const mCallback;
+
+    nsecs_t mWorkDuration;
+    nsecs_t mEarliestVsync;
+
+    struct ArmingInfo {
+        nsecs_t mActualWakeupTime;
+        nsecs_t mActualVsyncTime;
+    };
+    std::optional<ArmingInfo> mArmedInfo;
+    std::optional<nsecs_t> mLastDispatchTime;
+
+    std::mutex mRunningMutex;
+    std::condition_variable mCv;
+    bool mRunning GUARDED_BY(mRunningMutex) = false;
+};
+
+/*
+ * VSyncDispatchTimerQueue is a class that will dispatch callbacks as per VSyncDispatch interface
+ * using a single timer queue.
+ */
+class VSyncDispatchTimerQueue : public VSyncDispatch {
+public:
+    explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
+                                     nsecs_t timerSlack);
+    ~VSyncDispatchTimerQueue();
+
+    CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
+                                   std::string callbackName) final;
+    void unregisterCallback(CallbackToken token) final;
+    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
+    CancelResult cancel(CallbackToken token) final;
+
+private:
+    VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
+    VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete;
+
+    using CallbackMap = std::unordered_map<size_t, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
+
+    void timerCallback();
+    void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
+    void rearmTimer(nsecs_t now) REQUIRES(mMutex);
+    void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
+            REQUIRES(mMutex);
+    void cancelTimer() REQUIRES(mMutex);
+
+    static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
+    std::unique_ptr<TimeKeeper> const mTimeKeeper;
+    VSyncTracker& mTracker;
+    nsecs_t const mTimerSlack;
+
+    std::mutex mutable mMutex;
+    size_t mCallbackToken GUARDED_BY(mMutex) = 0;
+
+    CallbackMap mCallbacks GUARDED_BY(mMutex);
+    nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 76fd51f..b4d748d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -356,14 +356,6 @@
     auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
     mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
 
-    mUseSmart90ForVideo = use_smart_90_for_video(false);
-    property_get("debug.sf.use_smart_90_for_video", value, "0");
-
-    int int_value = atoi(value);
-    if (int_value) {
-        mUseSmart90ForVideo = true;
-    }
-
     property_get("debug.sf.luma_sampling", value, "1");
     mLumaSampling = atoi(value);
 
@@ -1643,11 +1635,7 @@
                 mGpuFrameMissedCount++;
             }
 
-            if (mUseSmart90ForVideo) {
-                // This call is made each time SF wakes up and creates a new frame. It is part
-                // of video detection feature.
-                mScheduler->updateFpsBasedOnContent();
-            }
+            mScheduler->chooseRefreshRateForContent();
 
             if (performSetActiveConfig()) {
                 break;
@@ -1727,7 +1715,12 @@
     refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
     for (sp<Layer> layer : mLayersWithQueuedFrames) {
         auto compositionLayer = layer->getCompositionLayer();
-        if (compositionLayer) refreshArgs.layersWithQueuedFrames.push_back(compositionLayer.get());
+        if (compositionLayer) {
+            refreshArgs.layersWithQueuedFrames.push_back(compositionLayer.get());
+            mFrameTracer->traceTimestamp(layer->getSequence(), layer->getCurrentBufferId(),
+                                         layer->getCurrentFrameNumber(), systemTime(),
+                                         FrameTracer::FrameEvent::HWC_COMPOSITION_QUEUED);
+        }
     }
 
     refreshArgs.repaintEverything = mRepaintEverything.exchange(false);
@@ -1908,9 +1901,8 @@
     }
 
     mDrawingState.traverseInZOrder([&](Layer* layer) {
-        bool frameLatched =
-                layer->onPostComposition(displayDevice->getId(), glCompositionDoneFenceTime,
-                                         presentFenceTime, compositorTiming);
+        bool frameLatched = layer->onPostComposition(displayDevice, glCompositionDoneFenceTime,
+                                                     presentFenceTime, compositorTiming);
         if (frameLatched) {
             recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
         }
@@ -3947,9 +3939,6 @@
 
 void SurfaceFlinger::dumpVSync(std::string& result) const {
     mScheduler->dump(result);
-    StringAppendF(&result, "+  Refresh rate switching: %s\n",
-                  mRefreshRateConfigs->refreshRateSwitchingSupported() ? "on" : "off");
-    StringAppendF(&result, "+  Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off");
 
     mRefreshRateStats->dump(result);
     result.append("\n");
@@ -5502,7 +5491,7 @@
     return nullptr;
 }
 
-void SurfaceFlinger::onLayerCreated(Layer* layer) {
+void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
     mNumLayers++;
     mScheduler->registerLayer(layer);
 }
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e7ad295..50b3ae4 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -310,7 +310,7 @@
     bool authenticateSurfaceTextureLocked(
         const sp<IGraphicBufferProducer>& bufferProducer) const;
 
-    void onLayerCreated(Layer*);
+    void onLayerFirstRef(Layer*);
     void onLayerDestroyed(Layer*);
 
     TransactionCompletedThread& getTransactionCompletedThread() {
@@ -503,9 +503,9 @@
     using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
 
     struct ActiveConfigInfo {
-        RefreshRateType type;
-        int configId;
-        Scheduler::ConfigEvent event;
+        RefreshRateType type = RefreshRateType::DEFAULT;
+        int configId = 0;
+        Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
 
         bool operator!=(const ActiveConfigInfo& other) const {
             return type != other.type || configId != other.configId || event != other.event;
@@ -1093,7 +1093,6 @@
     /* ------------------------------------------------------------------------
      * Scheduler
      */
-    bool mUseSmart90ForVideo = false;
     std::unique_ptr<Scheduler> mScheduler;
     scheduler::ConnectionHandle mAppConnectionHandle;
     scheduler::ConnectionHandle mSfConnectionHandle;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 3e47ec6..611afce 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -113,9 +113,9 @@
     mTimeStats.clientCompositionFrames++;
 }
 
-bool TimeStats::recordReadyLocked(int32_t layerID, TimeRecord* timeRecord) {
+bool TimeStats::recordReadyLocked(int32_t layerId, TimeRecord* timeRecord) {
     if (!timeRecord->ready) {
-        ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerID,
+        ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerId,
               timeRecord->frameTime.frameNumber);
         return false;
     }
@@ -128,7 +128,7 @@
             timeRecord->frameTime.acquireTime = timeRecord->acquireFence->getSignalTime();
             timeRecord->acquireFence = nullptr;
         } else {
-            ALOGV("[%d]-[%" PRIu64 "]-acquireFence signal time is invalid", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-acquireFence signal time is invalid", layerId,
                   timeRecord->frameTime.frameNumber);
         }
     }
@@ -141,7 +141,7 @@
             timeRecord->frameTime.presentTime = timeRecord->presentFence->getSignalTime();
             timeRecord->presentFence = nullptr;
         } else {
-            ALOGV("[%d]-[%" PRIu64 "]-presentFence signal time invalid", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-presentFence signal time invalid", layerId,
                   timeRecord->frameTime.frameNumber);
         }
     }
@@ -155,15 +155,15 @@
     return static_cast<int32_t>(delta);
 }
 
-void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerID) {
+void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId) {
     ATRACE_CALL();
 
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
     std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
     while (!timeRecords.empty()) {
-        if (!recordReadyLocked(layerID, &timeRecords[0])) break;
-        ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerID,
+        if (!recordReadyLocked(layerId, &timeRecords[0])) break;
+        ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerId,
               timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime);
 
         if (prevTimeRecord.ready) {
@@ -178,37 +178,37 @@
 
             const int32_t postToAcquireMs = msBetween(timeRecords[0].frameTime.postTime,
                                                       timeRecords[0].frameTime.acquireTime);
-            ALOGV("[%d]-[%" PRIu64 "]-post2acquire[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-post2acquire[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, postToAcquireMs);
             timeStatsLayer.deltas["post2acquire"].insert(postToAcquireMs);
 
             const int32_t postToPresentMs = msBetween(timeRecords[0].frameTime.postTime,
                                                       timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-post2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-post2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, postToPresentMs);
             timeStatsLayer.deltas["post2present"].insert(postToPresentMs);
 
             const int32_t acquireToPresentMs = msBetween(timeRecords[0].frameTime.acquireTime,
                                                          timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, acquireToPresentMs);
             timeStatsLayer.deltas["acquire2present"].insert(acquireToPresentMs);
 
             const int32_t latchToPresentMs = msBetween(timeRecords[0].frameTime.latchTime,
                                                        timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, latchToPresentMs);
             timeStatsLayer.deltas["latch2present"].insert(latchToPresentMs);
 
             const int32_t desiredToPresentMs = msBetween(timeRecords[0].frameTime.desiredTime,
                                                          timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, desiredToPresentMs);
             timeStatsLayer.deltas["desired2present"].insert(desiredToPresentMs);
 
             const int32_t presentToPresentMs = msBetween(prevTimeRecord.frameTime.presentTime,
                                                          timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, presentToPresentMs);
             timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
         }
@@ -227,28 +227,28 @@
             layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0;
 }
 
-void TimeStats::setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                             nsecs_t postTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerID, frameNumber, layerName.c_str(),
+    ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerId, frameNumber, layerName.c_str(),
           postTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
     if (!mTimeStats.stats.count(layerName) && mTimeStats.stats.size() >= MAX_NUM_LAYER_STATS) {
         return;
     }
-    if (!mTimeStatsTracker.count(layerID) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
+    if (!mTimeStatsTracker.count(layerId) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
         layerNameIsValid(layerName)) {
-        mTimeStatsTracker[layerID].layerName = layerName;
+        mTimeStatsTracker[layerId].layerName = layerName;
     }
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) {
         ALOGE("[%d]-[%s]-timeRecords is at its maximum size[%zu]. Ignore this when unittesting.",
-              layerID, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS);
-        mTimeStatsTracker.erase(layerID);
+              layerId, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS);
+        mTimeStatsTracker.erase(layerId);
         return;
     }
     // For most media content, the acquireFence is invalid because the buffer is
@@ -270,15 +270,15 @@
         layerRecord.waitData = layerRecord.timeRecords.size() - 1;
 }
 
-void TimeStats::setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) {
+void TimeStats::setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerID, frameNumber, latchTime);
+    ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerId, frameNumber, latchTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -288,15 +288,15 @@
     }
 }
 
-void TimeStats::setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) {
+void TimeStats::setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerID, frameNumber, desiredTime);
+    ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerId, frameNumber, desiredTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -306,15 +306,15 @@
     }
 }
 
-void TimeStats::setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) {
+void TimeStats::setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerID, frameNumber, acquireTime);
+    ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerId, frameNumber, acquireTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -324,17 +324,17 @@
     }
 }
 
-void TimeStats::setAcquireFence(int32_t layerID, uint64_t frameNumber,
+void TimeStats::setAcquireFence(int32_t layerId, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& acquireFence) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerID, frameNumber,
+    ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerId, frameNumber,
           acquireFence->getSignalTime());
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -344,15 +344,15 @@
     }
 }
 
-void TimeStats::setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) {
+void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerID, frameNumber, presentTime);
+    ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerId, frameNumber, presentTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -363,20 +363,20 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerID);
+    flushAvailableRecordsToStatsLocked(layerId);
 }
 
-void TimeStats::setPresentFence(int32_t layerID, uint64_t frameNumber,
+void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
                                 const std::shared_ptr<FenceTime>& presentFence) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerID, frameNumber,
+    ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerId, frameNumber,
           presentFence->getSignalTime());
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -387,25 +387,25 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerID);
+    flushAvailableRecordsToStatsLocked(layerId);
 }
 
-void TimeStats::onDestroy(int32_t layerID) {
+void TimeStats::onDestroy(int32_t layerId) {
     ATRACE_CALL();
-    ALOGV("[%d]-onDestroy", layerID);
+    ALOGV("[%d]-onDestroy", layerId);
     std::lock_guard<std::mutex> lock(mMutex);
-    mTimeStatsTracker.erase(layerID);
+    mTimeStatsTracker.erase(layerId);
 }
 
-void TimeStats::removeTimeRecord(int32_t layerID, uint64_t frameNumber) {
+void TimeStats::removeTimeRecord(int32_t layerId, uint64_t frameNumber) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerID, frameNumber);
+    ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerId, frameNumber);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     size_t removeAt = 0;
     for (const TimeRecord& record : layerRecord.timeRecords) {
         if (record.frameTime.frameNumber == frameNumber) break;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 1313132..6e71f5a 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -44,20 +44,20 @@
     virtual void incrementMissedFrames() = 0;
     virtual void incrementClientCompositionFrames() = 0;
 
-    virtual void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+    virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                              nsecs_t postTime) = 0;
-    virtual void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) = 0;
-    virtual void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) = 0;
-    virtual void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) = 0;
-    virtual void setAcquireFence(int32_t layerID, uint64_t frameNumber,
+    virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
+    virtual void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) = 0;
+    virtual void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) = 0;
+    virtual void setAcquireFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& acquireFence) = 0;
-    virtual void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) = 0;
-    virtual void setPresentFence(int32_t layerID, uint64_t frameNumber,
+    virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0;
+    virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& presentFence) = 0;
     // Clean up the layer record
-    virtual void onDestroy(int32_t layerID) = 0;
+    virtual void onDestroy(int32_t layerId) = 0;
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
-    virtual void removeTimeRecord(int32_t layerID, uint64_t frameNumber) = 0;
+    virtual void removeTimeRecord(int32_t layerId, uint64_t frameNumber) = 0;
 
     virtual void setPowerMode(int32_t powerMode) = 0;
     // Source of truth is RefrehRateStats.
@@ -116,20 +116,20 @@
     void incrementMissedFrames() override;
     void incrementClientCompositionFrames() override;
 
-    void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+    void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                      nsecs_t postTime) override;
-    void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) override;
-    void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) override;
-    void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) override;
-    void setAcquireFence(int32_t layerID, uint64_t frameNumber,
+    void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
+    void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) override;
+    void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) override;
+    void setAcquireFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& acquireFence) override;
-    void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) override;
-    void setPresentFence(int32_t layerID, uint64_t frameNumber,
+    void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) override;
+    void setPresentFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& presentFence) override;
     // Clean up the layer record
-    void onDestroy(int32_t layerID) override;
+    void onDestroy(int32_t layerId) override;
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
-    void removeTimeRecord(int32_t layerID, uint64_t frameNumber) override;
+    void removeTimeRecord(int32_t layerId, uint64_t frameNumber) override;
 
     void setPowerMode(int32_t powerMode) override;
     // Source of truth is RefrehRateStats.
@@ -139,8 +139,8 @@
     static const size_t MAX_NUM_TIME_RECORDS = 64;
 
 private:
-    bool recordReadyLocked(int32_t layerID, TimeRecord* timeRecord);
-    void flushAvailableRecordsToStatsLocked(int32_t layerID);
+    bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
+    void flushAvailableRecordsToStatsLocked(int32_t layerId);
     void flushPowerTimeLocked();
     void flushAvailableGlobalRecordsToStatsLocked();
 
@@ -152,7 +152,7 @@
     std::atomic<bool> mEnabled = false;
     std::mutex mMutex;
     TimeStatsHelper::TimeStatsGlobal mTimeStats;
-    // Hashmap for LayerRecord with layerID as the hash key
+    // Hashmap for LayerRecord with layerId as the hash key
     std::unordered_map<int32_t, LayerRecord> mTimeStatsTracker;
     PowerTime mPowerTime;
     GlobalRecord mGlobalRecord;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 246a62f..78114a1 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -54,7 +54,7 @@
         "FrameTracerTest.cpp",
         "TransactionApplicationTest.cpp",
         "StrongTypingTest.cpp",
-        "VSyncDispatchTest.cpp",
+        "VSyncDispatchTimerQueueTest.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 143a7a0..32f997f 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -168,6 +168,7 @@
 
     std::unordered_set<HWC2::Capability> mDefaultCapabilities = {HWC2::Capability::SidebandStream};
 
+    bool mDisplayOff = false;
     TestableSurfaceFlinger mFlinger;
     sp<DisplayDevice> mDisplay;
     sp<DisplayDevice> mExternalDisplay;
@@ -534,69 +535,85 @@
     }
 
     static void setupHwcSetGeometryCallExpectations(CompositionTest* test) {
-        // TODO: Coverage of other values
-        EXPECT_CALL(*test->mComposer,
-                    setLayerBlendMode(HWC_DISPLAY, HWC_LAYER, LayerProperties::BLENDMODE))
-                .Times(1);
-        // TODO: Coverage of other values for origin
-        EXPECT_CALL(*test->mComposer,
-                    setLayerDisplayFrame(HWC_DISPLAY, HWC_LAYER,
-                                         IComposerClient::Rect({0, 0, LayerProperties::WIDTH,
-                                                                LayerProperties::HEIGHT})))
-                .Times(1);
-        EXPECT_CALL(*test->mComposer,
-                    setLayerPlaneAlpha(HWC_DISPLAY, HWC_LAYER, LayerProperties::COLOR[3]))
-                .Times(1);
-        // TODO: Coverage of other values
-        EXPECT_CALL(*test->mComposer, setLayerZOrder(HWC_DISPLAY, HWC_LAYER, 0u)).Times(1);
-        // TODO: Coverage of other values
-        EXPECT_CALL(*test->mComposer, setLayerInfo(HWC_DISPLAY, HWC_LAYER, 0u, 0u)).Times(1);
+        if (!test->mDisplayOff) {
+            // TODO: Coverage of other values
+            EXPECT_CALL(*test->mComposer,
+                        setLayerBlendMode(HWC_DISPLAY, HWC_LAYER, LayerProperties::BLENDMODE))
+                    .Times(1);
+            // TODO: Coverage of other values for origin
+            EXPECT_CALL(*test->mComposer,
+                        setLayerDisplayFrame(HWC_DISPLAY, HWC_LAYER,
+                                             IComposerClient::Rect({0, 0, LayerProperties::WIDTH,
+                                                                    LayerProperties::HEIGHT})))
+                    .Times(1);
+            EXPECT_CALL(*test->mComposer,
+                        setLayerPlaneAlpha(HWC_DISPLAY, HWC_LAYER, LayerProperties::COLOR[3]))
+                    .Times(1);
+            // TODO: Coverage of other values
+            EXPECT_CALL(*test->mComposer, setLayerZOrder(HWC_DISPLAY, HWC_LAYER, 0u)).Times(1);
+            // TODO: Coverage of other values
+            EXPECT_CALL(*test->mComposer, setLayerInfo(HWC_DISPLAY, HWC_LAYER, 0u, 0u)).Times(1);
 
-        // These expectations retire on saturation as the code path these
-        // expectations are for appears to make an extra call to them.
-        // TODO: Investigate this extra call
-        EXPECT_CALL(*test->mComposer, setLayerTransform(HWC_DISPLAY, HWC_LAYER, DEFAULT_TRANSFORM))
-                .Times(AtLeast(1))
-                .RetiresOnSaturation();
+            // These expectations retire on saturation as the code path these
+            // expectations are for appears to make an extra call to them.
+            // TODO: Investigate this extra call
+            EXPECT_CALL(*test->mComposer,
+                        setLayerTransform(HWC_DISPLAY, HWC_LAYER, DEFAULT_TRANSFORM))
+                    .Times(AtLeast(1))
+                    .RetiresOnSaturation();
+        }
     }
 
     static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
-                                       IComposerClient::FRect({0.f, 0.f, LayerProperties::WIDTH,
-                                                               LayerProperties::HEIGHT})))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
+                                           IComposerClient::FRect({0.f, 0.f, LayerProperties::WIDTH,
+                                                                   LayerProperties::HEIGHT})))
+                    .Times(1);
+        }
     }
 
     static void setupHwcSetSourceCropColorCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
-                                       IComposerClient::FRect({0.f, 0.f, 0.f, 0.f})))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerSourceCrop(HWC_DISPLAY, HWC_LAYER,
+                                           IComposerClient::FRect({0.f, 0.f, 0.f, 0.f})))
+                    .Times(1);
+        }
     }
 
     static void setupHwcSetPerFrameCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerVisibleRegion(HWC_DISPLAY, HWC_LAYER,
-                                          std::vector<IComposerClient::Rect>({IComposerClient::Rect(
-                                                  {0, 0, LayerProperties::WIDTH,
-                                                   LayerProperties::HEIGHT})})))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerVisibleRegion(HWC_DISPLAY, HWC_LAYER,
+                                              std::vector<IComposerClient::Rect>(
+                                                      {IComposerClient::Rect(
+                                                              {0, 0, LayerProperties::WIDTH,
+                                                               LayerProperties::HEIGHT})})))
+                    .Times(1);
+        }
     }
 
     static void setupHwcSetPerFrameColorCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _))
+                    .Times(1);
 
-        // TODO: use COLOR
-        EXPECT_CALL(*test->mComposer,
-                    setLayerColor(HWC_DISPLAY, HWC_LAYER,
-                                  IComposerClient::Color({0xff, 0xff, 0xff, 0xff})))
-                .Times(1);
+            // TODO: use COLOR
+            EXPECT_CALL(*test->mComposer,
+                        setLayerColor(HWC_DISPLAY, HWC_LAYER,
+                                      IComposerClient::Color({0xff, 0xff, 0xff, 0xff})))
+                    .Times(1);
+        }
     }
 
     static void setupHwcSetPerFrameBufferCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _)).Times(1);
-        EXPECT_CALL(*test->mComposer, setLayerBuffer(HWC_DISPLAY, HWC_LAYER, _, _, _)).Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _))
+                    .Times(1);
+            EXPECT_CALL(*test->mComposer, setLayerBuffer(HWC_DISPLAY, HWC_LAYER, _, _, _)).Times(1);
+        }
 
         setupBufferLayerPostFrameCallExpectations(test);
     }
@@ -793,6 +810,9 @@
 
         sp<L> layer = factory();
 
+        // Layer should be registered with scheduler.
+        EXPECT_EQ(1, test->mFlinger.scheduler()->layerHistorySize());
+
         Mock::VerifyAndClear(test->mComposer);
         Mock::VerifyAndClear(test->mRenderEngine);
         Mock::VerifyAndClear(test->mMessageQueue);
@@ -828,6 +848,10 @@
 
         test->mDisplay->getCompositionDisplay()->clearOutputLayers();
         test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
+
+        // Layer should be unregistered with scheduler.
+        test->mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
+        EXPECT_EQ(0, test->mFlinger.scheduler()->layerHistorySize());
     }
 };
 
@@ -940,9 +964,11 @@
     static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(CompositionType);
 
     static void setupHwcSetCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, CompositionType))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, CompositionType))
+                    .Times(1);
+        }
     }
 
     static void setupHwcGetCallExpectations(CompositionTest* test) {
@@ -956,9 +982,11 @@
     static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(FinalCompositionType);
 
     static void setupHwcSetCallExpectations(CompositionTest* test) {
-        EXPECT_CALL(*test->mComposer,
-                    setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, InitialCompositionType))
-                .Times(1);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, InitialCompositionType))
+                    .Times(1);
+        }
     }
 
     static void setupHwcGetCallExpectations(CompositionTest* test) {
@@ -1341,6 +1369,7 @@
  */
 
 TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyGeometry) {
+    mDisplayOff = true;
     displayRefreshCompositionDirtyGeometry<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
             KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
@@ -1348,6 +1377,7 @@
 }
 
 TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyFrame) {
+    mDisplayOff = true;
     displayRefreshCompositionDirtyFrame<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
             KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
@@ -1355,6 +1385,7 @@
 }
 
 TEST_F(CompositionTest, displayOffREComposedNormalBufferLayer) {
+    mDisplayOff = true;
     displayRefreshCompositionDirtyFrame<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
             ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
diff --git a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
index b5af591..c334bcf 100644
--- a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
@@ -82,8 +82,8 @@
               "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
 
     const std::string layerName = "co.layername#0";
-    const int32_t layerID = 5;
-    mFrameTracer->traceNewLayer(layerID, layerName);
+    const int32_t layerId = 5;
+    mFrameTracer->traceNewLayer(layerId, layerName);
 
     EXPECT_EQ(mFrameTracer->miniDump(),
               "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
@@ -92,7 +92,7 @@
     tracingSession->StartBlocking();
     EXPECT_EQ(mFrameTracer->miniDump(),
               "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
-    mFrameTracer->traceNewLayer(layerID, layerName);
+    mFrameTracer->traceNewLayer(layerId, layerName);
     EXPECT_EQ(mFrameTracer->miniDump(),
               "FrameTracer miniDump:\nNumber of layers currently being traced is 1\n");
     tracingSession->StopBlocking();
@@ -103,31 +103,31 @@
               "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
 
     const std::string layerName = "co.layername#0";
-    const int32_t layerID = 5;
-    const int32_t secondLayerID = 6;
+    const int32_t layerId = 5;
+    const int32_t secondlayerId = 6;
 
     auto tracingSession = getTracingSessionForTest();
     tracingSession->StartBlocking();
-    mFrameTracer->traceNewLayer(layerID, layerName);
-    mFrameTracer->traceNewLayer(secondLayerID, layerName);
+    mFrameTracer->traceNewLayer(layerId, layerName);
+    mFrameTracer->traceNewLayer(secondlayerId, layerName);
     EXPECT_EQ(mFrameTracer->miniDump(),
               "FrameTracer miniDump:\nNumber of layers currently being traced is 2\n");
     tracingSession->StopBlocking();
 
-    mFrameTracer->onDestroy(layerID);
+    mFrameTracer->onDestroy(layerId);
     EXPECT_EQ(mFrameTracer->miniDump(),
               "FrameTracer miniDump:\nNumber of layers currently being traced is 1\n");
-    mFrameTracer->onDestroy(layerID);
+    mFrameTracer->onDestroy(layerId);
     EXPECT_EQ(mFrameTracer->miniDump(),
               "FrameTracer miniDump:\nNumber of layers currently being traced is 1\n");
-    mFrameTracer->onDestroy(secondLayerID);
+    mFrameTracer->onDestroy(secondlayerId);
     EXPECT_EQ(mFrameTracer->miniDump(),
               "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
 }
 
 TEST_F(FrameTracerTest, canTraceAfterAddingLayer) {
     const std::string layerName = "co.layername#0";
-    const int32_t layerID = 1;
+    const int32_t layerId = 1;
     const uint32_t bufferID = 2;
     const uint64_t frameNumber = 3;
     const nsecs_t timestamp = 4;
@@ -141,9 +141,9 @@
         // Clean up irrelevant traces.
         tracingSession->ReadTraceBlocking();
 
-        mFrameTracer->traceTimestamp(layerID, bufferID, frameNumber, timestamp, type, duration);
+        mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration);
         // Create second trace packet to finalize the previous one.
-        mFrameTracer->traceTimestamp(layerID, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+        mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
         tracingSession->StopBlocking();
 
         std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
@@ -157,10 +157,10 @@
         // Clean up irrelevant traces.
         tracingSession->ReadTraceBlocking();
 
-        mFrameTracer->traceNewLayer(layerID, layerName);
-        mFrameTracer->traceTimestamp(layerID, bufferID, frameNumber, timestamp, type, duration);
+        mFrameTracer->traceNewLayer(layerId, layerName);
+        mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration);
         // Create second trace packet to finalize the previous one.
-        mFrameTracer->traceTimestamp(layerID, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+        mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
         tracingSession->StopBlocking();
 
         std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
@@ -191,7 +191,7 @@
 
 TEST_F(FrameTracerTest, traceFenceTriggersOnNextTraceAfterFenceFired) {
     const std::string layerName = "co.layername#0";
-    const int32_t layerID = 5;
+    const int32_t layerId = 5;
     const uint32_t bufferID = 4;
     const uint64_t frameNumber = 3;
     const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
@@ -204,10 +204,10 @@
         // Clean up irrelevant traces.
         tracingSession->ReadTraceBlocking();
         // Trace.
-        mFrameTracer->traceNewLayer(layerID, layerName);
-        mFrameTracer->traceFence(layerID, bufferID, frameNumber, fenceTime, type);
+        mFrameTracer->traceNewLayer(layerId, layerName);
+        mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type);
         // Create extra trace packet to (hopefully not) trigger and finalize the fence packet.
-        mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+        mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
         tracingSession->StopBlocking();
         std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
         EXPECT_EQ(raw_trace.size(), 0);
@@ -219,12 +219,12 @@
         tracingSession->StartBlocking();
         // Clean up irrelevant traces.
         tracingSession->ReadTraceBlocking();
-        mFrameTracer->traceNewLayer(layerID, layerName);
-        mFrameTracer->traceFence(layerID, bufferID, frameNumber, fenceTime, type);
+        mFrameTracer->traceNewLayer(layerId, layerName);
+        mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type);
         const nsecs_t timestamp = systemTime();
         fenceFactory.signalAllForTest(Fence::NO_FENCE, timestamp);
         // Create extra trace packet to trigger and finalize fence trace packets.
-        mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+        mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
         tracingSession->StopBlocking();
 
         std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
@@ -254,7 +254,7 @@
 
 TEST_F(FrameTracerTest, traceFenceWithStartTimeAfterSignalTime_ShouldHaveNoDuration) {
     const std::string layerName = "co.layername#0";
-    const int32_t layerID = 5;
+    const int32_t layerId = 5;
     const uint32_t bufferID = 4;
     const uint64_t frameNumber = 3;
     const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
@@ -264,24 +264,24 @@
     tracingSession->StartBlocking();
     // Clean up irrelevant traces.
     tracingSession->ReadTraceBlocking();
-    mFrameTracer->traceNewLayer(layerID, layerName);
+    mFrameTracer->traceNewLayer(layerId, layerName);
 
     // traceFence called after fence signalled.
     const nsecs_t signalTime1 = systemTime();
     const nsecs_t startTime1 = signalTime1 + 100000;
     auto fence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime1);
-    mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence1, type, startTime1);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence1, type, startTime1);
 
     // traceFence called before fence signalled.
     const nsecs_t signalTime2 = systemTime();
     const nsecs_t startTime2 = signalTime2 + 100000;
     auto fence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
-    mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence2, type, startTime2);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence2, type, startTime2);
     fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime2);
 
     // Create extra trace packet to trigger and finalize fence trace packets.
-    mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+    mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
     tracingSession->StopBlocking();
 
     std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
@@ -309,7 +309,7 @@
 
 TEST_F(FrameTracerTest, traceFenceOlderThanDeadline_ShouldBeIgnored) {
     const std::string layerName = "co.layername#0";
-    const int32_t layerID = 5;
+    const int32_t layerId = 5;
     const uint32_t bufferID = 4;
     const uint64_t frameNumber = 3;
     const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
@@ -321,11 +321,11 @@
     tracingSession->StartBlocking();
     // Clean up irrelevant traces.
     tracingSession->ReadTraceBlocking();
-    mFrameTracer->traceNewLayer(layerID, layerName);
-    mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence, type);
+    mFrameTracer->traceNewLayer(layerId, layerName);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence, type);
     fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime);
     // Create extra trace packet to trigger and finalize any previous fence packets.
-    mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+    mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
     tracingSession->StopBlocking();
 
     std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
@@ -334,7 +334,7 @@
 
 TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) {
     const std::string layerName = "co.layername#0";
-    const int32_t layerID = 5;
+    const int32_t layerId = 5;
     const uint32_t bufferID = 4;
     const uint64_t frameNumber = 3;
     const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
@@ -345,24 +345,24 @@
     tracingSession->StartBlocking();
     // Clean up irrelevant traces.
     tracingSession->ReadTraceBlocking();
-    mFrameTracer->traceNewLayer(layerID, layerName);
+    mFrameTracer->traceNewLayer(layerId, layerName);
 
     // traceFence called after fence signalled.
     const nsecs_t signalTime1 = systemTime();
     const nsecs_t startTime1 = signalTime1 - duration;
     auto fence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime1);
-    mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence1, type, startTime1);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence1, type, startTime1);
 
     // traceFence called before fence signalled.
     const nsecs_t signalTime2 = systemTime();
     const nsecs_t startTime2 = signalTime2 - duration;
     auto fence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
-    mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence2, type, startTime2);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence2, type, startTime2);
     fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime2);
 
     // Create extra trace packet to trigger and finalize fence trace packets.
-    mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+    mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
     tracingSession->StopBlocking();
 
     std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 9a962bc..e93d31e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -29,10 +29,10 @@
 
     LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
 
-    LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
-    const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
+    LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
+    const LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
 
-    size_t layerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mLayerInfos.size(); }
+    size_t layerCount() const { return mScheduler->layerHistorySize(); }
     size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
 
     size_t frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index ae6aa89..40c00c4 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -21,6 +21,7 @@
 
 #include "Scheduler/DispSync.h"
 #include "Scheduler/EventThread.h"
+#include "Scheduler/LayerHistory.h"
 #include "Scheduler/Scheduler.h"
 
 namespace android {
@@ -28,18 +29,26 @@
 class TestableScheduler : public Scheduler {
 public:
     explicit TestableScheduler(const scheduler::RefreshRateConfigs& configs)
-          : Scheduler([](bool) {}, configs) {}
+          : Scheduler([](bool) {}, configs) {
+        mLayerHistory.emplace();
+    }
 
     TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
                       std::unique_ptr<EventControlThread> eventControlThread,
                       const scheduler::RefreshRateConfigs& configs)
-          : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs) {}
+          : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs) {
+        mLayerHistory.emplace();
+    }
 
     // Used to inject mock event thread.
     ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
         return Scheduler::createConnection(std::move(eventThread));
     }
 
+    size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
+        return mLayerHistory->mLayerInfos.size();
+    }
+
     /* ------------------------------------------------------------------------
      * Read-write access to private data to set up preconditions and assert
      * post-conditions.
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 4eb9ec3..7b60fa2 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -170,8 +170,8 @@
     return result;
 }
 
-static std::string genLayerName(int32_t layerID) {
-    return (layerID < 0 ? "PopupWindow:b54fcd1#0" : "com.dummy#") + std::to_string(layerID);
+static std::string genLayerName(int32_t layerId) {
+    return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.dummy#") + std::to_string(layerId);
 }
 
 void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
@@ -560,22 +560,22 @@
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
     for (size_t i = 0; i < 10000000; ++i) {
-        const int32_t layerID = genRandomInt32(-1, 10);
+        const int32_t layerId = genRandomInt32(-1, 10);
         const int32_t frameNumber = genRandomInt32(1, 10);
         switch (genRandomInt32(0, 100)) {
             case 0:
                 ALOGV("removeTimeRecord");
-                ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(layerID, frameNumber));
+                ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(layerId, frameNumber));
                 continue;
             case 1:
                 ALOGV("onDestroy");
-                ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(layerID));
+                ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(layerId));
                 continue;
         }
         TimeStamp type = static_cast<TimeStamp>(genRandomInt32(TIME_STAMP_BEGIN, TIME_STAMP_END));
         const int32_t ts = genRandomInt32(1, 1000000000);
-        ALOGV("type[%d], layerID[%d], frameNumber[%d], ts[%d]", type, layerID, frameNumber, ts);
-        setTimeStamp(type, layerID, frameNumber, ts);
+        ALOGV("type[%d], layerId[%d], frameNumber[%d], ts[%d]", type, layerId, frameNumber, ts);
+        setTimeStamp(type, layerId, frameNumber, ts);
     }
 }
 
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
similarity index 86%
rename from services/surfaceflinger/tests/unittests/VSyncDispatchTest.cpp
rename to services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index d1ed7e3..82950b5 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -19,7 +19,7 @@
 #define LOG_NDEBUG 0
 
 #include "Scheduler/TimeKeeper.h"
-#include "Scheduler/VSyncDispatch.h"
+#include "Scheduler/VSyncDispatchTimerQueue.h"
 #include "Scheduler/VSyncTracker.h"
 
 #include <gmock/gmock.h>
@@ -166,7 +166,7 @@
     std::chrono::milliseconds const mPauseAmount;
 };
 
-class VSyncDispatchTest : public testing::Test {
+class VSyncDispatchTimerQueueTest : public testing::Test {
 protected:
     std::unique_ptr<TimeKeeper> createTimeKeeper() {
         class TimeKeeperWrapper : public TimeKeeper {
@@ -184,7 +184,7 @@
         return std::make_unique<TimeKeeperWrapper>(mMockClock);
     }
 
-    ~VSyncDispatchTest() {
+    ~VSyncDispatchTimerQueueTest() {
         // destructor of  dispatch will cancelAlarm(). Ignore final cancel in common test.
         Mock::VerifyAndClearExpectations(&mMockClock);
     }
@@ -195,20 +195,21 @@
     static nsecs_t constexpr mDispatchGroupThreshold = 5;
     nsecs_t const mPeriod = 1000;
     NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
-    VSyncDispatch mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold};
+    VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold};
 };
 
-TEST_F(VSyncDispatchTest, unregistersSetAlarmOnDestruction) {
+TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
     EXPECT_CALL(mMockClock, alarmIn(_, 900));
     EXPECT_CALL(mMockClock, alarmCancel());
     {
-        VSyncDispatch mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold};
+        VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker,
+                                          mDispatchGroupThreshold};
         CountingCallback cb(mDispatch);
         EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled);
     }
 }
 
-TEST_F(VSyncDispatchTest, basicAlarmSettingFuture) {
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) {
     auto intended = mPeriod - 230;
     EXPECT_CALL(mMockClock, alarmIn(_, 900));
 
@@ -220,7 +221,7 @@
     EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
 }
 
-TEST_F(VSyncDispatchTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
     EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
     EXPECT_CALL(mMockClock, alarmIn(_, 1050));
 
@@ -232,7 +233,7 @@
     EXPECT_THAT(cb.mCalls[0], Eq(1150));
 }
 
-TEST_F(VSyncDispatchTest, basicAlarmSettingAdjustmentPast) {
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingAdjustmentPast) {
     auto const now = 234;
     mMockClock.advanceBy(234);
     auto const workDuration = 10 * mPeriod;
@@ -244,7 +245,7 @@
     EXPECT_EQ(mDispatch.schedule(cb, workDuration, mPeriod), ScheduleResult::Scheduled);
 }
 
-TEST_F(VSyncDispatchTest, basicAlarmCancel) {
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
     EXPECT_CALL(mMockClock, alarmIn(_, 900));
     EXPECT_CALL(mMockClock, alarmCancel());
 
@@ -253,7 +254,7 @@
     EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
 }
 
-TEST_F(VSyncDispatchTest, basicAlarmCancelTooLate) {
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) {
     EXPECT_CALL(mMockClock, alarmIn(_, 900));
     EXPECT_CALL(mMockClock, alarmCancel());
 
@@ -263,7 +264,7 @@
     EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
 }
 
-TEST_F(VSyncDispatchTest, basicAlarmCancelTooLateWhenRunning) {
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) {
     EXPECT_CALL(mMockClock, alarmIn(_, 900));
     EXPECT_CALL(mMockClock, alarmCancel());
 
@@ -277,7 +278,7 @@
     pausingThread.join();
 }
 
-TEST_F(VSyncDispatchTest, unregisterSynchronizes) {
+TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) {
     EXPECT_CALL(mMockClock, alarmIn(_, 900));
     EXPECT_CALL(mMockClock, alarmCancel());
 
@@ -299,7 +300,7 @@
     EXPECT_TRUE(cb.resourcePresent());
 }
 
-TEST_F(VSyncDispatchTest, basicTwoAlarmSetting) {
+TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) {
     EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
             .Times(4)
             .WillOnce(Return(1055))
@@ -327,7 +328,7 @@
     EXPECT_THAT(cb1.mCalls[0], Eq(1063));
 }
 
-TEST_F(VSyncDispatchTest, rearmsFaroutTimeoutWhenCancellingCloseOne) {
+TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) {
     EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
             .Times(4)
             .WillOnce(Return(10000))
@@ -348,7 +349,7 @@
     mDispatch.cancel(cb1);
 }
 
-TEST_F(VSyncDispatchTest, noUnnecessaryRearmsWhenRescheduling) {
+TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) {
     Sequence seq;
     EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
     EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
@@ -362,7 +363,7 @@
     advanceToNextCallback();
 }
 
-TEST_F(VSyncDispatchTest, necessaryRearmsWhenModifying) {
+TEST_F(VSyncDispatchTimerQueueTest, necessaryRearmsWhenModifying) {
     Sequence seq;
     EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
     EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
@@ -377,7 +378,7 @@
     advanceToNextCallback();
 }
 
-TEST_F(VSyncDispatchTest, modifyIntoGroup) {
+TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) {
     Sequence seq;
     EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
     EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
@@ -412,7 +413,7 @@
     EXPECT_THAT(cb0.mCalls[1], Eq(2000));
 }
 
-TEST_F(VSyncDispatchTest, rearmsWhenEndingAndDoesntCancel) {
+TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenEndingAndDoesntCancel) {
     EXPECT_CALL(mMockClock, alarmIn(_, 900));
     EXPECT_CALL(mMockClock, alarmIn(_, 800));
     EXPECT_CALL(mMockClock, alarmIn(_, 100));
@@ -427,7 +428,7 @@
     EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled);
 }
 
-TEST_F(VSyncDispatchTest, setAlarmCallsAtCorrectTimeWithChangingVsync) {
+TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) {
     EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
             .Times(3)
             .WillOnce(Return(950))
@@ -451,7 +452,7 @@
     EXPECT_THAT(cb.mCalls.size(), Eq(3));
 }
 
-TEST_F(VSyncDispatchTest, callbackReentrancy) {
+TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) {
     Sequence seq;
     EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
     EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
@@ -463,7 +464,7 @@
     advanceToNextCallback();
 }
 
-TEST_F(VSyncDispatchTest, callbackReentrantWithPastWakeup) {
+TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) {
     VSyncDispatch::CallbackToken tmp;
     tmp = mDispatch.registerCallback(
             [&](auto) {
@@ -475,7 +476,7 @@
     advanceToNextCallback();
 }
 
-TEST_F(VSyncDispatchTest, modificationsAroundVsyncTime) {
+TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) {
     Sequence seq;
     EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
     EXPECT_CALL(mMockClock, alarmIn(_, 200)).InSequence(seq);
@@ -495,7 +496,7 @@
     mDispatch.schedule(cb, 100, 2000);
 }
 
-TEST_F(VSyncDispatchTest, lateModifications) {
+TEST_F(VSyncDispatchTimerQueueTest, lateModifications) {
     Sequence seq;
     EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
     EXPECT_CALL(mMockClock, alarmIn(_, 400)).InSequence(seq);
@@ -516,7 +517,7 @@
     advanceToNextCallback();
 }
 
-TEST_F(VSyncDispatchTest, doesntCancelPriorValidTimerForFutureMod) {
+TEST_F(VSyncDispatchTimerQueueTest, doesntCancelPriorValidTimerForFutureMod) {
     Sequence seq;
     EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
 
@@ -526,7 +527,7 @@
     mDispatch.schedule(cb1, 500, 20000);
 }
 
-TEST_F(VSyncDispatchTest, setsTimerAfterCancellation) {
+TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) {
     Sequence seq;
     EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
     EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
@@ -538,19 +539,19 @@
     mDispatch.schedule(cb0, 100, 1000);
 }
 
-TEST_F(VSyncDispatchTest, makingUpIdsError) {
+TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
     VSyncDispatch::CallbackToken token(100);
     EXPECT_THAT(mDispatch.schedule(token, 100, 1000), Eq(ScheduleResult::Error));
     EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
 }
 
-TEST_F(VSyncDispatchTest, distinguishesScheduleAndReschedule) {
+TEST_F(VSyncDispatchTimerQueueTest, distinguishesScheduleAndReschedule) {
     CountingCallback cb0(mDispatch);
     EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
     EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::ReScheduled);
 }
 
-TEST_F(VSyncDispatchTest, helperMove) {
+TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
     EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
     EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
 
@@ -564,7 +565,7 @@
     cb1.cancel();
 }
 
-TEST_F(VSyncDispatchTest, helperMoveAssign) {
+TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) {
     EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
     EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
 
@@ -580,22 +581,22 @@
     cb1.cancel();
 }
 
-class VSyncDispatchEntryTest : public testing::Test {
+class VSyncDispatchTimerQueueEntryTest : public testing::Test {
 protected:
     nsecs_t const mPeriod = 1000;
     NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
 };
 
-TEST_F(VSyncDispatchEntryTest, stateAfterInitialization) {
+TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
     std::string name("basicname");
-    impl::VSyncDispatchEntry entry(name, [](auto) {});
+    VSyncDispatchTimerQueueEntry entry(name, [](auto) {});
     EXPECT_THAT(entry.name(), Eq(name));
     EXPECT_FALSE(entry.lastExecutedVsyncTarget());
     EXPECT_FALSE(entry.wakeupTime());
 }
 
-TEST_F(VSyncDispatchEntryTest, stateScheduling) {
-    impl::VSyncDispatchEntry entry("test", [](auto) {});
+TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) {
+    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
 
     EXPECT_FALSE(entry.wakeupTime());
     auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
@@ -608,14 +609,14 @@
     EXPECT_FALSE(entry.wakeupTime());
 }
 
-TEST_F(VSyncDispatchEntryTest, stateSchedulingReallyLongWakeupLatency) {
+TEST_F(VSyncDispatchTimerQueueEntryTest, stateSchedulingReallyLongWakeupLatency) {
     auto const duration = 500;
     auto const now = 8750;
 
     EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration))
             .Times(1)
             .WillOnce(Return(10000));
-    impl::VSyncDispatchEntry entry("test", [](auto) {});
+    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
 
     EXPECT_FALSE(entry.wakeupTime());
     auto const wakeup = entry.schedule(500, 994, mStubTracker, now);
@@ -625,10 +626,10 @@
     EXPECT_THAT(*queried, Eq(9500));
 }
 
-TEST_F(VSyncDispatchEntryTest, runCallback) {
+TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) {
     auto callCount = 0;
     auto calledTime = 0;
-    impl::VSyncDispatchEntry entry("test", [&](auto time) {
+    VSyncDispatchTimerQueueEntry entry("test", [&](auto time) {
         callCount++;
         calledTime = time;
     });
@@ -646,13 +647,13 @@
     EXPECT_THAT(*lastCalledTarget, Eq(mPeriod));
 }
 
-TEST_F(VSyncDispatchEntryTest, updateCallback) {
+TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) {
     EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
             .Times(2)
             .WillOnce(Return(1000))
             .WillOnce(Return(1020));
 
-    impl::VSyncDispatchEntry entry("test", [](auto) {});
+    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
 
     EXPECT_FALSE(entry.wakeupTime());
     entry.update(mStubTracker, 0);
@@ -667,8 +668,8 @@
     EXPECT_THAT(*queried, Eq(920));
 }
 
-TEST_F(VSyncDispatchEntryTest, skipsUpdateIfJustScheduled) {
-    impl::VSyncDispatchEntry entry("test", [](auto) {});
+TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
+    VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
     auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
     entry.update(mStubTracker, 0);
 
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 4725344..7e988a1 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -124,7 +124,8 @@
     };
 
     void AddImplicitLayers() {
-        if (!is_instance_ || !driver::Debuggable())
+        if (!is_instance_ ||
+            !android::GraphicsEnv::getInstance().isDebuggable())
             return;
 
         GetLayersFromSettings();
@@ -370,7 +371,8 @@
 
    private:
     bool EnableDebugCallback() const {
-        return (is_instance_ && driver::Debuggable() &&
+        return (is_instance_ &&
+                android::GraphicsEnv::getInstance().isDebuggable() &&
                 property_get_bool("debug.vulkan.enable_callback", false));
     }
 
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 0fb3a29..90a73e2 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -753,10 +753,6 @@
 
 }  // anonymous namespace
 
-bool Debuggable() {
-    return prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) > 0;
-}
-
 bool OpenHAL() {
     return Hal::Open();
 }
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index 7edadea..23c717c 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -100,7 +100,6 @@
     DeviceDriverTable driver;
 };
 
-bool Debuggable();
 bool OpenHAL();
 const VkAllocationCallbacks& GetDefaultAllocator();
 
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index 22f92d6..a14fed2 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -461,8 +461,7 @@
 void DiscoverLayers() {
     ATRACE_CALL();
 
-    if (property_get_bool("ro.debuggable", false) &&
-        prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+    if (android::GraphicsEnv::getInstance().isDebuggable()) {
         DiscoverLayersInPathList(kSystemLayerLibraryDir);
     }
     if (!android::GraphicsEnv::getInstance().getLayerPaths().empty())