Merge "Created libattestation"
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index eb6870e..96875d5 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -20,6 +20,7 @@
         "execv_helper.cpp",
         "globals.cpp",
         "run_dex2oat.cpp",
+        "unique_file.cpp",
         "utils.cpp",
         "utils_default.cpp",
         "view_compiler.cpp",
@@ -112,6 +113,7 @@
     srcs: [
         "run_dex2oat_test.cpp",
         "run_dex2oat.cpp",
+        "unique_file.cpp",
         "execv_helper.cpp",
     ],
     cflags: ["-Wall", "-Werror"],
@@ -119,9 +121,6 @@
         "libbase",
         "server_configurable_flags",
     ],
-    static_libs: [
-        //"libinstalld",
-    ],
     test_config: "run_dex2oat_test.xml",
 }
 
@@ -232,6 +231,7 @@
         "otapreopt.cpp",
         "otapreopt_utils.cpp",
         "run_dex2oat.cpp",
+        "unique_file.cpp",
         "utils.cpp",
         "utils_default.cpp",
         "view_compiler.cpp",
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 82ea746..5076ae6 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -54,6 +54,7 @@
 #include "installd_deps.h"
 #include "otapreopt_utils.h"
 #include "run_dex2oat.h"
+#include "unique_file.h"
 #include "utils.h"
 
 using android::base::Basename;
@@ -230,6 +231,12 @@
     return profile_boot_class_path == "true";
 }
 
+static void UnlinkIgnoreResult(const std::string& path) {
+    if (unlink(path.c_str()) < 0) {
+        PLOG(ERROR) << "Failed to unlink " << path;
+    }
+}
+
 /*
  * Whether dexopt should use a swap file when compiling an APK.
  *
@@ -346,6 +353,16 @@
     return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
 }
 
+static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::string& package_name,
+        const std::string& location, bool read_write, bool is_secondary_dex) {
+    std::string profile_path = create_reference_profile_path(package_name, location,
+                                                             is_secondary_dex);
+    unique_fd ufd = open_profile(uid, profile_path, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
+    return UniqueFile(ufd.release(), profile_path, [](const std::string& path) {
+        clear_profile(path);
+    });
+}
+
 static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name,
         const std::string& location) {
     std::string profile = create_snapshot_profile_path(package_name, location);
@@ -836,118 +853,14 @@
     return true;
 }
 
-// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor
-// on destruction. It will also run the given cleanup (unless told not to) after closing.
-//
-// Usage example:
-//
-//   Dex2oatFileWrapper file(open(...),
-//                                                   [name]() {
-//                                                       unlink(name.c_str());
-//                                                   });
-//   // Note: care needs to be taken about name, as it needs to have a lifetime longer than the
-//            wrapper if captured as a reference.
-//
-//   if (file.get() == -1) {
-//       // Error opening...
-//   }
-//
-//   ...
-//   if (error) {
-//       // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run
-//       // and delete the file (after the fd is closed).
-//       return -1;
-//   }
-//
-//   (Success case)
-//   file.SetCleanup(false);
-//   // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run
-//   // (leaving the file around; after the fd is closed).
-//
-class Dex2oatFileWrapper {
- public:
-    Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true), auto_close_(true) {
-    }
-
-    Dex2oatFileWrapper(int value, std::function<void ()> cleanup)
-            : value_(value), cleanup_(cleanup), do_cleanup_(true), auto_close_(true) {}
-
-    Dex2oatFileWrapper(Dex2oatFileWrapper&& other) {
-        value_ = other.value_;
-        cleanup_ = other.cleanup_;
-        do_cleanup_ = other.do_cleanup_;
-        auto_close_ = other.auto_close_;
-        other.release();
-    }
-
-    Dex2oatFileWrapper& operator=(Dex2oatFileWrapper&& other) {
-        value_ = other.value_;
-        cleanup_ = other.cleanup_;
-        do_cleanup_ = other.do_cleanup_;
-        auto_close_ = other.auto_close_;
-        other.release();
-        return *this;
-    }
-
-    ~Dex2oatFileWrapper() {
-        reset(-1);
-    }
-
-    int get() {
-        return value_;
-    }
-
-    void SetCleanup(bool cleanup) {
-        do_cleanup_ = cleanup;
-    }
-
-    void reset(int new_value) {
-        if (auto_close_ && value_ >= 0) {
-            close(value_);
-        }
-        if (do_cleanup_ && cleanup_ != nullptr) {
-            cleanup_();
-        }
-
-        value_ = new_value;
-    }
-
-    void reset(int new_value, std::function<void ()> new_cleanup) {
-        if (auto_close_ && value_ >= 0) {
-            close(value_);
-        }
-        if (do_cleanup_ && cleanup_ != nullptr) {
-            cleanup_();
-        }
-
-        value_ = new_value;
-        cleanup_ = new_cleanup;
-    }
-
-    void DisableAutoClose() {
-        auto_close_ = false;
-    }
-
- private:
-    void release() {
-        value_ = -1;
-        do_cleanup_ = false;
-        cleanup_ = nullptr;
-    }
-    int value_;
-    std::function<void ()> cleanup_;
-    bool do_cleanup_;
-    bool auto_close_;
-};
-
 // (re)Creates the app image if needed.
-Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path,
+UniqueFile maybe_open_app_image(const std::string& out_oat_path,
         bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) {
 
     const std::string image_path = create_image_filename(out_oat_path);
     if (image_path.empty()) {
         // Happens when the out_oat_path has an unknown extension.
-        return Dex2oatFileWrapper();
+        return UniqueFile();
     }
 
     // In case there is a stale image, remove it now. Ignore any error.
@@ -955,18 +868,19 @@
 
     // Not enabled, exit.
     if (!generate_app_image) {
-        return Dex2oatFileWrapper();
+        return UniqueFile();
     }
     std::string app_image_format = GetProperty("dalvik.vm.appimageformat", "");
     if (app_image_format.empty()) {
-        return Dex2oatFileWrapper();
+        return UniqueFile();
     }
     // Recreate is true since we do not want to modify a mapped image. If the app is
     // already running and we modify the image file, it can cause crashes (b/27493510).
-    Dex2oatFileWrapper wrapper_fd(
+    UniqueFile image_file(
             open_output_file(image_path.c_str(), true /*recreate*/, 0600 /*permissions*/),
-            [image_path]() { unlink(image_path.c_str()); });
-    if (wrapper_fd.get() < 0) {
+            image_path,
+            UnlinkIgnoreResult);
+    if (image_file.fd() < 0) {
         // Could not create application image file. Go on since we can compile without it.
         LOG(ERROR) << "installd could not create '" << image_path
                 << "' for image file during dexopt";
@@ -977,21 +891,21 @@
             }
         }
     } else if (!set_permissions_and_ownership(
-                wrapper_fd.get(), is_public, uid, image_path.c_str(), is_secondary_dex)) {
+                image_file.fd(), is_public, uid, image_path.c_str(), is_secondary_dex)) {
         ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str());
-        wrapper_fd.reset(-1);
+        image_file.reset();
     }
 
-    return wrapper_fd;
+    return image_file;
 }
 
 // Creates the dexopt swap file if necessary and return its fd.
 // Returns -1 if there's no need for a swap or in case of errors.
-unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) {
+unique_fd maybe_open_dexopt_swap_file(const std::string& out_oat_path) {
     if (!ShouldUseSwapFileForDexopt()) {
         return invalid_unique_fd();
     }
-    auto swap_file_name = std::string(out_oat_path) + ".swap";
+    auto swap_file_name = out_oat_path + ".swap";
     unique_fd swap_fd(open_output_file(
             swap_file_name.c_str(), /*recreate*/true, /*permissions*/0600));
     if (swap_fd.get() < 0) {
@@ -1009,13 +923,13 @@
 
 // Opens the reference profiles if needed.
 // Note that the reference profile might not exist so it's OK if the fd will be -1.
-Dex2oatFileWrapper maybe_open_reference_profile(const std::string& pkgname,
+UniqueFile maybe_open_reference_profile(const std::string& pkgname,
         const std::string& dex_path, const char* profile_name, bool profile_guided,
         bool is_public, int uid, bool is_secondary_dex) {
     // If we are not profile guided compilation, or we are compiling system server
     // do not bother to open the profiles; we won't be using them.
     if (!profile_guided || (pkgname[0] == '*')) {
-        return Dex2oatFileWrapper();
+        return UniqueFile();
     }
 
     // If this is a secondary dex path which is public do not open the profile.
@@ -1027,7 +941,7 @@
     // compiling with a public profile from the .dm file the PackageManager will
     // set is_public toghether with the profile guided compilation.
     if (is_secondary_dex && is_public) {
-        return Dex2oatFileWrapper();
+        return UniqueFile();
     }
 
     // Open reference profile in read only mode as dex2oat does not get write permissions.
@@ -1037,33 +951,28 @@
     } else {
         if (profile_name == nullptr) {
             // This path is taken for system server re-compilation lunched from ZygoteInit.
-            return Dex2oatFileWrapper();
+            return UniqueFile();
         } else {
             location = profile_name;
         }
     }
-    unique_fd ufd = open_reference_profile(uid, pkgname, location, /*read_write*/false,
-            is_secondary_dex);
-    const auto& cleanup = [pkgname, location, is_secondary_dex]() {
-        clear_reference_profile(pkgname, location, is_secondary_dex);
-    };
-    return Dex2oatFileWrapper(ufd.release(), cleanup);
+    return open_reference_profile_as_unique_file(uid, pkgname, location, /*read_write*/false,
+                                                 is_secondary_dex);
 }
 
-// Opens the vdex files and assigns the input fd to in_vdex_wrapper_fd and the output fd to
-// out_vdex_wrapper_fd. Returns true for success or false in case of errors.
+// Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to
+// out_vdex_wrapper. Returns true for success or false in case of errors.
 bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, int dexopt_needed,
         const char* instruction_set, bool is_public, int uid, bool is_secondary_dex,
-        bool profile_guided, Dex2oatFileWrapper* in_vdex_wrapper_fd,
-        Dex2oatFileWrapper* out_vdex_wrapper_fd) {
-    CHECK(in_vdex_wrapper_fd != nullptr);
-    CHECK(out_vdex_wrapper_fd != nullptr);
+        bool profile_guided, UniqueFile* in_vdex_wrapper,
+        UniqueFile* out_vdex_wrapper) {
+    CHECK(in_vdex_wrapper != nullptr);
+    CHECK(out_vdex_wrapper != nullptr);
     // Open the existing VDEX. We do this before creating the new output VDEX, which will
     // unlink the old one.
     char in_odex_path[PKG_PATH_MAX];
     int dexopt_action = abs(dexopt_needed);
     bool is_odex_location = dexopt_needed < 0;
-    std::string in_vdex_path_str;
 
     // Infer the name of the output VDEX.
     const std::string out_vdex_path_str = create_vdex_filename(out_oat_path);
@@ -1085,7 +994,7 @@
         } else {
             path = out_oat_path;
         }
-        in_vdex_path_str = create_vdex_filename(path);
+        std::string in_vdex_path_str = create_vdex_filename(path);
         if (in_vdex_path_str.empty()) {
             ALOGE("installd cannot compute input vdex location for '%s'\n", path);
             return false;
@@ -1103,13 +1012,15 @@
             !profile_guided;
         if (update_vdex_in_place) {
             // Open the file read-write to be able to update it.
-            in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0));
-            if (in_vdex_wrapper_fd->get() == -1) {
+            in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0),
+                                   in_vdex_path_str);
+            if (in_vdex_wrapper->fd() == -1) {
                 // If we failed to open the file, we cannot update it in place.
                 update_vdex_in_place = false;
             }
         } else {
-            in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0));
+            in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0),
+                                   in_vdex_path_str);
         }
     }
 
@@ -1118,22 +1029,24 @@
     if (update_vdex_in_place) {
         // We unlink the file in case the invocation of dex2oat fails, to ensure we don't
         // have bogus stale vdex files.
-        out_vdex_wrapper_fd->reset(
-              in_vdex_wrapper_fd->get(),
-              [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
+        out_vdex_wrapper->reset(
+              in_vdex_wrapper->fd(),
+              out_vdex_path_str,
+              UnlinkIgnoreResult);
         // Disable auto close for the in wrapper fd (it will be done when destructing the out
         // wrapper).
-        in_vdex_wrapper_fd->DisableAutoClose();
+        in_vdex_wrapper->DisableAutoClose();
     } else {
-        out_vdex_wrapper_fd->reset(
+        out_vdex_wrapper->reset(
               open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
-              [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); });
-        if (out_vdex_wrapper_fd->get() < 0) {
+              out_vdex_path_str,
+              UnlinkIgnoreResult);
+        if (out_vdex_wrapper->fd() < 0) {
             ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str());
             return false;
         }
     }
-    if (!set_permissions_and_ownership(out_vdex_wrapper_fd->get(), is_public, uid,
+    if (!set_permissions_and_ownership(out_vdex_wrapper->fd(), is_public, uid,
             out_vdex_path_str.c_str(), is_secondary_dex)) {
         ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str());
         return false;
@@ -1144,25 +1057,24 @@
 }
 
 // Opens the output oat file for the given apk.
-// If successful it stores the output path into out_oat_path and returns true.
-Dex2oatFileWrapper open_oat_out_file(const char* apk_path, const char* oat_dir,
-        bool is_public, int uid, const char* instruction_set, bool is_secondary_dex,
-        char* out_oat_path) {
+UniqueFile open_oat_out_file(const char* apk_path, const char* oat_dir,
+        bool is_public, int uid, const char* instruction_set, bool is_secondary_dex) {
+    char out_oat_path[PKG_PATH_MAX];
     if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) {
-        return Dex2oatFileWrapper();
+        return UniqueFile();
     }
-    const std::string out_oat_path_str(out_oat_path);
-    Dex2oatFileWrapper wrapper_fd(
+    UniqueFile oat(
             open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
-            [out_oat_path_str]() { unlink(out_oat_path_str.c_str()); });
-    if (wrapper_fd.get() < 0) {
+            out_oat_path,
+            UnlinkIgnoreResult);
+    if (oat.fd() < 0) {
         PLOG(ERROR) << "installd cannot open output during dexopt" <<  out_oat_path;
     } else if (!set_permissions_and_ownership(
-                wrapper_fd.get(), is_public, uid, out_oat_path, is_secondary_dex)) {
+                oat.fd(), is_public, uid, out_oat_path, is_secondary_dex)) {
         ALOGE("installd cannot set owner '%s' for output during dexopt\n", out_oat_path);
-        wrapper_fd.reset(-1);
+        oat.reset();
     }
-    return wrapper_fd;
+    return oat;
 }
 
 // Creates RDONLY fds for oat and vdex files, if exist.
@@ -1769,8 +1681,8 @@
     }
 
     // Open the input file.
-    unique_fd input_fd(open(dex_path, O_RDONLY, 0));
-    if (input_fd.get() < 0) {
+    UniqueFile in_dex(open(dex_path, O_RDONLY, 0), dex_path);
+    if (in_dex.fd() < 0) {
         *error_msg = StringPrintf("installd cannot open '%s' for input during dexopt", dex_path);
         LOG(ERROR) << *error_msg;
         return -1;
@@ -1784,19 +1696,19 @@
     }
 
     // Create the output OAT file.
-    char out_oat_path[PKG_PATH_MAX];
-    Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid,
-            instruction_set, is_secondary_dex, out_oat_path);
-    if (out_oat_fd.get() < 0) {
+    UniqueFile out_oat = open_oat_out_file(dex_path, oat_dir, is_public, uid,
+            instruction_set, is_secondary_dex);
+    if (out_oat.fd() < 0) {
         *error_msg = "Could not open out oat file.";
         return -1;
     }
 
     // Open vdex files.
-    Dex2oatFileWrapper in_vdex_fd;
-    Dex2oatFileWrapper out_vdex_fd;
-    if (!open_vdex_files_for_dex2oat(dex_path, out_oat_path, dexopt_needed, instruction_set,
-            is_public, uid, is_secondary_dex, profile_guided, &in_vdex_fd, &out_vdex_fd)) {
+    UniqueFile in_vdex;
+    UniqueFile out_vdex;
+    if (!open_vdex_files_for_dex2oat(dex_path, out_oat.path().c_str(), dexopt_needed,
+            instruction_set, is_public, uid, is_secondary_dex, profile_guided, &in_vdex,
+            &out_vdex)) {
         *error_msg = "Could not open vdex files.";
         return -1;
     }
@@ -1816,26 +1728,27 @@
     }
 
     // Create a swap file if necessary.
-    unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path);
+    unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat.path());
 
     // Open the reference profile if needed.
-    Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile(
+    UniqueFile reference_profile = maybe_open_reference_profile(
             pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex);
 
-    if (reference_profile_fd.get() == -1) {
+    if (reference_profile.fd() == -1) {
         // We don't create an app image without reference profile since there is no speedup from
         // loading it in that case and instead will be a small overhead.
         generate_app_image = false;
     }
 
     // Create the app image file if needed.
-    Dex2oatFileWrapper image_fd = maybe_open_app_image(
-            out_oat_path, generate_app_image, is_public, uid, is_secondary_dex);
+    UniqueFile out_image = maybe_open_app_image(
+            out_oat.path(), generate_app_image, is_public, uid, is_secondary_dex);
 
-    unique_fd dex_metadata_fd;
+    UniqueFile dex_metadata;
     if (dex_metadata_path != nullptr) {
-        dex_metadata_fd.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW)));
-        if (dex_metadata_fd.get() < 0) {
+        dex_metadata.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW)),
+                           dex_metadata_path);
+        if (dex_metadata.fd() < 0) {
             PLOG(ERROR) << "Failed to open dex metadata file " << dex_metadata_path;
         }
     }
@@ -1862,26 +1775,24 @@
     LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---";
 
     RunDex2Oat runner(dex2oat_bin, execv_helper.get());
-    runner.Initialize(input_fd.get(),
-                      out_oat_fd.get(),
-                      in_vdex_fd.get(),
-                      out_vdex_fd.get(),
-                      image_fd.get(),
-                      dex_path,
-                      out_oat_path,
+    runner.Initialize(out_oat,
+                      out_vdex,
+                      out_image,
+                      in_dex,
+                      in_vdex,
+                      dex_metadata,
+                      reference_profile,
+                      class_loader_context,
+                      join_fds(context_input_fds),
                       swap_fd.get(),
                       instruction_set,
                       compiler_filter,
                       debuggable,
                       boot_complete,
                       for_restore,
-                      reference_profile_fd.get(),
-                      class_loader_context,
-                      join_fds(context_input_fds),
                       target_sdk_version,
                       enable_hidden_api_checks,
                       generate_compact_dex,
-                      dex_metadata_fd.get(),
                       use_jitzygote_image,
                       compilation_reason);
 
@@ -1891,8 +1802,8 @@
         drop_capabilities(uid);
 
         SetDex2OatScheduling(boot_complete);
-        if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {
-            PLOG(ERROR) << "flock(" << out_oat_path << ") failed";
+        if (flock(out_oat.fd(), LOCK_EX | LOCK_NB) != 0) {
+            PLOG(ERROR) << "flock(" << out_oat.path() << ") failed";
             _exit(DexoptReturnCodes::kFlock);
         }
 
@@ -1909,13 +1820,13 @@
         }
     }
 
-    update_out_oat_access_times(dex_path, out_oat_path);
+    update_out_oat_access_times(dex_path, out_oat.path().c_str());
 
     // We've been successful, don't delete output.
-    out_oat_fd.SetCleanup(false);
-    out_vdex_fd.SetCleanup(false);
-    image_fd.SetCleanup(false);
-    reference_profile_fd.SetCleanup(false);
+    out_oat.DisableCleanup();
+    out_vdex.DisableCleanup();
+    out_image.DisableCleanup();
+    reference_profile.DisableCleanup();
 
     return 0;
 }
diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp
index 1572e3b..17ea903 100644
--- a/cmds/installd/run_dex2oat.cpp
+++ b/cmds/installd/run_dex2oat.cpp
@@ -30,6 +30,8 @@
 #include <log/log.h>
 #include <server_configurable_flags/get_flags.h>
 
+#include "unique_file.h"
+
 using android::base::Basename;
 using android::base::StringPrintf;
 
@@ -64,35 +66,33 @@
 RunDex2Oat::RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper)
   : dex2oat_bin_(dex2oat_bin), execv_helper_(execv_helper) {}
 
-void RunDex2Oat::Initialize(int zip_fd,
-                            int oat_fd,
-                            int input_vdex_fd,
-                            int output_vdex_fd,
-                            int image_fd,
-                            const char* input_file_name,
-                            const char* output_file_name,
+void RunDex2Oat::Initialize(const UniqueFile& output_oat,
+                            const UniqueFile& output_vdex,
+                            const UniqueFile& output_image,
+                            const UniqueFile& input_dex,
+                            const UniqueFile& input_vdex,
+                            const UniqueFile& dex_metadata,
+                            const UniqueFile& profile,
+                            const char* class_loader_context,
+                            const std::string& class_loader_context_fds,
                             int swap_fd,
                             const char* instruction_set,
                             const char* compiler_filter,
                             bool debuggable,
                             bool post_bootcomplete,
                             bool for_restore,
-                            int profile_fd,
-                            const char* class_loader_context,
-                            const std::string& class_loader_context_fds,
                             int target_sdk_version,
                             bool enable_hidden_api_checks,
                             bool generate_compact_dex,
-                            int dex_metadata_fd,
                             bool use_jitzygote_image,
                             const char* compilation_reason) {
     PrepareBootImageAndBootClasspathFlags(use_jitzygote_image);
 
-    PrepareInputFileFlags(zip_fd, oat_fd, input_vdex_fd, output_vdex_fd, image_fd, input_file_name,
-                          output_file_name, profile_fd, dex_metadata_fd, swap_fd,
-                          class_loader_context, class_loader_context_fds);
+    PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex,
+                          dex_metadata, profile, swap_fd, class_loader_context,
+                          class_loader_context_fds);
 
-    PrepareCompilerConfigFlags(input_vdex_fd, output_vdex_fd, instruction_set, compiler_filter,
+    PrepareCompilerConfigFlags(input_vdex, output_vdex, instruction_set, compiler_filter,
                                debuggable, target_sdk_version, enable_hidden_api_checks,
                                generate_compact_dex, compilation_reason);
 
@@ -139,38 +139,36 @@
     AddArg(updatable_bcp_packages);
 }
 
-void RunDex2Oat::PrepareInputFileFlags(int zip_fd,
-                                       int oat_fd,
-                                       int input_vdex_fd,
-                                       int output_vdex_fd,
-                                       int image_fd,
-                                       const char* input_file_name,
-                                       const char* output_file_name,
-                                       int profile_fd,
-                                       int dex_metadata_fd,
+void RunDex2Oat::PrepareInputFileFlags(const UniqueFile& output_oat,
+                                       const UniqueFile& output_vdex,
+                                       const UniqueFile& output_image,
+                                       const UniqueFile& input_dex,
+                                       const UniqueFile& input_vdex,
+                                       const UniqueFile& dex_metadata,
+                                       const UniqueFile& profile,
                                        int swap_fd,
                                        const char* class_loader_context,
                                        const std::string& class_loader_context_fds) {
-    std::string input_basename = Basename(input_file_name);
-    ALOGV("Running %s in=%s out=%s\n", dex2oat_bin_.c_str(), input_basename.c_str(),
-          output_file_name);
+    std::string input_basename = Basename(input_dex.path());
+    LOG(VERBOSE) << "Running " << dex2oat_bin_ << " in=" << input_basename << " out="
+                 << output_oat.path();
 
-    AddArg(StringPrintf("--zip-fd=%d", zip_fd));
+    AddArg(StringPrintf("--zip-fd=%d", input_dex.fd()));
     AddArg(StringPrintf("--zip-location=%s", input_basename.c_str()));
-    AddArg(StringPrintf("--oat-fd=%d", oat_fd));
-    AddArg(StringPrintf("--oat-location=%s", output_file_name));
-    AddArg(StringPrintf("--input-vdex-fd=%d", input_vdex_fd));
-    AddArg(StringPrintf("--output-vdex-fd=%d", output_vdex_fd));
+    AddArg(StringPrintf("--oat-fd=%d", output_oat.fd()));
+    AddArg(StringPrintf("--oat-location=%s", output_oat.path().c_str()));
+    AddArg(StringPrintf("--input-vdex-fd=%d", input_vdex.fd()));
+    AddArg(StringPrintf("--output-vdex-fd=%d", output_vdex.fd()));
 
-    if (image_fd >= 0) {
-        AddArg(StringPrintf("--app-image-fd=%d", image_fd));
+    if (output_image.fd() >= 0) {
+        AddArg(StringPrintf("--app-image-fd=%d", output_image.fd()));
         AddArg(MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s"));
     }
-    if (dex_metadata_fd > -1) {
-        AddArg("--dm-fd=" + std::to_string(dex_metadata_fd));
+    if (dex_metadata.fd() > -1) {
+        AddArg("--dm-fd=" + std::to_string(dex_metadata.fd()));
     }
-    if (profile_fd != -1) {
-        AddArg(StringPrintf("--profile-file-fd=%d", profile_fd));
+    if (profile.fd() != -1) {
+        AddArg(StringPrintf("--profile-file-fd=%d", profile.fd()));
     }
     if (swap_fd >= 0) {
         AddArg(StringPrintf("--swap-fd=%d", swap_fd));
@@ -178,7 +176,7 @@
 
     // Get the directory of the apk to pass as a base classpath directory.
     {
-        std::string apk_dir(input_file_name);
+        std::string apk_dir(input_dex.path());
         size_t dir_index = apk_dir.rfind('/');
         if (dir_index != std::string::npos) {
             apk_dir = apk_dir.substr(0, dir_index);
@@ -195,8 +193,8 @@
     }
 }
 
-void RunDex2Oat::PrepareCompilerConfigFlags(int input_vdex_fd,
-                                            int output_vdex_fd,
+void RunDex2Oat::PrepareCompilerConfigFlags(const UniqueFile& input_vdex,
+                                            const UniqueFile& output_vdex,
                                             const char* instruction_set,
                                             const char* compiler_filter,
                                             bool debuggable,
@@ -206,11 +204,11 @@
                                             const char* compilation_reason) {
     // Disable cdex if update input vdex is true since this combination of options is not
     // supported.
-    const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd);
+    const bool disable_cdex = !generate_compact_dex || (input_vdex.fd() == output_vdex.fd());
     if (disable_cdex) {
         AddArg(kDisableCompactDexFlag);
     }
-    
+
     // ISA related
     {
         AddArg(StringPrintf("--instruction-set=%s", instruction_set));
diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h
index 5453272..325a3a2 100644
--- a/cmds/installd/run_dex2oat.h
+++ b/cmds/installd/run_dex2oat.h
@@ -25,31 +25,31 @@
 namespace android {
 namespace installd {
 
+class UniqueFile;
+
 class RunDex2Oat {
   public:
     explicit RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper);
     virtual ~RunDex2Oat();
 
-    void Initialize(int zip_fd,
-                    int oat_fd,
-                    int input_vdex_fd,
-                    int output_vdex_fd,
-                    int image_fd,
-                    const char* input_file_name,
-                    const char* output_file_name,
+    void Initialize(const UniqueFile& output_oat,
+                    const UniqueFile& output_vdex,
+                    const UniqueFile& output_image,
+                    const UniqueFile& input_dex,
+                    const UniqueFile& input_vdex,
+                    const UniqueFile& dex_metadata,
+                    const UniqueFile& profile,
+                    const char* class_loader_context,
+                    const std::string& class_loader_context_fds,
                     int swap_fd,
                     const char* instruction_set,
                     const char* compiler_filter,
                     bool debuggable,
                     bool post_bootcomplete,
                     bool for_restore,
-                    int profile_fd,
-                    const char* class_loader_context,
-                    const std::string& class_loader_context_fds,
                     int target_sdk_version,
                     bool enable_hidden_api_checks,
                     bool generate_compact_dex,
-                    int dex_metadata_fd,
                     bool use_jitzygote_image,
                     const char* compilation_reason);
 
@@ -57,20 +57,18 @@
 
   protected:
     void PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image);
-    void PrepareInputFileFlags(int zip_fd,
-                               int oat_fd,
-                               int input_vdex_fd,
-                               int output_vdex_fd,
-                               int image_fd,
-                               const char* input_file_name,
-                               const char* output_file_name,
-                               int profile_fd,
-                               int dex_metadata_fd,
+    void PrepareInputFileFlags(const UniqueFile& output_oat,
+                               const UniqueFile& output_vdex,
+                               const UniqueFile& output_image,
+                               const UniqueFile& input_dex,
+                               const UniqueFile& input_vdex,
+                               const UniqueFile& dex_metadata,
+                               const UniqueFile& profile,
                                int swap_fd,
                                const char* class_loader_context,
                                const std::string& class_loader_context_fds);
-    void PrepareCompilerConfigFlags(int input_vdex_fd,
-                                    int output_vdex_fd,
+    void PrepareCompilerConfigFlags(const UniqueFile& input_vdex,
+                                    const UniqueFile& output_vdex,
                                     const char* instruction_set,
                                     const char* compiler_filter,
                                     bool debuggable,
diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp
index b1f429d..3813cf7 100644
--- a/cmds/installd/run_dex2oat_test.cpp
+++ b/cmds/installd/run_dex2oat_test.cpp
@@ -24,6 +24,7 @@
 
 #include "execv_helper.h"
 #include "run_dex2oat.h"
+#include "unique_file.h"
 
 namespace android {
 namespace installd {
@@ -34,14 +35,16 @@
     static constexpr const char* OUTPUT_PATH = "/dir/output/basename.oat";
     static constexpr const char* FLAG_UNUSED = "{{FLAG_UNUSED}}";
 
-    static constexpr int ZIP_FD = 1;
-    static constexpr int OAT_FD = 2;
-    static constexpr int INPUT_VDEX_FD = 3;
-    static constexpr int OUTPUT_VDEX_FD = 4;
-    static constexpr int IMAGE_FD = 5;
-    static constexpr int PROFILE_FD = 6;
-    static constexpr int DEX_METADATA_FD = 7;
-    static constexpr int SWAP_FD = 8;
+    // UniqueFile closes FD. Avoid using standard I/O since the test is expected to print gtest
+    // results. Alternatively, mock out UniqueFile to avoid the side effect of close(2).
+    static constexpr int ZIP_FD = 3;
+    static constexpr int OAT_FD = 4;
+    static constexpr int INPUT_VDEX_FD = 5;
+    static constexpr int OUTPUT_VDEX_FD = 6;
+    static constexpr int IMAGE_FD = 7;
+    static constexpr int PROFILE_FD = 8;
+    static constexpr int DEX_METADATA_FD = 9;
+    static constexpr int SWAP_FD = 10;
 
     using FakeSystemProperties = std::map<std::string, std::string>;
 
@@ -84,37 +87,33 @@
     struct RunDex2OatArgs {
         static std::unique_ptr<RunDex2OatArgs> MakeDefaultTestArgs() {
             auto args = std::make_unique<RunDex2OatArgs>();
-            args->input_file_name = INPUT_PATH;
-            args->zip_fd = ZIP_FD;
-            args->output_file_name = OUTPUT_PATH;
-            args->oat_fd = OAT_FD;
-            args->input_vdex_fd = INPUT_VDEX_FD;
-            args->output_vdex_fd = OUTPUT_VDEX_FD;
+            args->input_dex.reset(ZIP_FD, INPUT_PATH);
+            args->output_oat.reset(OAT_FD, OUTPUT_PATH);
+            args->input_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH");
+            args->output_vdex.reset(OUTPUT_VDEX_FD, "UNUSED_PATH");
             args->instruction_set = "arm64";
             args->compilation_reason = "rundex2oattest";
             return args;
         }
 
-        int zip_fd = -1;
-        int oat_fd = -1;
-        int input_vdex_fd = -1;
-        int output_vdex_fd = -1;
-        int image_fd = -1;
-        const char* input_file_name = nullptr;
-        const char* output_file_name = nullptr;
+        UniqueFile output_oat;
+        UniqueFile output_vdex;
+        UniqueFile output_image;
+        UniqueFile input_dex;
+        UniqueFile input_vdex;
+        UniqueFile dex_metadata;
+        UniqueFile profile;
         int swap_fd = -1;
         const char* instruction_set = nullptr;
         const char* compiler_filter = "extract";
         bool debuggable = false;
         bool post_bootcomplete = false;
         bool for_restore = false;
-        int profile_fd = -1;
         const char* class_loader_context = nullptr;
         std::string class_loader_context_fds;
         int target_sdk_version = 0;
         bool enable_hidden_api_checks = false;
         bool generate_compact_dex = true;
-        int dex_metadata_fd = -1;
         bool use_jitzygote_image = false;
         const char* compilation_reason = nullptr;
     };
@@ -143,7 +142,7 @@
                 cmd += arg;
                 cmd += " ";
             }
-            LOG(DEBUG) << "FakeExecVHelper exit_code: " << exit_code << " cmd: " << cmd;
+            LOG(DEBUG) << "FakeExecVHelper exit_code: " << exit_code << " cmd: " << cmd << "\n";
         }
     };
 
@@ -230,7 +229,7 @@
                     << "Flag " << flag << " should be specified without value, but got " << value;
             } else {
                 EXPECT_TRUE(execv_helper_->HasArg(flag + value))
-                    << "Flag " << flag << "=" << value << " is not specificed";
+                    << "Flag " << flag << value << " is not specificed";
             }
         }
     }
@@ -241,26 +240,24 @@
 
     void CallRunDex2Oat(std::unique_ptr<RunDex2OatArgs> args) {
         FakeRunDex2Oat runner(execv_helper_.get(), &system_properties_);
-        runner.Initialize(args->zip_fd,
-                          args->oat_fd,
-                          args->input_vdex_fd,
-                          args->output_vdex_fd,
-                          args->image_fd,
-                          args->input_file_name,
-                          args->output_file_name,
+        runner.Initialize(args->output_oat,
+                          args->output_vdex,
+                          args->output_image,
+                          args->input_dex,
+                          args->input_vdex,
+                          args->dex_metadata,
+                          args->profile,
+                          args->class_loader_context,
+                          args->class_loader_context_fds,
                           args->swap_fd,
                           args->instruction_set,
                           args->compiler_filter,
                           args->debuggable,
                           args->post_bootcomplete,
                           args->for_restore,
-                          args->profile_fd,
-                          args->class_loader_context,
-                          args->class_loader_context_fds,
                           args->target_sdk_version,
                           args->enable_hidden_api_checks,
                           args->generate_compact_dex,
-                          args->dex_metadata_fd,
                           args->use_jitzygote_image,
                           args->compilation_reason);
         runner.Exec(/*exit_code=*/ 0);
@@ -281,8 +278,8 @@
 
 TEST_F(RunDex2OatTest, WithAllOtherInputFds) {
     auto args = RunDex2OatArgs::MakeDefaultTestArgs();
-    args->image_fd = IMAGE_FD;
-    args->profile_fd = PROFILE_FD;
+    args->output_image.reset(IMAGE_FD, "UNUSED_PATH");
+    args->profile.reset(PROFILE_FD, "UNUSED_PATH");
     args->swap_fd = SWAP_FD;
     CallRunDex2Oat(std::move(args));
 
@@ -357,8 +354,8 @@
 TEST_F(RunDex2OatTest, DoNotGenerateCompactDexWithVdexInPlaceUpdate) {
     auto args = RunDex2OatArgs::MakeDefaultTestArgs();
     args->generate_compact_dex = true;
-    args->input_vdex_fd = INPUT_VDEX_FD;
-    args->output_vdex_fd = INPUT_VDEX_FD;
+    args->input_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH");
+    args->output_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH");
     CallRunDex2Oat(std::move(args));
 
     SetExpectedFlagUsed("--compact-dex-level", "=none");
diff --git a/cmds/installd/unique_file.cpp b/cmds/installd/unique_file.cpp
new file mode 100644
index 0000000..e99ce1e
--- /dev/null
+++ b/cmds/installd/unique_file.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unique_file.h"
+
+#include <string>
+
+#include <unistd.h>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace installd {
+
+UniqueFile::UniqueFile() : UniqueFile(-1, "") {}
+
+UniqueFile::UniqueFile(int value, std::string path) : UniqueFile(value, path, nullptr) {}
+
+UniqueFile::UniqueFile(int value, std::string path, CleanUpFunction cleanup)
+        : value_(value), path_(path), cleanup_(cleanup), do_cleanup_(true), auto_close_(true) {}
+
+UniqueFile::UniqueFile(UniqueFile&& other) {
+    *this = std::move(other);
+}
+
+UniqueFile::~UniqueFile() {
+    reset();
+}
+
+UniqueFile& UniqueFile::operator=(UniqueFile&& other) {
+    value_ = other.value_;
+    path_ = other.path_;
+    cleanup_ = other.cleanup_;
+    do_cleanup_ = other.do_cleanup_;
+    auto_close_ = other.auto_close_;
+    other.release();
+    return *this;
+}
+
+void UniqueFile::reset() {
+    reset(-1, "");
+}
+
+void UniqueFile::reset(int new_value, std::string path, CleanUpFunction new_cleanup) {
+    if (auto_close_ && value_ >= 0) {
+        if (close(value_) < 0) {
+            PLOG(ERROR) << "Failed to close fd " << value_ << ", with path " << path;
+        }
+    }
+    if (do_cleanup_ && cleanup_ != nullptr) {
+        cleanup_(path_);
+    }
+
+    value_ = new_value;
+    path_ = path;
+    cleanup_ = new_cleanup;
+}
+
+void UniqueFile::release() {
+    value_ = -1;
+    path_ = "";
+    do_cleanup_ = false;
+    cleanup_ = nullptr;
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/unique_file.h b/cmds/installd/unique_file.h
new file mode 100644
index 0000000..e85e23b
--- /dev/null
+++ b/cmds/installd/unique_file.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INSTALLD_UNIQUE_FILE_H
+#define ANDROID_INSTALLD_UNIQUE_FILE_H
+
+#include <functional>
+#include <string>
+
+namespace android {
+namespace installd {
+
+// A file management helper that serves two purposes:
+//
+// 1. Closes the file description on destruction, similar unique_fd.
+// 2. Runs a cleanup function on after close, if not cancelled.
+//
+// The class does not assume the relationship between the given fd and file path.
+//
+// Example:
+//
+//   UniqueFile file(open(...),
+//                           filepath,
+//                           [](const std::string& path) {
+//                               unlink(path.c_str());
+//                           });
+//   if (file.fd() == -1) {
+//       // Error opening...
+//   }
+//
+//   ...
+//   if (error) {
+//       // At this point, when the UniqueFile is destructed, the cleanup function will run
+//       // (e.g. to delete the file) after the fd is closed.
+//       return -1;
+//   }
+//
+//   (Success case)
+//   file.DisableCleanup();
+//   // At this point, when the UniqueFile is destructed, the cleanup function will not run
+//   // (e.g. leaving the file around) after the fd is closed.
+//
+class UniqueFile {
+ private:
+    using CleanUpFunction = std::function<void (const std::string&)>;
+
+ public:
+    UniqueFile();
+    UniqueFile(int value, std::string path);
+    UniqueFile(int value, std::string path, CleanUpFunction cleanup);
+    UniqueFile(UniqueFile&& other);
+    ~UniqueFile();
+
+    UniqueFile& operator=(UniqueFile&& other);
+
+    int fd() const {
+        return value_;
+    }
+
+    const std::string& path() const {
+      return path_;
+    }
+
+    void DisableAutoClose() {
+        auto_close_ = false;
+    }
+
+    void DisableCleanup() {
+        do_cleanup_ = false;
+    }
+
+    void reset();
+    void reset(int new_value, std::string path, CleanUpFunction new_cleanup = nullptr);
+
+ private:
+    void release();
+
+    int value_;
+    std::string path_;
+    CleanUpFunction cleanup_;
+    bool do_cleanup_;
+    bool auto_close_;
+};
+
+}  // namespace installd
+}  // namespace android
+
+#endif  // ANDROID_INSTALLD_UNIQUE_FILE_H
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 7277e85..b139251 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -44,6 +44,9 @@
     cflags: [
         "-DVENDORSERVICEMANAGER=1",
     ],
+    required: [
+        "vndservice",
+    ],
     srcs: ["main.cpp"],
 }
 
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 2618906..b1bc6dc 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -130,7 +130,7 @@
     }
 
     IPCThreadState::self()->setTheContextObject(manager);
-    ps->becomeContextManager(nullptr, nullptr);
+    ps->becomeContextManager();
 
     sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
 
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index c7685b7..60638ca 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -108,11 +108,11 @@
     inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
     inline int32_t getKeyboardType() const { return mKeyboardType; }
 
-    inline void setKeyCharacterMap(const sp<KeyCharacterMap>& value) {
+    inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) {
         mKeyCharacterMap = value;
     }
 
-    inline sp<KeyCharacterMap> getKeyCharacterMap() const {
+    inline const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const {
         return mKeyCharacterMap;
     }
 
@@ -136,7 +136,7 @@
     bool mHasMic;
     uint32_t mSources;
     int32_t mKeyboardType;
-    sp<KeyCharacterMap> mKeyCharacterMap;
+    std::shared_ptr<KeyCharacterMap> mKeyCharacterMap;
     bool mHasVibrator;
     bool mHasButtonUnderPad;
 
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index a1a32a6..d874347 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -23,12 +23,12 @@
 #include <binder/IBinder.h>
 #endif
 
+#include <android-base/result.h>
 #include <input/Input.h>
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <utils/Tokenizer.h>
 #include <utils/Unicode.h>
-#include <utils/RefBase.h>
 
 // Maximum number of keys supported by KeyCharacterMaps
 #define MAX_KEYS 8192
@@ -42,7 +42,7 @@
  *
  * This object is immutable after it has been loaded.
  */
-class KeyCharacterMap : public RefBase {
+class KeyCharacterMap {
 public:
     enum KeyboardType {
         KEYBOARD_TYPE_UNKNOWN = 0,
@@ -74,18 +74,18 @@
     };
 
     /* Loads a key character map from a file. */
-    static status_t load(const std::string& filename, Format format, sp<KeyCharacterMap>* outMap);
+    static base::Result<std::shared_ptr<KeyCharacterMap>> load(const std::string& filename,
+                                                               Format format);
 
     /* Loads a key character map from its string contents. */
-    static status_t loadContents(const std::string& filename,
-            const char* contents, Format format, sp<KeyCharacterMap>* outMap);
+    static base::Result<std::shared_ptr<KeyCharacterMap>> loadContents(const std::string& filename,
+                                                                       const char* contents,
+                                                                       Format format);
 
-    /* Combines a base key character map and an overlay. */
-    static sp<KeyCharacterMap> combine(const sp<KeyCharacterMap>& base,
-            const sp<KeyCharacterMap>& overlay);
+    const std::string getLoadFileName() const;
 
-    /* Returns an empty key character map. */
-    static sp<KeyCharacterMap> empty();
+    /* Combines this key character map with an overlay. */
+    void combine(const KeyCharacterMap& overlay);
 
     /* Gets the keyboard type. */
     int32_t getKeyboardType() const;
@@ -136,13 +136,14 @@
 
 #ifdef __ANDROID__
     /* Reads a key map from a parcel. */
-    static sp<KeyCharacterMap> readFromParcel(Parcel* parcel);
+    static std::shared_ptr<KeyCharacterMap> readFromParcel(Parcel* parcel);
 
     /* Writes a key map to a parcel. */
     void writeToParcel(Parcel* parcel) const;
 #endif
 
-protected:
+    KeyCharacterMap(const KeyCharacterMap& other);
+
     virtual ~KeyCharacterMap();
 
 private:
@@ -224,16 +225,14 @@
         status_t parseCharacterLiteral(char16_t* outCharacter);
     };
 
-    static sp<KeyCharacterMap> sEmpty;
-
     KeyedVector<int32_t, Key*> mKeys;
     int mType;
+    std::string mLoadFileName;
 
     KeyedVector<int32_t, int32_t> mKeysByScanCode;
     KeyedVector<int32_t, int32_t> mKeysByUsageCode;
 
     KeyCharacterMap();
-    KeyCharacterMap(const KeyCharacterMap& other);
 
     bool getKey(int32_t keyCode, const Key** outKey) const;
     bool getKeyBehavior(int32_t keyCode, int32_t metaState,
@@ -242,7 +241,7 @@
 
     bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
 
-    static status_t load(Tokenizer* tokenizer, Format format, sp<KeyCharacterMap>* outMap);
+    static base::Result<std::shared_ptr<KeyCharacterMap>> load(Tokenizer* tokenizer, Format format);
 
     static void addKey(Vector<KeyEvent>& outEvents,
             int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time);
diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h
index 26f3501..872dd45 100644
--- a/include/input/KeyLayoutMap.h
+++ b/include/input/KeyLayoutMap.h
@@ -17,11 +17,12 @@
 #ifndef _LIBINPUT_KEY_LAYOUT_MAP_H
 #define _LIBINPUT_KEY_LAYOUT_MAP_H
 
+#include <android-base/result.h>
 #include <stdint.h>
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
-#include <utils/Tokenizer.h>
 #include <utils/RefBase.h>
+#include <utils/Tokenizer.h>
 
 namespace android {
 
@@ -60,9 +61,12 @@
  *
  * This object is immutable after it has been loaded.
  */
-class KeyLayoutMap : public RefBase {
+class KeyLayoutMap {
 public:
-    static status_t load(const std::string& filename, sp<KeyLayoutMap>* outMap);
+    static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename);
+    static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer);
+    static base::Result<std::shared_ptr<KeyLayoutMap>> loadContents(const std::string& filename,
+                                                                    const char* contents);
 
     status_t mapKey(int32_t scanCode, int32_t usageCode,
             int32_t* outKeyCode, uint32_t* outFlags) const;
@@ -71,8 +75,8 @@
     status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const;
 
     status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const;
+    const std::string getLoadFileName() const;
 
-protected:
     virtual ~KeyLayoutMap();
 
 private:
@@ -91,6 +95,7 @@
     KeyedVector<int32_t, AxisInfo> mAxes;
     KeyedVector<int32_t, Led> mLedsByScanCode;
     KeyedVector<int32_t, Led> mLedsByUsageCode;
+    std::string mLoadFileName;
 
     KeyLayoutMap();
 
diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h
index 92da10c..2ae4fd5 100644
--- a/include/input/Keyboard.h
+++ b/include/input/Keyboard.h
@@ -34,10 +34,10 @@
 class KeyMap {
 public:
     std::string keyLayoutFile;
-    sp<KeyLayoutMap> keyLayoutMap;
+    std::shared_ptr<KeyLayoutMap> keyLayoutMap;
 
     std::string keyCharacterMapFile;
-    sp<KeyCharacterMap> keyCharacterMap;
+    std::shared_ptr<KeyCharacterMap> keyCharacterMap;
 
     KeyMap();
     ~KeyMap();
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 861b589..9675a53 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -78,7 +78,7 @@
     // or dessert updates. Instead, apex users should use libbinder_ndk.
     apex_available: [
         "//apex_available:platform",
-        // TODO(b/139016109) remove these three
+        // TODO(b/166468760) remove these three
         "com.android.media.swcodec",
         "test_com.android.media.swcodec",
     ],
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 157538e..9aaca65 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -679,7 +679,7 @@
                 CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(),
                     ANDROID_LOG_ERROR);
             } else /* FATAL_IF_NOT_ONEWAY */ {
-                LOG_ALWAYS_FATAL("Process may not make oneway calls (code: %u).", code);
+                LOG_ALWAYS_FATAL("Process may not make non-oneway calls (code: %u).", code);
             }
         }
 
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index acc1e67..a530565 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -144,11 +144,9 @@
     }
 }
 
-bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData)
+bool ProcessState::becomeContextManager()
 {
     AutoMutex _l(mLock);
-    mBinderContextCheckFunc = checkFunc;
-    mBinderContextUserData = userData;
 
     flat_binder_object obj {
         .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
@@ -160,13 +158,11 @@
     if (result != 0) {
         android_errorWriteLog(0x534e4554, "121035042");
 
-        int dummy = 0;
-        result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+        int unused = 0;
+        result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &unused);
     }
 
     if (result == -1) {
-        mBinderContextCheckFunc = nullptr;
-        mBinderContextUserData = nullptr;
         ALOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno));
     }
 
@@ -397,14 +393,12 @@
     , mExecutingThreadsCount(0)
     , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
     , mStarvationStartTimeMs(0)
-    , mBinderContextCheckFunc(nullptr)
-    , mBinderContextUserData(nullptr)
     , mThreadPoolStarted(false)
     , mThreadPoolSeq(1)
     , mCallRestriction(CallRestriction::NONE)
 {
 
-// TODO(b/139016109): enforce in build system
+// TODO(b/166468760): enforce in build system
 #if defined(__ANDROID_APEX__)
     LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.");
 #endif
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 9f5346a..efb95f4 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -50,14 +50,8 @@
             sp<IBinder>         getContextObject(const sp<IBinder>& caller);
 
             void                startThreadPool();
-                        
-    typedef bool (*context_check_func)(const String16& name,
-                                       const sp<IBinder>& caller,
-                                       void* userData);
 
-            bool                becomeContextManager(
-                                    context_check_func checkFunc,
-                                    void* userData);
+            bool                becomeContextManager();
 
             sp<IBinder>         getStrongProxyForHandle(int32_t handle);
             void                expungeHandle(int32_t handle, IBinder* binder);
@@ -128,9 +122,6 @@
 
             Vector<handle_entry>mHandleToObject;
 
-            context_check_func  mBinderContextCheckFunc;
-            void*               mBinderContextUserData;
-
             String8             mRootDir;
             bool                mThreadPoolStarted;
     volatile int32_t            mThreadPoolSeq;
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index d4feaba..5811760 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -32,7 +32,8 @@
  *
  * \param binder local server binder to request security contexts on
  */
-void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) __INTRODUCED_IN(31);
+__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid)
+        __INTRODUCED_IN(31);
 
 /**
  * Returns the selinux context of the callee.
@@ -45,7 +46,7 @@
  * \return security context or null if unavailable. The lifetime of this context
  * is the lifetime of the transaction.
  */
-__attribute__((warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
+__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31);
 
 __END_DECLS
 
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 52bcd20..2784aa8 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -29,9 +29,9 @@
  * \param binder object to register globally with the service manager.
  * \param instance identifier of the service. This will be used to lookup the service.
  *
- * \return STATUS_OK on success.
+ * \return EX_NONE on success.
  */
-binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance);
+binder_exception_t AServiceManager_addService(AIBinder* binder, const char* instance);
 
 /**
  * Gets a binder object with this specific instance name. Will return nullptr immediately if the
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index 6b2184e..c782d47 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -29,14 +29,14 @@
 using ::android::status_t;
 using ::android::String16;
 
-binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance) {
+binder_exception_t AServiceManager_addService(AIBinder* binder, const char* instance) {
     if (binder == nullptr || instance == nullptr) {
-        return STATUS_UNEXPECTED_NULL;
+        return EX_ILLEGAL_ARGUMENT;
     }
 
     sp<IServiceManager> sm = defaultServiceManager();
-    status_t status = sm->addService(String16(instance), binder->getBinder());
-    return PruneStatusT(status);
+    status_t exception = sm->addService(String16(instance), binder->getBinder());
+    return PruneException(exception);
 }
 AIBinder* AServiceManager_checkService(const char* instance) {
     if (instance == nullptr) {
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp
index 87e1341..d889593 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -123,8 +123,8 @@
             return STATUS_UNKNOWN_ERROR;
 
         default:
-            LOG(WARNING) << __func__
-                         << ": Unknown status_t pruned into STATUS_UNKNOWN_ERROR: " << status;
+            LOG(WARNING) << __func__ << ": Unknown status_t (" << status
+                         << ") pruned into STATUS_UNKNOWN_ERROR";
             return STATUS_UNKNOWN_ERROR;
     }
 }
@@ -155,8 +155,8 @@
             return EX_TRANSACTION_FAILED;
 
         default:
-            LOG(WARNING) << __func__
-                         << ": Unknown status_t pruned into EX_TRANSACTION_FAILED: " << exception;
+            LOG(WARNING) << __func__ << ": Unknown binder exception (" << exception
+                         << ") pruned into EX_TRANSACTION_FAILED";
             return EX_TRANSACTION_FAILED;
     }
 }
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 1424b6c..2bd5248 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -84,10 +84,11 @@
 
     AIBinder_setRequestingSid(binder.get(), true);
 
-    binder_status_t status = AServiceManager_addService(binder.get(), kBinderNdkUnitTestService);
+    binder_exception_t exception =
+            AServiceManager_addService(binder.get(), kBinderNdkUnitTestService);
 
-    if (status != STATUS_OK) {
-        LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService;
+    if (exception != EX_NONE) {
+        LOG(FATAL) << "Could not register: " << exception << " " << kBinderNdkUnitTestService;
     }
 
     ABinderProcess_joinThreadPool();
@@ -111,10 +112,10 @@
 
 void manualService(const char* instance) {
     // Strong reference to MyFoo kept by service manager.
-    binder_status_t status = (new MyFoo)->addService(instance);
+    binder_exception_t exception = (new MyFoo)->addService(instance);
 
-    if (status != STATUS_OK) {
-        LOG(FATAL) << "Could not register: " << status << " " << instance;
+    if (exception != EX_NONE) {
+        LOG(FATAL) << "Could not register: " << exception << " " << instance;
     }
 }
 int manualPollingService(const char* instance) {
@@ -322,11 +323,20 @@
     }
 };
 
+TEST(NdkBinder, AddNullService) {
+    EXPECT_EQ(EX_ILLEGAL_ARGUMENT, AServiceManager_addService(nullptr, "any-service-name"));
+}
+
+TEST(NdkBinder, AddInvalidServiceName) {
+    sp<IFoo> foo = new MyTestFoo;
+    EXPECT_EQ(EX_ILLEGAL_ARGUMENT, foo->addService("!@#$%^&"));
+}
+
 TEST(NdkBinder, GetServiceInProcess) {
     static const char* kInstanceName = "test-get-service-in-process";
 
     sp<IFoo> foo = new MyTestFoo;
-    EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName));
+    EXPECT_EQ(EX_NONE, foo->addService(kInstanceName));
 
     sp<IFoo> getFoo = IFoo::getService(kInstanceName);
     EXPECT_EQ(foo.get(), getFoo.get());
@@ -373,8 +383,8 @@
     static const char* kInstanceName1 = "test-multi-1";
     static const char* kInstanceName2 = "test-multi-2";
     sp<IFoo> foo = new MyTestFoo;
-    EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName1));
-    EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName2));
+    EXPECT_EQ(EX_NONE, foo->addService(kInstanceName1));
+    EXPECT_EQ(EX_NONE, foo->addService(kInstanceName2));
     EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
 }
 
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 7974a06..0d0d102 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -72,6 +72,7 @@
         struct VSync {
             uint32_t count;
             nsecs_t expectedVSyncTimestamp __attribute__((aligned(8)));
+            nsecs_t deadlineTimestamp __attribute__((aligned(8)));
         };
 
         struct Hotplug {
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index dbd6293..34eba5b 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -153,14 +153,20 @@
     initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false);
 }
 
-InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) :
-        mId(other.mId), mGeneration(other.mGeneration), mControllerNumber(other.mControllerNumber),
-        mIdentifier(other.mIdentifier), mAlias(other.mAlias), mIsExternal(other.mIsExternal),
-        mHasMic(other.mHasMic), mSources(other.mSources),
-        mKeyboardType(other.mKeyboardType), mKeyCharacterMap(other.mKeyCharacterMap),
-        mHasVibrator(other.mHasVibrator), mHasButtonUnderPad(other.mHasButtonUnderPad),
-        mMotionRanges(other.mMotionRanges) {
-}
+InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other)
+      : mId(other.mId),
+        mGeneration(other.mGeneration),
+        mControllerNumber(other.mControllerNumber),
+        mIdentifier(other.mIdentifier),
+        mAlias(other.mAlias),
+        mIsExternal(other.mIsExternal),
+        mHasMic(other.mHasMic),
+        mSources(other.mSources),
+        mKeyboardType(other.mKeyboardType),
+        mKeyCharacterMap(other.mKeyCharacterMap),
+        mHasVibrator(other.mHasVibrator),
+        mHasButtonUnderPad(other.mHasButtonUnderPad),
+        mMotionRanges(other.mMotionRanges) {}
 
 InputDeviceInfo::~InputDeviceInfo() {
 }
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 0fa93d2..7ac8a2e 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -86,15 +86,14 @@
 
 // --- KeyCharacterMap ---
 
-sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap();
-
 KeyCharacterMap::KeyCharacterMap() :
     mType(KEYBOARD_TYPE_UNKNOWN) {
 }
 
-KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) :
-    RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode),
-    mKeysByUsageCode(other.mKeysByUsageCode) {
+KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other)
+      : mType(other.mType),
+        mKeysByScanCode(other.mKeysByScanCode),
+        mKeysByUsageCode(other.mKeysByUsageCode) {
     for (size_t i = 0; i < other.mKeys.size(); i++) {
         mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i)));
     }
@@ -107,104 +106,95 @@
     }
 }
 
-status_t KeyCharacterMap::load(const std::string& filename,
-        Format format, sp<KeyCharacterMap>* outMap) {
-    outMap->clear();
-
+base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename,
+                                                                     Format format) {
     Tokenizer* tokenizer;
     status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
     if (status) {
-        ALOGE("Error %d opening key character map file %s.", status, filename.c_str());
-    } else {
-        status = load(tokenizer, format, outMap);
-        delete tokenizer;
+        return Errorf("Error {} opening key character map file {}.", status, filename.c_str());
     }
-    return status;
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    auto ret = load(t.get(), format);
+    if (ret) {
+        (*ret)->mLoadFileName = filename;
+    }
+    return ret;
 }
 
-status_t KeyCharacterMap::loadContents(const std::string& filename, const char* contents,
-        Format format, sp<KeyCharacterMap>* outMap) {
-    outMap->clear();
-
+base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::loadContents(
+        const std::string& filename, const char* contents, Format format) {
     Tokenizer* tokenizer;
     status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
     if (status) {
         ALOGE("Error %d opening key character map.", status);
-    } else {
-        status = load(tokenizer, format, outMap);
-        delete tokenizer;
+        return Errorf("Error {} opening key character map.", status);
     }
-    return status;
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    auto ret = load(t.get(), format);
+    if (ret) {
+        (*ret)->mLoadFileName = filename;
+    }
+    return ret;
 }
 
-status_t KeyCharacterMap::load(Tokenizer* tokenizer,
-        Format format, sp<KeyCharacterMap>* outMap) {
+base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(Tokenizer* tokenizer,
+                                                                     Format format) {
     status_t status = OK;
-    sp<KeyCharacterMap> map = new KeyCharacterMap();
+    std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
     if (!map.get()) {
         ALOGE("Error allocating key character map.");
-        status = NO_MEMORY;
-    } else {
-#if DEBUG_PARSER_PERFORMANCE
-        nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
-        Parser parser(map.get(), tokenizer, format);
-        status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
-        nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
-        ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
-                tokenizer->getFilename().string(), tokenizer->getLineNumber(),
-                elapsedTime / 1000000.0);
-#endif
-        if (!status) {
-            *outMap = map;
-        }
+        return Errorf("Error allocating key character map.");
     }
-    return status;
+#if DEBUG_PARSER_PERFORMANCE
+    nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+    Parser parser(map.get(), tokenizer, format);
+    status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+    nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+    ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
+          tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0);
+#endif
+    if (status == OK) {
+        return map;
+    }
+
+    return Errorf("Load KeyCharacterMap failed {}.", status);
 }
 
-sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
-        const sp<KeyCharacterMap>& overlay) {
-    if (overlay == nullptr) {
-        return base;
-    }
-    if (base == nullptr) {
-        return overlay;
-    }
-
-    sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get());
-    for (size_t i = 0; i < overlay->mKeys.size(); i++) {
-        int32_t keyCode = overlay->mKeys.keyAt(i);
-        Key* key = overlay->mKeys.valueAt(i);
-        ssize_t oldIndex = map->mKeys.indexOfKey(keyCode);
+void KeyCharacterMap::combine(const KeyCharacterMap& overlay) {
+    for (size_t i = 0; i < overlay.mKeys.size(); i++) {
+        int32_t keyCode = overlay.mKeys.keyAt(i);
+        Key* key = overlay.mKeys.valueAt(i);
+        ssize_t oldIndex = mKeys.indexOfKey(keyCode);
         if (oldIndex >= 0) {
-            delete map->mKeys.valueAt(oldIndex);
-            map->mKeys.editValueAt(oldIndex) = new Key(*key);
+            delete mKeys.valueAt(oldIndex);
+            mKeys.editValueAt(oldIndex) = new Key(*key);
         } else {
-            map->mKeys.add(keyCode, new Key(*key));
+            mKeys.add(keyCode, new Key(*key));
         }
     }
 
-    for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) {
-        map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i),
-                overlay->mKeysByScanCode.valueAt(i));
+    for (size_t i = 0; i < overlay.mKeysByScanCode.size(); i++) {
+        mKeysByScanCode.replaceValueFor(overlay.mKeysByScanCode.keyAt(i),
+                                        overlay.mKeysByScanCode.valueAt(i));
     }
 
-    for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) {
-        map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i),
-                overlay->mKeysByUsageCode.valueAt(i));
+    for (size_t i = 0; i < overlay.mKeysByUsageCode.size(); i++) {
+        mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i),
+                                         overlay.mKeysByUsageCode.valueAt(i));
     }
-    return map;
-}
-
-sp<KeyCharacterMap> KeyCharacterMap::empty() {
-    return sEmpty;
+    mLoadFileName = overlay.mLoadFileName;
 }
 
 int32_t KeyCharacterMap::getKeyboardType() const {
     return mType;
 }
 
+const std::string KeyCharacterMap::getLoadFileName() const {
+    return mLoadFileName;
+}
+
 char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const {
     char16_t result = 0;
     const Key* key;
@@ -601,8 +591,12 @@
 }
 
 #ifdef __ANDROID__
-sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
-    sp<KeyCharacterMap> map = new KeyCharacterMap();
+std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return nullptr;
+    }
+    std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap());
     map->mType = parcel->readInt32();
     size_t numKeys = parcel->readInt32();
     if (parcel->errorCheck()) {
@@ -657,6 +651,10 @@
 }
 
 void KeyCharacterMap::writeToParcel(Parcel* parcel) const {
+    if (parcel == nullptr) {
+        ALOGE("%s: Null parcel", __func__);
+        return;
+    }
     parcel->writeInt32(mType);
 
     size_t numKeys = mKeys.size();
diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 9c399b3..16ce48a 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -49,37 +49,60 @@
 KeyLayoutMap::~KeyLayoutMap() {
 }
 
-status_t KeyLayoutMap::load(const std::string& filename, sp<KeyLayoutMap>* outMap) {
-    outMap->clear();
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename,
+                                                                       const char* contents) {
+    Tokenizer* tokenizer;
+    status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer);
+    if (status) {
+        ALOGE("Error %d opening key layout map.", status);
+        return Errorf("Error {} opening key layout map file {}.", status, filename.c_str());
+    }
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    auto ret = load(t.get());
+    if (ret) {
+        (*ret)->mLoadFileName = filename;
+    }
+    return ret;
+}
 
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename) {
     Tokenizer* tokenizer;
     status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer);
     if (status) {
         ALOGE("Error %d opening key layout map file %s.", status, filename.c_str());
-    } else {
-        sp<KeyLayoutMap> map = new KeyLayoutMap();
-        if (!map.get()) {
-            ALOGE("Error allocating key layout map.");
-            status = NO_MEMORY;
-        } else {
-#if DEBUG_PARSER_PERFORMANCE
-            nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
-            Parser parser(map.get(), tokenizer);
-            status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
-            nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
-            ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
-                    tokenizer->getFilename().string(), tokenizer->getLineNumber(),
-                    elapsedTime / 1000000.0);
-#endif
-            if (!status) {
-                *outMap = map;
-            }
-        }
-        delete tokenizer;
+        return Errorf("Error {} opening key layout map file {}.", status, filename.c_str());
     }
-    return status;
+    std::unique_ptr<Tokenizer> t(tokenizer);
+    auto ret = load(t.get());
+    if (ret) {
+        (*ret)->mLoadFileName = filename;
+    }
+    return ret;
+}
+
+base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(Tokenizer* tokenizer) {
+    std::shared_ptr<KeyLayoutMap> map = std::shared_ptr<KeyLayoutMap>(new KeyLayoutMap());
+    status_t status = OK;
+    if (!map.get()) {
+        ALOGE("Error allocating key layout map.");
+        return Errorf("Error allocating key layout map.");
+    } else {
+#if DEBUG_PARSER_PERFORMANCE
+        nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+#endif
+        Parser parser(map.get(), tokenizer);
+        status = parser.parse();
+#if DEBUG_PARSER_PERFORMANCE
+        nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
+        ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.",
+              tokenizer->getFilename().string(), tokenizer->getLineNumber(),
+              elapsedTime / 1000000.0);
+#endif
+        if (!status) {
+            return std::move(map);
+        }
+    }
+    return Errorf("Load KeyLayoutMap failed {}.", status);
 }
 
 status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode,
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 25025f2..38a68b3 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -110,11 +110,11 @@
         return NAME_NOT_FOUND;
     }
 
-    status_t status = KeyLayoutMap::load(path, &keyLayoutMap);
-    if (status) {
-        return status;
+    base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path);
+    if (!ret) {
+        return ret.error().code();
     }
-
+    keyLayoutMap = *ret;
     keyLayoutFile = path;
     return OK;
 }
@@ -127,12 +127,12 @@
         return NAME_NOT_FOUND;
     }
 
-    status_t status = KeyCharacterMap::load(path,
-            KeyCharacterMap::FORMAT_BASE, &keyCharacterMap);
-    if (status) {
-        return status;
+    base::Result<std::shared_ptr<KeyCharacterMap>> ret =
+            KeyCharacterMap::load(path, KeyCharacterMap::FORMAT_BASE);
+    if (!ret) {
+        return ret.error().code();
     }
-
+    keyCharacterMap = *ret;
     keyCharacterMapFile = path;
     return OK;
 }
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 3dcb498..9264bb6 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -74,10 +74,6 @@
 cc_library_static {
     name: "librenderengine",
     defaults: ["librenderengine_defaults"],
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
     double_loadable: true,
     clang: true,
     cflags: [
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 672ffae..3dcfebe 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -1276,7 +1276,8 @@
 
     if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) {
         glEnable(GL_BLEND);
-        glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        glBlendFuncSeparate(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
+                            GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
     } else {
         glDisable(GL_BLEND);
     }
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index ba17143..c92e817 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -919,7 +919,7 @@
 
 void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() {
     fillRedBufferWithoutPremultiplyAlpha();
-    expectBufferColor(fullscreenRect(), 128, 0, 0, 64, 1);
+    expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1);
 }
 
 void RenderEngineTest::clearLeftRegion() {
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 6b1bb40..e1fb45c 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -515,6 +515,10 @@
     }
     out += "(" + transformToString(type) + ")\n";
 
+    if (type == IDENTITY) {
+        return;
+    }
+
     for (size_t i = 0; i < 3; i++) {
         StringAppendF(&out, "%s    %.4f  %.4f  %.4f\n", prefix, static_cast<double>(mMatrix[0][i]),
                       static_cast<double>(mMatrix[1][i]), static_cast<double>(mMatrix[2][i]));
diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp
index 6366e1d..000cf27 100644
--- a/services/gpuservice/tracing/GpuMemTracer.cpp
+++ b/services/gpuservice/tracing/GpuMemTracer.cpp
@@ -80,6 +80,7 @@
     mGpuMem->traverseGpuMemTotals([](int64_t ts, uint32_t gpuId, uint32_t pid, uint64_t size) {
         GpuMemDataSource::Trace([&](GpuMemDataSource::TraceContext ctx) {
             auto packet = ctx.NewTracePacket();
+            packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
             packet->set_timestamp(ts);
             auto* event = packet->set_gpu_mem_total_event();
             event->set_gpu_id(gpuId);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 0980107..b428c4e 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -764,7 +764,7 @@
                 "Must provide a valid touch state if adding portal windows or outside targets");
     }
     // Traverse windows from front to back to find touched window.
-    const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
         const InputWindowInfo* windowInfo = windowHandle->getInfo();
         if (windowInfo->displayId == displayId) {
@@ -1029,7 +1029,7 @@
     return true;
 }
 
-void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus,
+void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
                                               std::string_view reason) {
     if (mPendingEvent != nullptr) {
         // Move the pending event to the front of the queue. This will give the chance
@@ -1039,7 +1039,7 @@
     }
 
     FocusEntry* focusEntry =
-            new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus, reason);
+            new FocusEntry(mIdGenerator.nextId(), now(), windowToken, hasFocus, reason);
 
     // This event should go to the front of the queue, but behind all other focus events
     // Find the last focus event, and insert right after it
@@ -1122,10 +1122,10 @@
         if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
             std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
                     &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
-            sp<InputWindowHandle> focusedWindowHandle =
-                    getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(*entry));
-            if (focusedWindowHandle != nullptr) {
-                commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken());
+            sp<IBinder> focusedWindowToken =
+                    getValueByKey(mFocusedWindowTokenByDisplay, getTargetDisplayId(*entry));
+            if (focusedWindowToken != nullptr) {
+                commandEntry->inputChannel = getInputChannelLocked(focusedWindowToken);
             }
             commandEntry->keyEntry = entry;
             postCommandLocked(std::move(commandEntry));
@@ -1414,8 +1414,7 @@
     std::string reason;
 
     int32_t displayId = getTargetDisplayId(entry);
-    sp<InputWindowHandle> focusedWindowHandle =
-            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+    sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
     std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =
             getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
 
@@ -1838,7 +1837,7 @@
         sp<InputWindowHandle> foregroundWindowHandle =
                 tempTouchState.getFirstForegroundWindowHandle();
         if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) {
-            const std::vector<sp<InputWindowHandle>> windowHandles =
+            const std::vector<sp<InputWindowHandle>>& windowHandles =
                     getWindowHandlesLocked(displayId);
             for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
                 const InputWindowInfo* info = windowHandle->getInfo();
@@ -2070,7 +2069,7 @@
 bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
                                                     int32_t x, int32_t y) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
-    const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
     for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
         if (windowHandle == otherHandle) {
             break; // All future windows are below us. Exit early.
@@ -2086,7 +2085,7 @@
 
 bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const {
     int32_t displayId = windowHandle->getInfo()->displayId;
-    const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId);
+    const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
     const InputWindowInfo* windowInfo = windowHandle->getInfo();
     for (const sp<InputWindowHandle>& otherHandle : windowHandles) {
         if (windowHandle == otherHandle) {
@@ -2123,8 +2122,7 @@
         return;
     }
     int32_t displayId = getTargetDisplayId(eventEntry);
-    sp<InputWindowHandle> focusedWindowHandle =
-            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
+    sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
     if (focusedWindowHandle != nullptr) {
         const InputWindowInfo* info = focusedWindowHandle->getInfo();
         if (info->inputFeatures.test(InputWindowInfo::Feature::DISABLE_USER_ACTIVITY)) {
@@ -2461,30 +2459,22 @@
 }
 
 void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action,
-                                                      const sp<IBinder>& newToken) {
+                                                      const sp<IBinder>& token) {
     int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
     uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK;
     if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) {
         return;
     }
 
-    sp<InputWindowHandle> inputWindowHandle = getWindowHandleLocked(newToken);
-    if (inputWindowHandle == nullptr) {
-        return;
-    }
-
-    sp<InputWindowHandle> focusedWindowHandle =
-            getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
-
-    bool hasFocusChanged = !focusedWindowHandle || focusedWindowHandle->getToken() != newToken;
-
-    if (!hasFocusChanged) {
+    sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+    if (focusedToken == token) {
+        // ignore since token is focused
         return;
     }
 
     std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
             &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
-    commandEntry->newToken = newToken;
+    commandEntry->newToken = token;
     postCommandLocked(std::move(commandEntry));
 }
 
@@ -3606,9 +3596,11 @@
     }
 }
 
-std::vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(
+const std::vector<sp<InputWindowHandle>>& InputDispatcher::getWindowHandlesLocked(
         int32_t displayId) const {
-    return getValueByKey(mWindowHandlesByDisplay, displayId);
+    static const std::vector<sp<InputWindowHandle>> EMPTY_WINDOW_HANDLES;
+    auto it = mWindowHandlesByDisplay.find(displayId);
+    return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES;
 }
 
 sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
@@ -3618,7 +3610,7 @@
     }
 
     for (auto& it : mWindowHandlesByDisplay) {
-        const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
+        const std::vector<sp<InputWindowHandle>>& windowHandles = it.second;
         for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
             if (windowHandle->getToken() == windowHandleToken) {
                 return windowHandle;
@@ -3628,9 +3620,28 @@
     return nullptr;
 }
 
+sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
+                                                             int displayId) const {
+    if (windowHandleToken == nullptr) {
+        return nullptr;
+    }
+
+    for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
+        if (windowHandle->getToken() == windowHandleToken) {
+            return windowHandle;
+        }
+    }
+    return nullptr;
+}
+
+sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
+    sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+    return getWindowHandleLocked(focusedToken, displayId);
+}
+
 bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const {
     for (auto& it : mWindowHandlesByDisplay) {
-        const std::vector<sp<InputWindowHandle>> windowHandles = it.second;
+        const std::vector<sp<InputWindowHandle>>& windowHandles = it.second;
         for (const sp<InputWindowHandle>& handle : windowHandles) {
             if (handle->getId() == windowHandle->getId() &&
                 handle->getToken() == windowHandle->getToken()) {
@@ -3781,13 +3792,13 @@
 
     updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);
 
-    sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
+    sp<IBinder> newFocusedToken = nullptr;
     bool foundHoveredWindow = false;
     for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
-        // Set newFocusedWindowHandle to the top most focused window instead of the last one
-        if (!newFocusedWindowHandle && windowHandle->getInfo()->focusable &&
+        // Set newFocusedToken to the top most focused window instead of the last one
+        if (!newFocusedToken && windowHandle->getInfo()->focusable &&
             windowHandle->getInfo()->visible) {
-            newFocusedWindowHandle = windowHandle;
+            newFocusedToken = windowHandle->getToken();
         }
         if (windowHandle == mLastHoverWindowHandle) {
             foundHoveredWindow = true;
@@ -3798,12 +3809,9 @@
         mLastHoverWindowHandle = nullptr;
     }
 
-    sp<InputWindowHandle> oldFocusedWindowHandle =
-            getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
-
-    if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
-        onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle, displayId,
-                             "setInputWindowsLocked");
+    sp<IBinder> oldFocusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+    if (oldFocusedToken != newFocusedToken) {
+        onFocusChangedLocked(oldFocusedToken, newFocusedToken, displayId, "setInputWindowsLocked");
     }
 
     std::unordered_map<int32_t, TouchState>::iterator stateIt =
@@ -3899,11 +3907,11 @@
         std::scoped_lock _l(mLock);
 
         if (mFocusedDisplayId != displayId) {
-            sp<InputWindowHandle> oldFocusedWindowHandle =
-                    getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId);
-            if (oldFocusedWindowHandle != nullptr) {
+            sp<IBinder> oldFocusedWindowToken =
+                    getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+            if (oldFocusedWindowToken != nullptr) {
                 std::shared_ptr<InputChannel> inputChannel =
-                        getInputChannelLocked(oldFocusedWindowHandle->getToken());
+                        getInputChannelLocked(oldFocusedWindowToken);
                 if (inputChannel != nullptr) {
                     CancelationOptions
                             options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
@@ -3915,19 +3923,15 @@
             mFocusedDisplayId = displayId;
 
             // Find new focused window and validate
-            sp<InputWindowHandle> newFocusedWindowHandle =
-                    getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
-            notifyFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+            sp<IBinder> newFocusedWindowToken =
+                    getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+            notifyFocusChangedLocked(oldFocusedWindowToken, newFocusedWindowToken);
 
-            if (newFocusedWindowHandle == nullptr) {
+            if (newFocusedWindowToken == nullptr) {
                 ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
-                if (!mFocusedWindowHandlesByDisplay.empty()) {
-                    ALOGE("But another display has a focused window:");
-                    for (auto& it : mFocusedWindowHandlesByDisplay) {
-                        const sp<InputWindowHandle>& windowHandle = it.second;
-                        ALOGE("Display #%" PRId32 " has focused window: '%s'\n", it.first,
-                              windowHandle->getName().c_str());
-                    }
+                if (!mFocusedWindowTokenByDisplay.empty()) {
+                    ALOGE("But another display has a focused window\n%s",
+                          dumpFocusedWindowsLocked().c_str());
                 }
             }
         }
@@ -4112,6 +4116,28 @@
     }
 }
 
+std::string InputDispatcher::dumpFocusedWindowsLocked() {
+    if (mFocusedWindowTokenByDisplay.empty()) {
+        return INDENT "FocusedWindows: <none>\n";
+    }
+
+    std::string dump;
+    dump += INDENT "FocusedWindows:\n";
+    for (auto& it : mFocusedWindowTokenByDisplay) {
+        const int32_t displayId = it.first;
+        const sp<InputWindowHandle> windowHandle = getFocusedWindowHandleLocked(displayId);
+        if (windowHandle) {
+            dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
+                                 windowHandle->getName().c_str());
+        } else {
+            dump += StringPrintf(INDENT2 "displayId=%" PRId32
+                                         " has focused token without a window'\n",
+                                 displayId);
+        }
+    }
+    return dump;
+}
+
 void InputDispatcher::dumpDispatchStateLocked(std::string& dump) {
     dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
     dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
@@ -4133,17 +4159,7 @@
         dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
     }
 
-    if (!mFocusedWindowHandlesByDisplay.empty()) {
-        dump += StringPrintf(INDENT "FocusedWindows:\n");
-        for (auto& it : mFocusedWindowHandlesByDisplay) {
-            const int32_t displayId = it.first;
-            const sp<InputWindowHandle>& windowHandle = it.second;
-            dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
-                                 windowHandle->getName().c_str());
-        }
-    } else {
-        dump += StringPrintf(INDENT "FocusedWindows: <none>\n");
-    }
+    dump += dumpFocusedWindowsLocked();
 
     if (!mTouchStatesByDisplay.empty()) {
         dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
@@ -4589,10 +4605,8 @@
     postCommandLocked(std::move(commandEntry));
 }
 
-void InputDispatcher::notifyFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
-                                               const sp<InputWindowHandle>& newFocus) {
-    sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr;
-    sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr;
+void InputDispatcher::notifyFocusChangedLocked(const sp<IBinder>& oldToken,
+                                               const sp<IBinder>& newToken) {
     std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
             &InputDispatcher::doNotifyFocusChangedLockedInterruptible);
     commandEntry->oldToken = oldToken;
@@ -5171,35 +5185,26 @@
  */
 void InputDispatcher::setFocusedWindow(const FocusRequest& request) {}
 
-void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocusedWindowHandle,
-                                           const sp<InputWindowHandle>& newFocusedWindowHandle,
-                                           int32_t displayId, std::string_view reason) {
-    if (oldFocusedWindowHandle) {
-        if (DEBUG_FOCUS) {
-            ALOGD("Focus left window: %s in display %" PRId32,
-                  oldFocusedWindowHandle->getName().c_str(), displayId);
-        }
-        std::shared_ptr<InputChannel> focusedInputChannel =
-                getInputChannelLocked(oldFocusedWindowHandle->getToken());
+void InputDispatcher::onFocusChangedLocked(const sp<IBinder>& oldFocusedToken,
+                                           const sp<IBinder>& newFocusedToken, int32_t displayId,
+                                           std::string_view reason) {
+    if (oldFocusedToken) {
+        std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(oldFocusedToken);
         if (focusedInputChannel) {
             CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                                        "focus left window");
             synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
-            enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/, reason);
+            enqueueFocusEventLocked(oldFocusedToken, false /*hasFocus*/, reason);
         }
-        mFocusedWindowHandlesByDisplay.erase(displayId);
+        mFocusedWindowTokenByDisplay.erase(displayId);
     }
-    if (newFocusedWindowHandle) {
-        if (DEBUG_FOCUS) {
-            ALOGD("Focus entered window: %s in display %" PRId32,
-                  newFocusedWindowHandle->getName().c_str(), displayId);
-        }
-        mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
-        enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/, reason);
+    if (newFocusedToken) {
+        mFocusedWindowTokenByDisplay[displayId] = newFocusedToken;
+        enqueueFocusEventLocked(newFocusedToken, true /*hasFocus*/, reason);
     }
 
     if (mFocusedDisplayId == displayId) {
-        notifyFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle);
+        notifyFocusChangedLocked(oldFocusedToken, newFocusedToken);
     }
 }
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index e452ca2..93d3fde 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -171,7 +171,7 @@
     void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock);
 
     // Enqueues a focus event.
-    void enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus,
+    void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
                                  std::string_view reason) REQUIRES(mLock);
 
     // Adds an event to a queue of recent events for debugging purposes.
@@ -293,13 +293,19 @@
             GUARDED_BY(mLock);
     void setInputWindowsLocked(const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
                                int32_t displayId) REQUIRES(mLock);
-    // Get window handles by display, return an empty vector if not found.
-    std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const
+    // Get a reference to window handles by display, return an empty vector if not found.
+    const std::vector<sp<InputWindowHandle>>& getWindowHandlesLocked(int32_t displayId) const
             REQUIRES(mLock);
     sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const
             REQUIRES(mLock);
+
+    // Same function as above, but faster. Since displayId is provided, this avoids the need
+    // to loop through all displays.
+    sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken,
+                                                int displayId) const REQUIRES(mLock);
     std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const
             REQUIRES(mLock);
+    sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock);
     bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
     bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock);
 
@@ -310,9 +316,11 @@
             const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
             REQUIRES(mLock);
 
-    // Focus tracking for keys, trackball, etc.
-    std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
-            GUARDED_BY(mLock);
+    // Focus tracking for keys, trackball, etc. A window token can be associated with one or more
+    // InputWindowHandles. If a window is mirrored, the window and its mirror will share the same
+    // token. Focus is tracked by the token per display and the events are dispatched to the
+    // channel associated by this token.
+    std::unordered_map<int32_t, sp<IBinder>> mFocusedWindowTokenByDisplay GUARDED_BY(mLock);
 
     std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
 
@@ -479,6 +487,7 @@
     void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
     void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
     void logDispatchStateLocked() REQUIRES(mLock);
+    std::string dumpFocusedWindowsLocked() REQUIRES(mLock);
 
     // Registration.
     void removeMonitorChannelLocked(const InputChannel& inputChannel) REQUIRES(mLock);
@@ -493,11 +502,10 @@
                                        uint32_t seq, bool handled) REQUIRES(mLock);
     void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
             REQUIRES(mLock);
-    void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
-                              const sp<InputWindowHandle>& newFocus, int32_t displayId,
-                              std::string_view reason) REQUIRES(mLock);
-    void notifyFocusChangedLocked(const sp<InputWindowHandle>& oldFocus,
-                                  const sp<InputWindowHandle>& newFocus) REQUIRES(mLock);
+    void onFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus,
+                              int32_t displayId, std::string_view reason) REQUIRES(mLock);
+    void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
+            REQUIRES(mLock);
     void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
     void onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) REQUIRES(mLock);
     void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason)
diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp
index 3496a24..9e797e4 100644
--- a/services/inputflinger/host/Android.bp
+++ b/services/inputflinger/host/Android.bp
@@ -23,6 +23,7 @@
 
     header_libs: ["jni_headers"],
     shared_libs: [
+        "libbase",
         "libbinder",
         "libcrypto",
         "libcutils",
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index a24b293..573524e 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -341,7 +341,7 @@
     virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) = 0;
 
     /* Gets the keyboard layout for a particular input device. */
-    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(
+    virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
             const InputDeviceIdentifier& identifier) = 0;
 
     /* Gets a user-supplied alias for a particular input device, or an empty string if none. */
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 76b9419..67d3cc2 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -216,10 +216,7 @@
     return !isVirtual && enabled;
 }
 
-const sp<KeyCharacterMap>& EventHub::Device::getKeyCharacterMap() const {
-    if (combinedKeyMap != nullptr) {
-        return combinedKeyMap;
-    }
+const std::shared_ptr<KeyCharacterMap> EventHub::Device::getKeyCharacterMap() const {
     return keyMap.keyCharacterMap;
 }
 
@@ -661,8 +658,8 @@
 
     if (device != nullptr) {
         // Check the key character map first.
-        sp<KeyCharacterMap> kcm = device->getKeyCharacterMap();
-        if (kcm != nullptr) {
+        const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
+        if (kcm) {
             if (!kcm->mapKey(scanCode, usageCode, outKeycode)) {
                 *outFlags = 0;
                 status = NO_ERROR;
@@ -677,7 +674,7 @@
         }
 
         if (status == NO_ERROR) {
-            if (kcm != nullptr) {
+            if (kcm) {
                 kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState);
             } else {
                 *outMetaState = metaState;
@@ -754,7 +751,7 @@
     }
 }
 
-sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
+const std::shared_ptr<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
     if (device != nullptr) {
@@ -763,15 +760,13 @@
     return nullptr;
 }
 
-bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) {
+bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map) {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
-    if (device != nullptr) {
-        if (map != device->overlayKeyMap) {
-            device->overlayKeyMap = map;
-            device->combinedKeyMap = KeyCharacterMap::combine(device->keyMap.keyCharacterMap, map);
-            return true;
-        }
+    if (device != nullptr && map != nullptr && device->keyMap.keyCharacterMap != nullptr) {
+        device->keyMap.keyCharacterMap->combine(*map);
+        device->keyMap.keyCharacterMapFile = device->keyMap.keyCharacterMap->getLoadFileName();
+        return true;
     }
     return false;
 }
@@ -979,18 +974,30 @@
             mNeedToSendFinishedDeviceScan = true;
         }
 
-        for (auto it = mOpeningDevices.begin(); it != mOpeningDevices.end();) {
-            std::unique_ptr<Device> device = std::move(*it);
+        while (!mOpeningDevices.empty()) {
+            std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
+            mOpeningDevices.pop_back();
             ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
             event->when = now;
             event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
             event->type = DEVICE_ADDED;
             event += 1;
-            auto [dev_it, insert] = mDevices.insert_or_assign(device->id, std::move(device));
-            if (!insert) {
+
+            // Try to find a matching video device by comparing device names
+            for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end();
+                 it++) {
+                std::unique_ptr<TouchVideoDevice>& videoDevice = *it;
+                if (tryAddVideoDevice(*device, videoDevice)) {
+                    // videoDevice was transferred to 'device'
+                    it = mUnattachedVideoDevices.erase(it);
+                    break;
+                }
+            }
+
+            auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device));
+            if (!inserted) {
                 ALOGW("Device id %d exists, replaced.", device->id);
             }
-            it = mOpeningDevices.erase(it);
             mNeedToSendFinishedDeviceScan = true;
             if (--capacity == 0) {
                 break;
@@ -1536,21 +1543,6 @@
         device->setLedForControllerLocked();
     }
 
-    // Find a matching video device by comparing device names
-    // This should be done before registerDeviceForEpollLocked, so that both fds are added to epoll
-    for (std::unique_ptr<TouchVideoDevice>& videoDevice : mUnattachedVideoDevices) {
-        if (device->identifier.name == videoDevice->getName()) {
-            device->videoDevice = std::move(videoDevice);
-            break;
-        }
-    }
-    mUnattachedVideoDevices
-            .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(),
-                                  [](const std::unique_ptr<TouchVideoDevice>& videoDevice) {
-                                      return videoDevice == nullptr;
-                                  }),
-                   mUnattachedVideoDevices.end());
-
     if (registerDeviceForEpollLocked(*device) != OK) {
         return -1;
     }
@@ -1576,12 +1568,8 @@
     }
     // Transfer ownership of this video device to a matching input device
     for (const auto& [id, device] : mDevices) {
-        if (videoDevice->getName() == device->identifier.name) {
-            device->videoDevice = std::move(videoDevice);
-            if (device->enabled) {
-                registerVideoDeviceForEpollLocked(*device->videoDevice);
-            }
-            return;
+        if (tryAddVideoDevice(*device, videoDevice)) {
+            return; // 'device' now owns 'videoDevice'
         }
     }
 
@@ -1592,6 +1580,18 @@
     mUnattachedVideoDevices.push_back(std::move(videoDevice));
 }
 
+bool EventHub::tryAddVideoDevice(EventHub::Device& device,
+                                 std::unique_ptr<TouchVideoDevice>& videoDevice) {
+    if (videoDevice->getName() != device.identifier.name) {
+        return false;
+    }
+    device.videoDevice = std::move(videoDevice);
+    if (device.enabled) {
+        registerVideoDeviceForEpollLocked(*device.videoDevice);
+    }
+    return true;
+}
+
 bool EventHub::isDeviceEnabled(int32_t deviceId) {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
@@ -1848,8 +1848,6 @@
                                  device->keyMap.keyCharacterMapFile.c_str());
             dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n",
                                  device->configurationFile.c_str());
-            dump += StringPrintf(INDENT3 "HaveKeyboardLayoutOverlay: %s\n",
-                                 toString(device->overlayKeyMap != nullptr));
             dump += INDENT3 "VideoDevice: ";
             if (device->videoDevice) {
                 dump += device->videoDevice->dump() + "\n";
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index c63c179..b224476 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -240,7 +240,7 @@
 
         if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
             if (!mClasses.test(InputDeviceClass::VIRTUAL)) {
-                sp<KeyCharacterMap> keyboardLayout =
+                std::shared_ptr<KeyCharacterMap> keyboardLayout =
                         mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier);
                 bool shouldBumpGeneration = false;
                 for_each_subdevice(
@@ -481,6 +481,10 @@
     return count;
 }
 
+void InputDevice::updateLedState(bool reset) {
+    for_each_mapper([reset](InputMapper& mapper) { mapper.updateLedState(reset); });
+}
+
 InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId)
       : mDevice(device),
         mContext(device.getContext()),
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index bb1ccbf..feaacb3 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -47,6 +47,7 @@
         mEventHub(eventHub),
         mPolicy(policy),
         mGlobalMetaState(0),
+        mLedMetaState(AMETA_NUM_LOCK_ON),
         mGeneration(1),
         mNextInputDeviceId(END_RESERVED_ID),
         mDisableVirtualKeysTimeout(LLONG_MIN),
@@ -353,6 +354,18 @@
     return mGlobalMetaState;
 }
 
+void InputReader::updateLedMetaStateLocked(int32_t metaState) {
+    mLedMetaState = metaState;
+    for (auto& devicePair : mDevices) {
+        std::shared_ptr<InputDevice>& device = devicePair.second;
+        device->updateLedState(false);
+    }
+}
+
+int32_t InputReader::getLedMetaStateLocked() {
+    return mLedMetaState;
+}
+
 void InputReader::notifyExternalStylusPresenceChanged() {
     refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE);
 }
@@ -710,6 +723,16 @@
     return mReader->getGlobalMetaStateLocked();
 }
 
+void InputReader::ContextImpl::updateLedMetaState(int32_t metaState) {
+    // lock is already held by the input loop
+    mReader->updateLedMetaStateLocked(metaState);
+}
+
+int32_t InputReader::ContextImpl::getLedMetaState() {
+    // lock is already held by the input loop
+    return mReader->getLedMetaStateLocked();
+}
+
 void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) {
     // lock is already held by the input loop
     mReader->disableVirtualKeysUntilLocked(time);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index ff12d98..aec4837 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -226,8 +226,9 @@
     virtual void getVirtualKeyDefinitions(
             int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
 
-    virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
-    virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0;
+    virtual const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
+    virtual bool setKeyboardLayoutOverlay(int32_t deviceId,
+                                          std::shared_ptr<KeyCharacterMap> map) = 0;
 
     /* Control the vibrator. */
     virtual void vibrate(int32_t deviceId, const VibrationElement& effect) = 0;
@@ -373,8 +374,10 @@
             int32_t deviceId,
             std::vector<VirtualKeyDefinition>& outVirtualKeys) const override final;
 
-    sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override final;
-    bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) override final;
+    const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(
+            int32_t deviceId) const override final;
+    bool setKeyboardLayoutOverlay(int32_t deviceId,
+                                  std::shared_ptr<KeyCharacterMap> map) override final;
 
     void vibrate(int32_t deviceId, const VibrationElement& effect) override final;
     void cancelVibrate(int32_t deviceId) override final;
@@ -421,9 +424,6 @@
         std::unique_ptr<VirtualKeyMap> virtualKeyMap;
         KeyMap keyMap;
 
-        sp<KeyCharacterMap> overlayKeyMap;
-        sp<KeyCharacterMap> combinedKeyMap;
-
         bool ffEffectPlaying;
         int16_t ffEffectId; // initially -1
 
@@ -441,7 +441,7 @@
         bool hasValidFd() const;
         const bool isVirtual; // set if fd < 0 is passed to constructor
 
-        const sp<KeyCharacterMap>& getKeyCharacterMap() const;
+        const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const;
 
         template <std::size_t N>
         status_t readDeviceBitMask(unsigned long ioctlCode, BitArray<N>& bitArray);
@@ -460,6 +460,14 @@
 
     status_t openDeviceLocked(const std::string& devicePath);
     void openVideoDeviceLocked(const std::string& devicePath);
+    /**
+     * Try to associate a video device with an input device. If the association succeeds,
+     * the videoDevice is moved into the input device. 'videoDevice' will become null if this
+     * happens.
+     * Return true if the association succeeds.
+     * Return false otherwise.
+     */
+    bool tryAddVideoDevice(Device& device, std::unique_ptr<TouchVideoDevice>& videoDevice);
     void createVirtualKeyboardLocked();
     void addDeviceLocked(std::unique_ptr<Device> device);
     void assignDescriptorLocked(InputDeviceIdentifier& identifier);
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index da36a48..7f5e1c8 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -98,6 +98,8 @@
 
     std::optional<int32_t> getAssociatedDisplayId();
 
+    void updateLedState(bool reset);
+
     size_t getMapperCount();
 
     // construct and add a mapper to the input device
@@ -259,10 +261,10 @@
     inline void getVirtualKeyDefinitions(std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
         return mEventHub->getVirtualKeyDefinitions(mId, outVirtualKeys);
     }
-    inline sp<KeyCharacterMap> getKeyCharacterMap() const {
+    inline const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const {
         return mEventHub->getKeyCharacterMap(mId);
     }
-    inline bool setKeyboardLayoutOverlay(const sp<KeyCharacterMap>& map) {
+    inline bool setKeyboardLayoutOverlay(std::shared_ptr<KeyCharacterMap> map) {
         return mEventHub->setKeyboardLayoutOverlay(mId, map);
     }
     inline void vibrate(const VibrationElement& element) {
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 9cb2052..2d6ccf5 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -55,34 +55,32 @@
                 const sp<InputListenerInterface>& listener);
     virtual ~InputReader();
 
-    virtual void dump(std::string& dump) override;
-    virtual void monitor() override;
+    void dump(std::string& dump) override;
+    void monitor() override;
 
-    virtual status_t start() override;
-    virtual status_t stop() override;
+    status_t start() override;
+    status_t stop() override;
 
-    virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
+    void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
 
-    virtual bool isInputDeviceEnabled(int32_t deviceId) override;
+    bool isInputDeviceEnabled(int32_t deviceId) override;
 
-    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
-                                     int32_t scanCode) override;
-    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
-                                    int32_t keyCode) override;
-    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
+    int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) override;
+    int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override;
+    int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
 
-    virtual void toggleCapsLockState(int32_t deviceId) override;
+    void toggleCapsLockState(int32_t deviceId) override;
 
-    virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
-                         const int32_t* keyCodes, uint8_t* outFlags) override;
+    bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
+                 uint8_t* outFlags) override;
 
-    virtual void requestRefreshConfiguration(uint32_t changes) override;
+    void requestRefreshConfiguration(uint32_t changes) override;
 
-    virtual void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern,
-                         ssize_t repeat, int32_t token) override;
-    virtual void cancelVibrate(int32_t deviceId, int32_t token) override;
+    void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern, ssize_t repeat,
+                 int32_t token) override;
+    void cancelVibrate(int32_t deviceId, int32_t token) override;
 
-    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
+    bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
 
 protected:
     // These members are protected so they can be instrumented by test cases.
@@ -100,21 +98,22 @@
     public:
         explicit ContextImpl(InputReader* reader);
 
-        virtual void updateGlobalMetaState() override;
-        virtual int32_t getGlobalMetaState() override;
-        virtual void disableVirtualKeysUntil(nsecs_t time) override;
-        virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override;
-        virtual void fadePointer() override;
-        virtual std::shared_ptr<PointerControllerInterface> getPointerController(
-                int32_t deviceId) override;
-        virtual void requestTimeoutAtTime(nsecs_t when) override;
-        virtual int32_t bumpGeneration() override;
-        virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override;
-        virtual void dispatchExternalStylusState(const StylusState& outState) override;
-        virtual InputReaderPolicyInterface* getPolicy() override;
-        virtual InputListenerInterface* getListener() override;
-        virtual EventHubInterface* getEventHub() override;
-        virtual int32_t getNextId() override;
+        void updateGlobalMetaState() override;
+        int32_t getGlobalMetaState() override;
+        void disableVirtualKeysUntil(nsecs_t time) override;
+        bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override;
+        void fadePointer() override;
+        std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) override;
+        void requestTimeoutAtTime(nsecs_t when) override;
+        int32_t bumpGeneration() override;
+        void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override;
+        void dispatchExternalStylusState(const StylusState& outState) override;
+        InputReaderPolicyInterface* getPolicy() override;
+        InputListenerInterface* getListener() override;
+        EventHubInterface* getEventHub() override;
+        int32_t getNextId() override;
+        void updateLedMetaState(int32_t metaState) override;
+        int32_t getLedMetaState() override;
     } mContext;
 
     friend class ContextImpl;
@@ -157,6 +156,10 @@
     void updateGlobalMetaStateLocked();
     int32_t getGlobalMetaStateLocked();
 
+    int32_t mLedMetaState;
+    void updateLedMetaStateLocked(int32_t metaState);
+    int32_t getLedMetaStateLocked();
+
     void notifyExternalStylusPresenceChanged();
     void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices);
     void dispatchExternalStylusState(const StylusState& state);
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index ffb8d8c..dc807f7 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -59,6 +59,9 @@
     virtual EventHubInterface* getEventHub() = 0;
 
     virtual int32_t getNextId() = 0;
+
+    virtual void updateLedMetaState(int32_t metaState) = 0;
+    virtual int32_t getLedMetaState() = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index d9fc5cc..56ab928 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -74,6 +74,7 @@
     virtual void updateExternalStylusState(const StylusState& state);
 
     virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
+    virtual void updateLedState(bool reset) {}
 
 protected:
     InputDeviceContext& mDeviceContext;
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index bd4232d..8b9f235 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -390,11 +390,14 @@
 bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
     int32_t oldMetaState = mMetaState;
     int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState);
-    bool metaStateChanged = oldMetaState != newMetaState;
+    int32_t metaStateChanged = oldMetaState ^ newMetaState;
     if (metaStateChanged) {
         mMetaState = newMetaState;
-        updateLedState(false);
-
+        constexpr int32_t allLedMetaState =
+                AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON;
+        if ((metaStateChanged & allLedMetaState) != 0) {
+            getContext()->updateLedMetaState(newMetaState & allLedMetaState);
+        }
         getContext()->updateGlobalMetaState();
     }
 
@@ -415,6 +418,26 @@
 }
 
 void KeyboardInputMapper::updateLedState(bool reset) {
+    mMetaState |= getContext()->getLedMetaState();
+
+    constexpr int32_t META_NUM = 3;
+    const std::array<int32_t, META_NUM> keyCodes = {AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK,
+                                                    AKEYCODE_SCROLL_LOCK};
+    const std::array<int32_t, META_NUM> metaCodes = {AMETA_CAPS_LOCK_ON, AMETA_NUM_LOCK_ON,
+                                                     AMETA_SCROLL_LOCK_ON};
+    std::array<uint8_t, META_NUM> flags = {0, 0, 0};
+    bool hasKeyLayout =
+            getDeviceContext().markSupportedKeyCodes(META_NUM, keyCodes.data(), flags.data());
+    // If the device doesn't have the physical meta key it shouldn't generate the corresponding
+    // meta state.
+    if (hasKeyLayout) {
+        for (int i = 0; i < META_NUM; i++) {
+            if (!flags[i]) {
+                mMetaState &= ~metaCodes[i];
+            }
+        }
+    }
+
     updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset);
     updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset);
     updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 0bdeded..4c0b42a 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -42,6 +42,7 @@
     virtual int32_t getMetaState() override;
     virtual void updateMetaState(int32_t keyCode) override;
     virtual std::optional<int32_t> getAssociatedDisplayId() override;
+    virtual void updateLedState(bool reset);
 
 private:
     // The current viewport.
@@ -93,7 +94,6 @@
 
     void resetLedState();
     void initializeLedState(LedState& ledState, int32_t led);
-    void updateLedState(bool reset);
     void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
     std::optional<DisplayViewport> findViewport(nsecs_t when,
                                                 const InputReaderConfiguration* config);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 3872de5..f56735a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -102,29 +102,23 @@
         mMaxY = maxY;
     }
 
-    virtual void setPosition(float x, float y) {
+    void setPosition(float x, float y) override {
         mX = x;
         mY = y;
     }
 
-    virtual void setButtonState(int32_t buttonState) {
-        mButtonState = buttonState;
-    }
+    void setButtonState(int32_t buttonState) override { mButtonState = buttonState; }
 
-    virtual int32_t getButtonState() const {
-        return mButtonState;
-    }
+    int32_t getButtonState() const override { return mButtonState; }
 
-    virtual void getPosition(float* outX, float* outY) const {
+    void getPosition(float* outX, float* outY) const override {
         *outX = mX;
         *outY = mY;
     }
 
-    virtual int32_t getDisplayId() const {
-        return mDisplayId;
-    }
+    int32_t getDisplayId() const override { return mDisplayId; }
 
-    virtual void setDisplayViewport(const DisplayViewport& viewport) {
+    void setDisplayViewport(const DisplayViewport& viewport) override {
         mDisplayId = viewport.displayId;
     }
 
@@ -133,7 +127,7 @@
     }
 
 private:
-    virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const {
+    bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override {
         *outMinX = mMinX;
         *outMinY = mMinY;
         *outMaxX = mMaxX;
@@ -141,7 +135,7 @@
         return mHaveBounds;
     }
 
-    virtual void move(float deltaX, float deltaY) {
+    void move(float deltaX, float deltaY) override {
         mX += deltaX;
         if (mX < mMinX) mX = mMinX;
         if (mX > mMaxX) mX = mMaxX;
@@ -150,17 +144,14 @@
         if (mY > mMaxY) mY = mMaxY;
     }
 
-    virtual void fade(Transition) {
-    }
+    void fade(Transition) override {}
 
-    virtual void unfade(Transition) {
-    }
+    void unfade(Transition) override {}
 
-    virtual void setPresentation(Presentation) {
-    }
+    void setPresentation(Presentation) override {}
 
-    virtual void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
-            int32_t displayId) {
+    void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
+                  int32_t displayId) override {
         std::vector<int32_t> newSpots;
         // Add spots for fingers that are down.
         for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
@@ -171,8 +162,7 @@
         mSpotsByDisplay[displayId] = newSpots;
     }
 
-    virtual void clearSpots() {
-    }
+    void clearSpots() override {}
 
     std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
 };
@@ -192,7 +182,7 @@
     TouchAffineTransformation transform;
 
 protected:
-    virtual ~FakeInputReaderPolicy() { }
+    virtual ~FakeInputReaderPolicy() {}
 
 public:
     FakeInputReaderPolicy() {
@@ -325,28 +315,27 @@
         return v;
     }
 
-    virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) {
+    void getReaderConfiguration(InputReaderConfiguration* outConfig) override {
         *outConfig = mConfig;
     }
 
-    virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) {
+    std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override {
         return mPointerControllers[deviceId];
     }
 
-    virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
+    void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mInputDevices = inputDevices;
         mInputDevicesChanged = true;
         mDevicesChangedCondition.notify_all();
     }
 
-    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier&) {
+    std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
+            const InputDeviceIdentifier&) override {
         return nullptr;
     }
 
-    virtual std::string getDeviceAlias(const InputDeviceIdentifier&) {
-        return "";
-    }
+    std::string getDeviceAlias(const InputDeviceIdentifier&) override { return ""; }
 
     void waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
         std::unique_lock<std::mutex> lock(mLock);
@@ -591,29 +580,27 @@
         return index >= 0 ? mDevices.valueAt(index) : nullptr;
     }
 
-    virtual Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const {
+    Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override {
         Device* device = getDevice(deviceId);
         return device ? device->classes : Flags<InputDeviceClass>(0);
     }
 
-    virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const {
+    InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override {
         Device* device = getDevice(deviceId);
         return device ? device->identifier : InputDeviceIdentifier();
     }
 
-    virtual int32_t getDeviceControllerNumber(int32_t) const {
-        return 0;
-    }
+    int32_t getDeviceControllerNumber(int32_t) const override { return 0; }
 
-    virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const {
+    void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             *outConfiguration = device->configuration;
         }
     }
 
-    virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
-            RawAbsoluteAxisInfo* outAxisInfo) const {
+    status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
+                                 RawAbsoluteAxisInfo* outAxisInfo) const override {
         Device* device = getDevice(deviceId);
         if (device && device->enabled) {
             ssize_t index = device->absoluteAxes.indexOfKey(axis);
@@ -626,7 +613,7 @@
         return -1;
     }
 
-    virtual bool hasRelativeAxis(int32_t deviceId, int axis) const {
+    bool hasRelativeAxis(int32_t deviceId, int axis) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             return device->relativeAxes.indexOfKey(axis) >= 0;
@@ -634,13 +621,10 @@
         return false;
     }
 
-    virtual bool hasInputProperty(int32_t, int) const {
-        return false;
-    }
+    bool hasInputProperty(int32_t, int) const override { return false; }
 
-    virtual status_t mapKey(int32_t deviceId,
-            int32_t scanCode, int32_t usageCode, int32_t metaState,
-            int32_t* outKeycode, int32_t *outMetaState, uint32_t* outFlags) const {
+    status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
+                    int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             const KeyInfo* key = getKey(device, scanCode, usageCode);
@@ -676,15 +660,13 @@
         return nullptr;
     }
 
-    virtual status_t mapAxis(int32_t, int32_t, AxisInfo*) const {
-        return NAME_NOT_FOUND;
-    }
+    status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; }
 
-    virtual void setExcludedDevices(const std::vector<std::string>& devices) {
+    void setExcludedDevices(const std::vector<std::string>& devices) override {
         mExcludedDevices = devices;
     }
 
-    virtual size_t getEvents(int, RawEvent* buffer, size_t) {
+    size_t getEvents(int, RawEvent* buffer, size_t) override {
         std::scoped_lock<std::mutex> lock(mLock);
         if (mEvents.empty()) {
             return 0;
@@ -696,7 +678,7 @@
         return 1;
     }
 
-    virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) {
+    std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override {
         auto it = mVideoFrames.find(deviceId);
         if (it != mVideoFrames.end()) {
             std::vector<TouchVideoFrame> frames = std::move(it->second);
@@ -706,7 +688,7 @@
         return {};
     }
 
-    virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const {
+    int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             ssize_t index = device->scanCodeStates.indexOfKey(scanCode);
@@ -717,7 +699,7 @@
         return AKEY_STATE_UNKNOWN;
     }
 
-    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const {
+    int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             ssize_t index = device->keyCodeStates.indexOfKey(keyCode);
@@ -728,7 +710,7 @@
         return AKEY_STATE_UNKNOWN;
     }
 
-    virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const {
+    int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             ssize_t index = device->switchStates.indexOfKey(sw);
@@ -739,8 +721,8 @@
         return AKEY_STATE_UNKNOWN;
     }
 
-    virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
-            int32_t* outValue) const {
+    status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+                                  int32_t* outValue) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             ssize_t index = device->absoluteAxisValue.indexOfKey(axis);
@@ -753,22 +735,22 @@
         return -1;
     }
 
-    virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
-            uint8_t* outFlags) const {
+    // Return true if the device has non-empty key layout.
+    bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
+                               uint8_t* outFlags) const override {
         bool result = false;
         Device* device = getDevice(deviceId);
         if (device) {
+            result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0;
             for (size_t i = 0; i < numCodes; i++) {
                 for (size_t j = 0; j < device->keysByScanCode.size(); j++) {
                     if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) {
                         outFlags[i] = 1;
-                        result = true;
                     }
                 }
                 for (size_t j = 0; j < device->keysByUsageCode.size(); j++) {
                     if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) {
                         outFlags[i] = 1;
-                        result = true;
                     }
                 }
             }
@@ -776,7 +758,7 @@
         return result;
     }
 
-    virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const {
+    bool hasScanCode(int32_t deviceId, int32_t scanCode) const override {
         Device* device = getDevice(deviceId);
         if (device) {
             ssize_t index = device->keysByScanCode.indexOfKey(scanCode);
@@ -785,12 +767,12 @@
         return false;
     }
 
-    virtual bool hasLed(int32_t deviceId, int32_t led) const {
+    bool hasLed(int32_t deviceId, int32_t led) const override {
         Device* device = getDevice(deviceId);
         return device && device->leds.indexOfKey(led) >= 0;
     }
 
-    virtual void setLedState(int32_t deviceId, int32_t led, bool on) {
+    void setLedState(int32_t deviceId, int32_t led, bool on) override {
         Device* device = getDevice(deviceId);
         if (device) {
             ssize_t index = device->leds.indexOfKey(led);
@@ -804,8 +786,8 @@
         }
     }
 
-    virtual void getVirtualKeyDefinitions(int32_t deviceId,
-            std::vector<VirtualKeyDefinition>& outVirtualKeys) const {
+    void getVirtualKeyDefinitions(
+            int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override {
         outVirtualKeys.clear();
 
         Device* device = getDevice(deviceId);
@@ -814,145 +796,31 @@
         }
     }
 
-    virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t) const {
+    const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override {
         return nullptr;
     }
 
-    virtual bool setKeyboardLayoutOverlay(int32_t, const sp<KeyCharacterMap>&) {
+    bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override {
         return false;
     }
 
-    virtual void vibrate(int32_t, const VibrationElement&) {}
+    void vibrate(int32_t, const VibrationElement&) override {}
 
-    virtual void cancelVibrate(int32_t) {
-    }
+    void cancelVibrate(int32_t) override {}
 
     virtual bool isExternal(int32_t) const {
         return false;
     }
 
-    virtual void dump(std::string&) {
-    }
+    void dump(std::string&) override {}
 
-    virtual void monitor() {
-    }
+    void monitor() override {}
 
-    virtual void requestReopenDevices() {
-    }
+    void requestReopenDevices() override {}
 
-    virtual void wake() {
-    }
+    void wake() override {}
 };
 
-
-// --- FakeInputReaderContext ---
-
-class FakeInputReaderContext : public InputReaderContext {
-    std::shared_ptr<EventHubInterface> mEventHub;
-    sp<InputReaderPolicyInterface> mPolicy;
-    sp<InputListenerInterface> mListener;
-    int32_t mGlobalMetaState;
-    bool mUpdateGlobalMetaStateWasCalled;
-    int32_t mGeneration;
-    int32_t mNextId;
-    std::weak_ptr<PointerControllerInterface> mPointerController;
-
-public:
-    FakeInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
-                           const sp<InputReaderPolicyInterface>& policy,
-                           const sp<InputListenerInterface>& listener)
-          : mEventHub(eventHub),
-            mPolicy(policy),
-            mListener(listener),
-            mGlobalMetaState(0),
-            mNextId(1) {}
-
-    virtual ~FakeInputReaderContext() { }
-
-    void assertUpdateGlobalMetaStateWasCalled() {
-        ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
-                << "Expected updateGlobalMetaState() to have been called.";
-        mUpdateGlobalMetaStateWasCalled = false;
-    }
-
-    void setGlobalMetaState(int32_t state) {
-        mGlobalMetaState = state;
-    }
-
-    uint32_t getGeneration() {
-        return mGeneration;
-    }
-
-    void updatePointerDisplay() {
-        std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
-        if (controller != nullptr) {
-            InputReaderConfiguration config;
-            mPolicy->getReaderConfiguration(&config);
-            auto viewport = config.getDisplayViewportById(config.defaultPointerDisplayId);
-            if (viewport) {
-                controller->setDisplayViewport(*viewport);
-            }
-        }
-    }
-
-private:
-    virtual void updateGlobalMetaState() {
-        mUpdateGlobalMetaStateWasCalled = true;
-    }
-
-    virtual int32_t getGlobalMetaState() {
-        return mGlobalMetaState;
-    }
-
-    virtual EventHubInterface* getEventHub() {
-        return mEventHub.get();
-    }
-
-    virtual InputReaderPolicyInterface* getPolicy() {
-        return mPolicy.get();
-    }
-
-    virtual InputListenerInterface* getListener() {
-        return mListener.get();
-    }
-
-    virtual void disableVirtualKeysUntil(nsecs_t) {
-    }
-
-    virtual bool shouldDropVirtualKey(nsecs_t, int32_t, int32_t) { return false; }
-
-    virtual std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) {
-        std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
-        if (controller == nullptr) {
-            controller = mPolicy->obtainPointerController(deviceId);
-            mPointerController = controller;
-            updatePointerDisplay();
-        }
-        return controller;
-    }
-
-    virtual void fadePointer() {
-    }
-
-    virtual void requestTimeoutAtTime(nsecs_t) {
-    }
-
-    virtual int32_t bumpGeneration() {
-        return ++mGeneration;
-    }
-
-    virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) {
-
-    }
-
-    virtual void dispatchExternalStylusState(const StylusState&) {
-
-    }
-
-    virtual int32_t getNextId() { return mNextId++; }
-};
-
-
 // --- FakeInputMapper ---
 
 class FakeInputMapper : public InputMapper {
@@ -982,7 +850,7 @@
             mResetWasCalled(false),
             mProcessWasCalled(false) {}
 
-    virtual ~FakeInputMapper() { }
+    virtual ~FakeInputMapper() {}
 
     void setKeyboardType(int32_t keyboardType) {
         mKeyboardType = keyboardType;
@@ -1051,11 +919,9 @@
     }
 
 private:
-    virtual uint32_t getSources() {
-        return mSources;
-    }
+    uint32_t getSources() override { return mSources; }
 
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) {
+    void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {
         InputMapper::populateDeviceInfo(deviceInfo);
 
         if (mKeyboardType != AINPUT_KEYBOARD_TYPE_NONE) {
@@ -1063,7 +929,7 @@
         }
     }
 
-    virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) {
+    void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mConfigureWasCalled = true;
 
@@ -1076,45 +942,45 @@
         mStateChangedCondition.notify_all();
     }
 
-    virtual void reset(nsecs_t) {
+    void reset(nsecs_t) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mResetWasCalled = true;
         mStateChangedCondition.notify_all();
     }
 
-    virtual void process(const RawEvent* rawEvent) {
+    void process(const RawEvent* rawEvent) override {
         std::scoped_lock<std::mutex> lock(mLock);
         mLastEvent = *rawEvent;
         mProcessWasCalled = true;
         mStateChangedCondition.notify_all();
     }
 
-    virtual int32_t getKeyCodeState(uint32_t, int32_t keyCode) {
+    int32_t getKeyCodeState(uint32_t, int32_t keyCode) override {
         ssize_t index = mKeyCodeStates.indexOfKey(keyCode);
         return index >= 0 ? mKeyCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
     }
 
-    virtual int32_t getScanCodeState(uint32_t, int32_t scanCode) {
+    int32_t getScanCodeState(uint32_t, int32_t scanCode) override {
         ssize_t index = mScanCodeStates.indexOfKey(scanCode);
         return index >= 0 ? mScanCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN;
     }
 
-    virtual int32_t getSwitchState(uint32_t, int32_t switchCode) {
+    int32_t getSwitchState(uint32_t, int32_t switchCode) override {
         ssize_t index = mSwitchStates.indexOfKey(switchCode);
         return index >= 0 ? mSwitchStates.valueAt(index) : AKEY_STATE_UNKNOWN;
     }
 
-    virtual bool markSupportedKeyCodes(uint32_t, size_t numCodes,
-            const int32_t* keyCodes, uint8_t* outFlags) {
-        bool result = false;
+    // Return true if the device has non-empty key layout.
+    bool markSupportedKeyCodes(uint32_t, size_t numCodes, const int32_t* keyCodes,
+                               uint8_t* outFlags) override {
         for (size_t i = 0; i < numCodes; i++) {
             for (size_t j = 0; j < mSupportedKeyCodes.size(); j++) {
                 if (keyCodes[i] == mSupportedKeyCodes[j]) {
                     outFlags[i] = 1;
-                    result = true;
                 }
             }
         }
+        bool result = mSupportedKeyCodes.size() > 0;
         return result;
     }
 
@@ -1143,7 +1009,7 @@
     InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
                             const sp<InputReaderPolicyInterface>& policy,
                             const sp<InputListenerInterface>& listener)
-          : InputReader(eventHub, policy, listener) {}
+          : InputReader(eventHub, policy, listener), mFakeContext(this) {}
 
     virtual ~InstrumentedInputReader() {}
 
@@ -1155,7 +1021,7 @@
         identifier.name = name;
         identifier.location = location;
         int32_t generation = deviceId + 1;
-        return std::make_shared<InputDevice>(&mContext, deviceId, generation, identifier);
+        return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier);
     }
 
     // Make the protected loopOnce method accessible to tests.
@@ -1172,7 +1038,50 @@
         return InputReader::createDeviceLocked(eventHubId, identifier);
     }
 
+    // --- FakeInputReaderContext ---
+    class FakeInputReaderContext : public ContextImpl {
+        int32_t mGlobalMetaState;
+        bool mUpdateGlobalMetaStateWasCalled;
+        int32_t mGeneration;
+
+    public:
+        FakeInputReaderContext(InputReader* reader)
+              : ContextImpl(reader),
+                mGlobalMetaState(0),
+                mUpdateGlobalMetaStateWasCalled(false),
+                mGeneration(1) {}
+
+        virtual ~FakeInputReaderContext() {}
+
+        void assertUpdateGlobalMetaStateWasCalled() {
+            ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled)
+                    << "Expected updateGlobalMetaState() to have been called.";
+            mUpdateGlobalMetaStateWasCalled = false;
+        }
+
+        void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; }
+
+        uint32_t getGeneration() { return mGeneration; }
+
+        void updateGlobalMetaState() override {
+            mUpdateGlobalMetaStateWasCalled = true;
+            ContextImpl::updateGlobalMetaState();
+        }
+
+        int32_t getGlobalMetaState() override {
+            return mGlobalMetaState | ContextImpl::getGlobalMetaState();
+        }
+
+        int32_t bumpGeneration() override {
+            mGeneration = ContextImpl::bumpGeneration();
+            return mGeneration;
+        }
+    } mFakeContext;
+
     friend class InputReaderTest;
+
+public:
+    FakeInputReaderContext* getContext() { return &mFakeContext; }
 };
 
 // --- InputReaderPolicyTest ---
@@ -1180,8 +1089,8 @@
 protected:
     sp<FakeInputReaderPolicy> mFakePolicy;
 
-    virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
-    virtual void TearDown() override { mFakePolicy.clear(); }
+    void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
+    void TearDown() override { mFakePolicy.clear(); }
 };
 
 /**
@@ -1371,7 +1280,7 @@
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     std::unique_ptr<InstrumentedInputReader> mReader;
 
-    virtual void SetUp() override {
+    void SetUp() override {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
@@ -1380,7 +1289,7 @@
                                                             mFakeListener);
     }
 
-    virtual void TearDown() override {
+    void TearDown() override {
         mFakeListener.clear();
         mFakePolicy.clear();
     }
@@ -1827,7 +1736,7 @@
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<InputReaderInterface> mReader;
 
-    virtual void SetUp() override {
+    void SetUp() override {
         mFakePolicy = new FakeInputReaderPolicy();
         mTestListener = new TestInputListener(2000ms /*eventHappenedTimeout*/,
                                               30ms /*eventDidNotHappenTimeout*/);
@@ -1842,7 +1751,7 @@
         ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
     }
 
-    virtual void TearDown() override {
+    void TearDown() override {
         ASSERT_EQ(mReader->stop(), OK);
         mTestListener.clear();
         mFakePolicy.clear();
@@ -1956,7 +1865,7 @@
 protected:
     const std::string UNIQUE_ID = "local:0";
 
-    virtual void SetUp() override {
+    void SetUp() override {
         InputReaderIntegrationTest::SetUp();
         // At least add an internal display.
         setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
@@ -2097,27 +2006,26 @@
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<TestInputListener> mFakeListener;
-    FakeInputReaderContext* mFakeContext;
-
+    std::unique_ptr<InstrumentedInputReader> mReader;
     std::shared_ptr<InputDevice> mDevice;
 
-    virtual void SetUp() override {
+    void SetUp() override {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
-        mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
-
-        mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, Flags<InputDeviceClass>(0));
+        mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+                                                            mFakeListener);
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
         identifier.location = DEVICE_LOCATION;
-        mDevice = std::make_shared<InputDevice>(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
+        mDevice = std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, DEVICE_GENERATION,
                                                 identifier);
+        mReader->pushNextDevice(mDevice);
+        mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, Flags<InputDeviceClass>(0));
+        mReader->loopOnce();
     }
 
-    virtual void TearDown() override {
-        mDevice = nullptr;
-        delete mFakeContext;
+    void TearDown() override {
         mFakeListener.clear();
         mFakePolicy.clear();
     }
@@ -2336,27 +2244,21 @@
     std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<TestInputListener> mFakeListener;
-    FakeInputReaderContext* mFakeContext;
-    InputDevice* mDevice;
+    std::unique_ptr<InstrumentedInputReader> mReader;
+    std::shared_ptr<InputDevice> mDevice;
 
     virtual void SetUp(Flags<InputDeviceClass> classes) {
         mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
-        mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
-        InputDeviceIdentifier identifier;
-        identifier.name = DEVICE_NAME;
-        identifier.location = DEVICE_LOCATION;
-        mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, identifier);
-
-        mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, classes);
+        mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+                                                            mFakeListener);
+        mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes);
     }
 
-    virtual void SetUp() override { SetUp(DEVICE_CLASSES); }
+    void SetUp() override { SetUp(DEVICE_CLASSES); }
 
-    virtual void TearDown() override {
-        delete mDevice;
-        delete mFakeContext;
+    void TearDown() override {
         mFakeListener.clear();
         mFakePolicy.clear();
     }
@@ -2367,11 +2269,27 @@
 
     void configureDevice(uint32_t changes) {
         if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-            mFakeContext->updatePointerDisplay();
+            mReader->requestRefreshConfiguration(changes);
+            mReader->loopOnce();
         }
         mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
     }
 
+    std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name,
+                                           const std::string& location, int32_t eventHubId,
+                                           Flags<InputDeviceClass> classes) {
+        InputDeviceIdentifier identifier;
+        identifier.name = name;
+        identifier.location = location;
+        std::shared_ptr<InputDevice> device =
+                std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION,
+                                              identifier);
+        mReader->pushNextDevice(device);
+        mFakeEventHub->addDevice(eventHubId, name, classes);
+        mReader->loopOnce();
+        return device;
+    }
+
     template <class T, typename... Args>
     T& addMapperAndConfigure(Args... args) {
         T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...);
@@ -2393,8 +2311,7 @@
         mFakePolicy->clearViewports();
     }
 
-    static void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code,
-                        int32_t value) {
+    void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code, int32_t value) {
         RawEvent event;
         event.when = when;
         event.deviceId = mapper.getDeviceContext().getEventHubId();
@@ -2402,6 +2319,7 @@
         event.code = code;
         event.value = value;
         mapper.process(&event);
+        mReader->loopOnce();
     }
 
     static void assertMotionRange(const InputDeviceInfo& info,
@@ -2543,10 +2461,16 @@
     const int32_t USAGE_UNKNOWN = 0x07ffff;
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
     mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, POLICY_FLAG_WAKE);
 
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    // Initial metastate to AMETA_NONE.
+    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+    mapper.updateMetaState(AKEYCODE_NUM_LOCK);
 
     // Key down by scan code.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
@@ -2641,13 +2565,17 @@
 TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 0);
 
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    // Initial metastate.
-    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+    // Initial metastate to AMETA_NONE.
+    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+    mapper.updateMetaState(AKEYCODE_NUM_LOCK);
 
     // Metakey down.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
@@ -2655,7 +2583,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
-    ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
 
     // Key down.
     process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
@@ -2674,7 +2602,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_NONE, args.metaState);
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-    ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
 }
 
 TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) {
@@ -2879,6 +2807,9 @@
     KeyboardInputMapper& mapper =
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    // Initialize metastate to AMETA_NUM_LOCK_ON.
+    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+    mapper.updateMetaState(AKEYCODE_NUM_LOCK);
 
     // Initialization should have turned all of the lights off.
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
@@ -2934,6 +2865,43 @@
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 }
 
+TEST_F(KeyboardInputMapperTest, NoMetaStateWhenMetaKeysNotPresent) {
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_BUTTON_A, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_B, 0, AKEYCODE_BUTTON_B, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_X, 0, AKEYCODE_BUTTON_X, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, BTN_Y, 0, AKEYCODE_BUTTON_Y, 0);
+
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+
+    // Initial metastate should be AMETA_NONE as no meta keys added.
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+    // Meta state should be AMETA_NONE after reset
+    mapper.reset(ARBITRARY_TIME);
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+    // Meta state should be AMETA_NONE with update, as device doesn't have the keys.
+    mapper.updateMetaState(AKEYCODE_NUM_LOCK);
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+
+    NotifyKeyArgs args;
+    // Press button "A"
+    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_A, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(AMETA_NONE, args.metaState);
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+    ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
+    ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
+
+    // Button up.
+    process(mapper, ARBITRARY_TIME + 2, EV_KEY, BTN_A, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(AMETA_NONE, args.metaState);
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
+    ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
+    ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
+}
+
 TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
     // keyboard 1.
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
@@ -2943,16 +2911,13 @@
 
     // keyboard 2.
     const std::string USB2 = "USB2";
+    const std::string DEVICE_NAME2 = "KEYBOARD2";
     constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
     constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
-    InputDeviceIdentifier identifier;
-    identifier.name = "KEYBOARD2";
-    identifier.location = USB2;
-    std::unique_ptr<InputDevice> device2 =
-            std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
-                                          identifier);
-    mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME,
-                             Flags<InputDeviceClass>(0) /*classes*/);
+    std::shared_ptr<InputDevice> device2 =
+            newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+                      Flags<InputDeviceClass>(0));
+
     mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
     mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
     mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
@@ -3015,13 +2980,78 @@
                                                 AKEYCODE_DPAD_LEFT, newDisplayId));
 }
 
+TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) {
+    mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+    mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/);
+    mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    // Initial metastate to AMETA_NONE.
+    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
+    mapper.updateMetaState(AKEYCODE_NUM_LOCK);
+
+    // Initialization should have turned all of the lights off.
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+
+    // Toggle caps lock on.
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
+
+    // Toggle num lock on.
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
+
+    // Toggle scroll lock on.
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+    ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
+
+    mFakeEventHub->removeDevice(EVENTHUB_ID);
+    mReader->loopOnce();
+
+    // keyboard 2 should default toggle keys.
+    const std::string USB2 = "USB2";
+    const std::string DEVICE_NAME2 = "KEYBOARD2";
+    constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+    constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
+    std::shared_ptr<InputDevice> device2 =
+            newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+                      Flags<InputDeviceClass>(0));
+    mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/);
+    mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/);
+    mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
+    mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
+
+    device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD,
+                                            AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
+    device2->reset(ARBITRARY_TIME);
+
+    ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL));
+    ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML));
+    ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_SCROLLL));
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
+}
+
 // --- KeyboardInputMapperTest_ExternalDevice ---
 
 class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
 protected:
-    virtual void SetUp() override {
-        InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL);
-    }
+    void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); }
 };
 
 TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) {
@@ -3109,7 +3139,7 @@
 
     std::shared_ptr<FakePointerController> mFakePointerController;
 
-    virtual void SetUp() override {
+    void SetUp() override {
         InputMapperTest::SetUp();
 
         mFakePointerController = std::make_shared<FakePointerController>();
@@ -3211,7 +3241,7 @@
     addConfigurationProperty("cursor.mode", "navigation");
     CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs args;
 
@@ -3839,10 +3869,10 @@
 
     // Disable pointer capture and check that the device generation got bumped
     // and events are generated the usual way.
-    const uint32_t generation = mFakeContext->getGeneration();
+    const uint32_t generation = mReader->getContext()->getGeneration();
     mFakePolicy->setPointerCapture(false);
     configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
-    ASSERT_TRUE(mFakeContext->getGeneration() != generation);
+    ASSERT_TRUE(mReader->getContext()->getGeneration() != generation);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
@@ -4257,7 +4287,7 @@
     prepareVirtualKeys();
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyKeyArgs args;
 
@@ -4307,7 +4337,7 @@
     prepareVirtualKeys();
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyKeyArgs keyArgs;
 
@@ -4428,7 +4458,7 @@
     prepareVirtualKeys();
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs motionArgs;
 
@@ -4503,7 +4533,7 @@
     prepareVirtualKeys();
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs motionArgs;
 
@@ -4599,7 +4629,7 @@
     prepareVirtualKeys();
     SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs motionArgs;
 
@@ -5494,7 +5524,7 @@
     prepareVirtualKeys();
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs motionArgs;
 
@@ -5770,7 +5800,7 @@
     prepareVirtualKeys();
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs motionArgs;
 
@@ -5945,7 +5975,7 @@
     prepareVirtualKeys();
     MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
-    mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
+    mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
     NotifyMotionArgs motionArgs;
 
@@ -6925,16 +6955,13 @@
 
     // Create the second touch screen device, and enable multi fingers.
     const std::string USB2 = "USB2";
+    const std::string DEVICE_NAME2 = "TOUCHSCREEN2";
     constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
     constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
-    InputDeviceIdentifier identifier;
-    identifier.name = "TOUCHSCREEN2";
-    identifier.location = USB2;
-    std::unique_ptr<InputDevice> device2 =
-            std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
-                                          identifier);
-    mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME,
-                             Flags<InputDeviceClass>(0) /*classes*/);
+    std::shared_ptr<InputDevice> device2 =
+            newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
+                      Flags<InputDeviceClass>(0));
+
     mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX,
                                    0 /*flat*/, 0 /*fuzz*/);
     mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX,
@@ -7446,9 +7473,7 @@
 
 class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
 protected:
-    virtual void SetUp() override {
-        InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL);
-    }
+    void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); }
 };
 
 /**
diff --git a/services/powermanager/benchmarks/AndroidTest.xml b/services/powermanager/benchmarks/AndroidTest.xml
deleted file mode 100644
index 40f4872..0000000
--- a/services/powermanager/benchmarks/AndroidTest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<configuration description="Config for libpowermanager benchmarks">
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="true" />
-        <option name="push" value="libpowermanager_benchmarks->/data/benchmarktest/benchmark" />
-    </target_preparer>
-    <option name="test-suite-tag" value="apct" />
-    <option name="test-suite-tag" value="apct-metric-instrumentation" />
-    <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
-        <option name="native-benchmark-device-path" value="/data/benchmarktest" />
-        <option name="benchmark-module-name" value="benchmark" />
-    </test>
-</configuration>
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 8722952..94cbfa1 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -25,7 +25,7 @@
 
 #include "BufferLayerConsumer.h"
 #include "Layer.h"
-#include "Scheduler/DispSync.h"
+#include "Scheduler/VsyncController.h"
 
 #include <inttypes.h>
 
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index 5e3044f..a28902d 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -34,7 +34,6 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
-class DispSync;
 class Layer;
 class String8;
 
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 138d08c..b916e0d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1453,6 +1453,13 @@
 
 void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
     ATRACE_CALL();
+    if (mLayerDetached) {
+        // If the layer is detached, then we don't defer this transaction since we will not
+        // commit the pending state while the layer is detached. Adding sync points may cause
+        // the barrier layer to wait for the states to be committed before dequeuing a buffer.
+        return;
+    }
+
     mCurrentState.barrierLayer_legacy = barrierLayer;
     mCurrentState.frameNumber_legacy = frameNumber;
     // We don't set eTransactionNeeded, because just receiving a deferral
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index b24855d..a2fc692 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -38,7 +38,7 @@
 #include "DisplayRenderArea.h"
 #include "Layer.h"
 #include "Promise.h"
-#include "Scheduler/DispSync.h"
+#include "Scheduler/VsyncController.h"
 #include "SurfaceFlinger.h"
 
 namespace android {
@@ -249,7 +249,7 @@
         // until the next vsync deadline, defer this sampling work
         // to a later frame, when hopefully there will be more time.
         DisplayStatInfo stats;
-        mScheduler.getDisplayStatInfo(&stats);
+        mScheduler.getDisplayStatInfo(&stats, systemTime());
         if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) {
             ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame));
             mDiscardedFrames++;
@@ -446,9 +446,26 @@
                                    PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread");
     }
 
-    ScreenCaptureResults captureResults;
+    class SyncScreenCaptureListener : public BnScreenCaptureListener {
+    public:
+        status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override {
+            resultsPromise.set_value(captureResults);
+            return NO_ERROR;
+        }
+
+        ScreenCaptureResults waitForResults() {
+            std::future<ScreenCaptureResults> resultsFuture = resultsPromise.get_future();
+            return resultsFuture.get();
+        }
+
+    private:
+        std::promise<ScreenCaptureResults> resultsPromise;
+    };
+
+    const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
     mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
-                                 true /* regionSampling */, captureResults);
+                                 true /* regionSampling */, captureListener);
+    ScreenCaptureResults captureResults = captureListener->waitForResults();
 
     std::vector<Descriptor> activeDescriptors;
     for (const auto& descriptor : descriptors) {
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
deleted file mode 100644
index f34aabc..0000000
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2012 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 <stddef.h>
-
-#include <utils/Mutex.h>
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-
-#include <ui/FenceTime.h>
-
-#include <memory>
-
-namespace android {
-
-class FenceTime;
-
-class DispSync {
-public:
-    DispSync() = default;
-    virtual ~DispSync() = default;
-
-    virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
-    virtual void beginResync() = 0;
-    virtual bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
-                                 bool* periodFlushed) = 0;
-    virtual void setPeriod(nsecs_t period) = 0;
-    virtual nsecs_t getPeriod() = 0;
-    virtual nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const = 0;
-    virtual void setIgnorePresentFences(bool ignore) = 0;
-    virtual nsecs_t expectedPresentTime(nsecs_t now) = 0;
-
-    virtual void dump(std::string& result) const = 0;
-
-protected:
-    DispSync(DispSync const&) = delete;
-    DispSync& operator=(DispSync const&) = delete;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 75f072d..ce5c31a 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -22,16 +22,13 @@
 #include <utils/Trace.h>
 #include <mutex>
 
-#include "DispSync.h"
 #include "EventThread.h"
+#include "VsyncController.h"
 
 namespace android::scheduler {
 using base::StringAppendF;
 using namespace std::chrono_literals;
 
-// The DispSync interface has a 'repeat this callback at rate' semantic. This object adapts
-// VSyncDispatch's individually-scheduled callbacks so as to meet DispSync's existing semantic
-// for now.
 class CallbackRepeater {
 public:
     CallbackRepeater(VSyncDispatch& dispatch, VSyncDispatch::Callback cb, const char* name,
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 559626b..f513535 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -98,11 +98,13 @@
 }
 
 DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t timestamp,
-                                      uint32_t count, nsecs_t expectedVSyncTimestamp) {
+                                      uint32_t count, nsecs_t expectedVSyncTimestamp,
+                                      nsecs_t deadlineTimestamp) {
     DisplayEventReceiver::Event event;
     event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp};
     event.vsync.count = count;
     event.vsync.expectedVSyncTimestamp = expectedVSyncTimestamp;
+    event.vsync.deadlineTimestamp = deadlineTimestamp;
     return event;
 }
 
@@ -285,12 +287,12 @@
 }
 
 void EventThread::onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
-                               nsecs_t /*deadlineTimestamp*/) {
+                               nsecs_t deadlineTimestamp) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     LOG_FATAL_IF(!mVSyncState);
     mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
-                                       expectedVSyncTimestamp));
+                                       expectedVSyncTimestamp, deadlineTimestamp));
     mCondition.notify_all();
 }
 
@@ -412,9 +414,11 @@
 
                 LOG_FATAL_IF(!mVSyncState);
                 const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
-                const auto expectedVSyncTime = now + timeout.count();
+                const auto deadlineTimestamp = now + timeout.count();
+                const auto expectedVSyncTime = deadlineTimestamp + timeout.count();
                 mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,
-                                                   ++mVSyncState->count, expectedVSyncTime));
+                                                   ++mVSyncState->count, expectedVSyncTime,
+                                                   deadlineTimestamp));
             }
         }
     }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index b0b5e2e..5271ccc 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -39,7 +39,6 @@
 #include <numeric>
 
 #include "../Layer.h"
-#include "DispSync.h"
 #include "DispSyncSource.h"
 #include "EventThread.h"
 #include "InjectVSyncSource.h"
@@ -50,6 +49,7 @@
 #include "VSyncDispatchTimerQueue.h"
 #include "VSyncPredictor.h"
 #include "VSyncReactor.h"
+#include "VsyncController.h"
 
 #define RETURN_IF_INVALID_HANDLE(handle, ...)                        \
     do {                                                             \
@@ -124,7 +124,7 @@
 
 Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
                      Options options)
-      : Scheduler(createVsyncSchedule(options), configs, callback,
+      : Scheduler(createVsyncSchedule(options.supportKernelTimer), configs, callback,
                   createLayerHistory(configs, options.useContentDetectionV2), options) {
     using namespace sysprop;
 
@@ -180,17 +180,17 @@
     mIdleTimer.reset();
 }
 
-Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(Options options) {
+Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) {
     auto clock = std::make_unique<scheduler::SystemClock>();
     auto tracker = createVSyncTracker();
     auto dispatch = createVSyncDispatch(*tracker);
 
     // TODO(b/144707443): Tune constants.
     constexpr size_t pendingFenceLimit = 20;
-    auto sync =
+    auto controller =
             std::make_unique<scheduler::VSyncReactor>(std::move(clock), *tracker, pendingFenceLimit,
-                                                      options.supportKernelTimer);
-    return {std::move(sync), std::move(tracker), std::move(dispatch)};
+                                                      supportKernelTimer);
+    return {std::move(controller), std::move(tracker), std::move(dispatch)};
 }
 
 std::unique_ptr<LayerHistory> Scheduler::createLayerHistory(
@@ -204,10 +204,6 @@
     return std::make_unique<scheduler::impl::LayerHistory>();
 }
 
-DispSync& Scheduler::getPrimaryDispSync() {
-    return *mVsyncSchedule.sync;
-}
-
 std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
         const char* name, std::chrono::nanoseconds workDuration,
         std::chrono::nanoseconds readyDuration, bool traceVsync) {
@@ -318,9 +314,9 @@
     mConnections[handle].thread->setDuration(workDuration, readyDuration);
 }
 
-void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) {
-    stats->vsyncTime = getPrimaryDispSync().computeNextRefresh(0, systemTime());
-    stats->vsyncPeriod = getPrimaryDispSync().getPeriod();
+void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats, nsecs_t now) {
+    stats->vsyncTime = mVsyncSchedule.tracker->nextAnticipatedVSyncTimeFrom(now);
+    stats->vsyncPeriod = mVsyncSchedule.tracker->currentPeriod();
 }
 
 Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
@@ -357,7 +353,7 @@
 void Scheduler::enableHardwareVsync() {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
     if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
-        getPrimaryDispSync().beginResync();
+        mVsyncSchedule.tracker->resetModel();
         mSchedulerCallback.setVsyncEnabled(true);
         mPrimaryHWVsyncEnabled = true;
     }
@@ -406,10 +402,10 @@
 
 void Scheduler::setVsyncPeriod(nsecs_t period) {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
-    getPrimaryDispSync().setPeriod(period);
+    mVsyncSchedule.controller->startPeriodTransition(period);
 
     if (!mPrimaryHWVsyncEnabled) {
-        getPrimaryDispSync().beginResync();
+        mVsyncSchedule.tracker->resetModel();
         mSchedulerCallback.setVsyncEnabled(true);
         mPrimaryHWVsyncEnabled = true;
     }
@@ -422,8 +418,8 @@
     { // Scope for the lock
         std::lock_guard<std::mutex> lock(mHWVsyncLock);
         if (mPrimaryHWVsyncEnabled) {
-            needsHwVsync =
-                    getPrimaryDispSync().addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
+            needsHwVsync = mVsyncSchedule.controller->addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
+                                                                          periodFlushed);
         }
     }
 
@@ -435,7 +431,7 @@
 }
 
 void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
-    if (getPrimaryDispSync().addPresentFence(fenceTime)) {
+    if (mVsyncSchedule.controller->addPresentFence(fenceTime)) {
         enableHardwareVsync();
     } else {
         disableHardwareVsync(false);
@@ -443,11 +439,7 @@
 }
 
 void Scheduler::setIgnorePresentFences(bool ignore) {
-    getPrimaryDispSync().setIgnorePresentFences(ignore);
-}
-
-nsecs_t Scheduler::getDispSyncExpectedPresentTime(nsecs_t now) {
-    return getPrimaryDispSync().expectedPresentTime(now);
+    mVsyncSchedule.controller->setIgnorePresentFences(ignore);
 }
 
 void Scheduler::registerLayer(Layer* layer) {
@@ -581,7 +573,7 @@
                refreshRate.getFps() <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
         // Disable HW VSYNC if the timer expired, as we don't need it enabled if
         // we're not pushing frames, and if we're in PERFORMANCE mode then we'll
-        // need to update the DispSync model anyway.
+        // need to update the VsyncController model anyway.
         disableHardwareVsync(false /* makeUnavailable */);
     }
 
@@ -624,11 +616,11 @@
                   mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
 }
 
-void Scheduler::dumpVSync(std::string& s) const {
+void Scheduler::dumpVsync(std::string& s) const {
     using base::StringAppendF;
 
     StringAppendF(&s, "VSyncReactor:\n");
-    mVsyncSchedule.sync->dump(s);
+    mVsyncSchedule.controller->dump(s);
     StringAppendF(&s, "VSyncDispatch:\n");
     mVsyncSchedule.dispatch->dump(s);
 }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 9a4ac36..0b5c9d2 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -40,12 +40,12 @@
 using namespace std::chrono_literals;
 using scheduler::LayerHistory;
 
-class DispSync;
 class FenceTime;
 class InjectVSyncSource;
 class PredictedVsyncTracer;
 
 namespace scheduler {
+class VsyncController;
 class VSyncDispatch;
 class VSyncTracker;
 } // namespace scheduler
@@ -69,8 +69,6 @@
     Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&);
     ~Scheduler();
 
-    DispSync& getPrimaryDispSync();
-
     using ConnectionHandle = scheduler::ConnectionHandle;
     ConnectionHandle createConnection(const char* connectionName,
                                       std::chrono::nanoseconds workDuration,
@@ -95,7 +93,7 @@
     void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration,
                      std::chrono::nanoseconds readyDuration);
 
-    void getDisplayStatInfo(DisplayStatInfo* stats);
+    void getDisplayStatInfo(DisplayStatInfo* stats, nsecs_t now);
 
     // Returns injector handle if injection has toggled, or an invalid handle otherwise.
     ConnectionHandle enableVSyncInjection(bool enable);
@@ -112,13 +110,12 @@
     void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
     void resync();
 
-    // Passes a vsync sample to DispSync. periodFlushed will be true if
-    // DispSync detected that the vsync period changed, and false otherwise.
+    // Passes a vsync sample to VsyncController. periodFlushed will be true if
+    // VsyncController detected that the vsync period changed, and false otherwise.
     void addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
                          bool* periodFlushed);
     void addPresentFence(const std::shared_ptr<FenceTime>&);
     void setIgnorePresentFences(bool ignore);
-    nsecs_t getDispSyncExpectedPresentTime(nsecs_t now);
 
     // Layers are registered on creation, and unregistered when the weak reference expires.
     void registerLayer(Layer*);
@@ -138,7 +135,7 @@
 
     void dump(std::string&) const;
     void dump(ConnectionHandle, std::string&) const;
-    void dumpVSync(std::string&) const;
+    void dumpVsync(std::string&) const;
 
     // Get the appropriate refresh for current conditions.
     std::optional<HwcConfigIndexType> getPreferredConfigId();
@@ -178,7 +175,7 @@
     };
 
     struct VsyncSchedule {
-        std::unique_ptr<DispSync> sync;
+        std::unique_ptr<scheduler::VsyncController> controller;
         std::unique_ptr<scheduler::VSyncTracker> tracker;
         std::unique_ptr<scheduler::VSyncDispatch> dispatch;
     };
@@ -190,7 +187,7 @@
     Scheduler(VsyncSchedule, const scheduler::RefreshRateConfigs&, ISchedulerCallback&,
               std::unique_ptr<LayerHistory>, Options);
 
-    static VsyncSchedule createVsyncSchedule(Options);
+    static VsyncSchedule createVsyncSchedule(bool supportKernelIdleTimer);
     static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&,
                                                             bool useContentDetectionV2);
 
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
index 59c336a..c9c2d84 100644
--- a/services/surfaceflinger/Scheduler/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -89,7 +89,7 @@
 }
 
 void Timer::alarmAt(std::function<void()> const& cb, nsecs_t time) {
-    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    std::lock_guard lock(mMutex);
     using namespace std::literals;
     static constexpr int ns_per_s =
             std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
@@ -109,7 +109,7 @@
 }
 
 void Timer::alarmCancel() {
-    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    std::lock_guard lock(mMutex);
 
     struct itimerspec old_timer;
     struct itimerspec new_timer {
@@ -192,7 +192,7 @@
                 setDebugState(DebugState::Running);
                 std::function<void()> cb;
                 {
-                    std::lock_guard<decltype(mMutex)> lk(mMutex);
+                    std::lock_guard lock(mMutex);
                     cb = mCallback;
                 }
                 if (cb) {
@@ -211,7 +211,7 @@
 }
 
 void Timer::setDebugState(DebugState state) {
-    std::lock_guard lk(mMutex);
+    std::lock_guard lock(mMutex);
     mDebugState = state;
 }
 
@@ -233,7 +233,7 @@
 }
 
 void Timer::dump(std::string& result) const {
-    std::lock_guard lk(mMutex);
+    std::lock_guard lock(mMutex);
     StringAppendF(&result, "\t\tDebugState: %s\n", strDebugState(mDebugState));
 }
 
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 2154a40..ca6ea27 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -185,7 +185,7 @@
         mMinVsyncDistance(minVsyncDistance) {}
 
 VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
-    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    std::lock_guard lock(mMutex);
     cancelTimer();
 }
 
@@ -257,7 +257,7 @@
     };
     std::vector<Invocation> invocations;
     {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        std::lock_guard lock(mMutex);
         auto const now = mTimeKeeper->now();
         mLastTimerCallback = now;
         for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
@@ -289,7 +289,7 @@
 
 VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
         Callback const& callbackFn, std::string callbackName) {
-    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    std::lock_guard lock(mMutex);
     return CallbackToken{
             mCallbacks
                     .emplace(++mCallbackToken,
@@ -302,7 +302,7 @@
 void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
     std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
     {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        std::lock_guard lock(mMutex);
         auto it = mCallbacks.find(token);
         if (it != mCallbacks.end()) {
             entry = it->second;
@@ -319,7 +319,7 @@
                                                  ScheduleTiming scheduleTiming) {
     auto result = ScheduleResult::Error;
     {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        std::lock_guard lock(mMutex);
 
         auto it = mCallbacks.find(token);
         if (it == mCallbacks.end()) {
@@ -350,7 +350,7 @@
 }
 
 CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
-    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    std::lock_guard lock(mMutex);
 
     auto it = mCallbacks.find(token);
     if (it == mCallbacks.end()) {
@@ -372,7 +372,7 @@
 }
 
 void VSyncDispatchTimerQueue::dump(std::string& result) const {
-    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    std::lock_guard lock(mMutex);
     StringAppendF(&result, "\tTimer:\n");
     mTimeKeeper->dump(result);
     StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 3c5b3f1..7b5d462 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -30,6 +30,8 @@
 namespace android::scheduler {
 using base::StringAppendF;
 
+VsyncController::~VsyncController() = default;
+
 Clock::~Clock() = default;
 nsecs_t SystemClock::now() const {
     return systemTime(SYSTEM_TIME_MONOTONIC);
@@ -44,7 +46,7 @@
 
 VSyncReactor::~VSyncReactor() = default;
 
-bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
+bool VSyncReactor::addPresentFence(const std::shared_ptr<android::FenceTime>& fence) {
     if (!fence) {
         return false;
     }
@@ -91,14 +93,14 @@
     return mMoreSamplesNeeded;
 }
 
-void VSyncReactor::setIgnorePresentFences(bool ignoration) {
+void VSyncReactor::setIgnorePresentFences(bool ignore) {
     std::lock_guard lock(mMutex);
-    mExternalIgnoreFences = ignoration;
+    mExternalIgnoreFences = ignore;
     updateIgnorePresentFencesInternal();
 }
 
-void VSyncReactor::setIgnorePresentFencesInternal(bool ignoration) {
-    mInternalIgnoreFences = ignoration;
+void VSyncReactor::setIgnorePresentFencesInternal(bool ignore) {
+    mInternalIgnoreFences = ignore;
     updateIgnorePresentFencesInternal();
 }
 
@@ -108,16 +110,7 @@
     }
 }
 
-nsecs_t VSyncReactor::computeNextRefresh(int periodOffset, nsecs_t now) const {
-    auto const currentPeriod = periodOffset ? mTracker.currentPeriod() : 0;
-    return mTracker.nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod);
-}
-
-nsecs_t VSyncReactor::expectedPresentTime(nsecs_t now) {
-    return mTracker.nextAnticipatedVSyncTimeFrom(now);
-}
-
-void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) {
+void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) {
     ATRACE_CALL();
     mPeriodConfirmationInProgress = true;
     mPeriodTransitioningTo = newPeriod;
@@ -132,28 +125,20 @@
     mLastHwVsync.reset();
 }
 
-void VSyncReactor::setPeriod(nsecs_t period) {
+void VSyncReactor::startPeriodTransition(nsecs_t period) {
     ATRACE_INT64("VSR-setPeriod", period);
-    std::lock_guard lk(mMutex);
+    std::lock_guard lock(mMutex);
     mLastHwVsync.reset();
 
-    if (!mSupportKernelIdleTimer && period == getPeriod()) {
+    if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod()) {
         endPeriodTransition();
         setIgnorePresentFencesInternal(false);
         mMoreSamplesNeeded = false;
     } else {
-        startPeriodTransition(period);
+        startPeriodTransitionInternal(period);
     }
 }
 
-nsecs_t VSyncReactor::getPeriod() {
-    return mTracker.currentPeriod();
-}
-
-void VSyncReactor::beginResync() {
-    mTracker.resetModel();
-}
-
 bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> HwcVsyncPeriod) {
     if (!mPeriodConfirmationInProgress) {
         return false;
@@ -164,13 +149,13 @@
     }
 
     const bool periodIsChanging =
-            mPeriodTransitioningTo && (*mPeriodTransitioningTo != getPeriod());
+            mPeriodTransitioningTo && (*mPeriodTransitioningTo != mTracker.currentPeriod());
     if (mSupportKernelIdleTimer && !periodIsChanging) {
         // Clear out the Composer-provided period and use the allowance logic below
         HwcVsyncPeriod = {};
     }
 
-    auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
+    auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : mTracker.currentPeriod();
     static constexpr int allowancePercent = 10;
     static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
     auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
@@ -182,8 +167,8 @@
     return std::abs(distance - period) < allowance;
 }
 
-bool VSyncReactor::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
-                                   bool* periodFlushed) {
+bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                                       bool* periodFlushed) {
     assert(periodFlushed);
 
     std::lock_guard lock(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 80b5232..449d4c3 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -22,8 +22,8 @@
 #include <mutex>
 #include <unordered_map>
 #include <vector>
-#include "DispSync.h"
 #include "TimeKeeper.h"
+#include "VsyncController.h"
 namespace android::scheduler {
 
 class Clock;
@@ -31,32 +31,26 @@
 class VSyncTracker;
 
 // TODO (b/145217110): consider renaming.
-class VSyncReactor : public android::DispSync {
+class VSyncReactor : public VsyncController {
 public:
     VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker, size_t pendingFenceLimit,
                  bool supportKernelIdleTimer);
     ~VSyncReactor();
 
-    bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final;
-    void setIgnorePresentFences(bool ignoration) final;
+    bool addPresentFence(const std::shared_ptr<android::FenceTime>& fence) final;
+    void setIgnorePresentFences(bool ignore) final;
 
-    nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const final;
-    nsecs_t expectedPresentTime(nsecs_t now) final;
+    void startPeriodTransition(nsecs_t period) final;
 
-    void setPeriod(nsecs_t period) final;
-    nsecs_t getPeriod() final;
-
-    // TODO: (b/145626181) remove begin,endResync functions from DispSync i/f when possible.
-    void beginResync() final;
-    bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
-                         bool* periodFlushed) final;
+    bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                             bool* periodFlushed) final;
 
     void dump(std::string& result) const final;
 
 private:
-    void setIgnorePresentFencesInternal(bool ignoration) REQUIRES(mMutex);
+    void setIgnorePresentFencesInternal(bool ignore) REQUIRES(mMutex);
     void updateIgnorePresentFencesInternal() REQUIRES(mMutex);
-    void startPeriodTransition(nsecs_t newPeriod) REQUIRES(mMutex);
+    void startPeriodTransitionInternal(nsecs_t newPeriod) REQUIRES(mMutex);
     void endPeriodTransition() REQUIRES(mMutex);
     bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod)
             REQUIRES(mMutex);
@@ -68,7 +62,7 @@
     mutable std::mutex mMutex;
     bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
     bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
-    std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
+    std::vector<std::shared_ptr<android::FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
 
     bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
     bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false;
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
new file mode 100644
index 0000000..0f0df22
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <ui/FenceTime.h>
+
+#include <memory>
+
+namespace android::scheduler {
+
+class FenceTime;
+
+class VsyncController {
+public:
+    virtual ~VsyncController();
+
+    /*
+     * Adds a present fence to the model. The controller will use the fence time as
+     * a vsync signal.
+     *
+     * \param [in] fence    The present fence given from the display
+     * \return              True if the model needs more vsync signals to make
+     *                      an accurate prediction,
+     *                      False otherwise
+     */
+    virtual bool addPresentFence(const std::shared_ptr<android::FenceTime>&) = 0;
+
+    /*
+     * Adds a hw sync timestamp to the model. The controller will use the timestamp
+     * time as a vsync signal.
+     *
+     * \param [in] timestamp       The HW Vsync timestamp
+     * \param [in] hwcVsyncPeriod  The Vsync period reported by composer, if available
+     * \param [out] periodFlushed  True if the vsync period changed is completed
+     * \return                     True if the model needs more vsync signals to make
+     *                             an accurate prediction,
+     *                             False otherwise
+     */
+    virtual bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                                     bool* periodFlushed) = 0;
+
+    /*
+     * Inform the controller that the period is changing and the controller needs to recalibrate
+     * itself. The controller will end the period transition internally.
+     *
+     * \param [in] period   The period that the system is changing into.
+     */
+    virtual void startPeriodTransition(nsecs_t period) = 0;
+
+    /*
+     * Tells the tracker to stop using present fences to get a vsync signal.
+     *
+     * \param [in] ignore  Whether to ignore the present fences or not
+     */
+    virtual void setIgnorePresentFences(bool ignore) = 0;
+
+    virtual void dump(std::string& result) const = 0;
+
+protected:
+    VsyncController() = default;
+    VsyncController(VsyncController const&) = delete;
+    VsyncController& operator=(VsyncController const&) = delete;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7572be3..9e5b8d8 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -117,13 +117,13 @@
 #include "Promise.h"
 #include "RefreshRateOverlay.h"
 #include "RegionSamplingThread.h"
-#include "Scheduler/DispSync.h"
 #include "Scheduler/DispSyncSource.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/LayerHistory.h"
 #include "Scheduler/MessageQueue.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncConfiguration.h"
+#include "Scheduler/VsyncController.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlingerProperties.h"
 #include "SurfaceInterceptor.h"
@@ -961,7 +961,7 @@
         //
         // Normally it's one full refresh period (to give SF a chance to
         // latch the buffer), but this can be reduced by configuring a
-        // DispSync offset.  Any additional delays introduced by the hardware
+        // VsyncController offset.  Any additional delays introduced by the hardware
         // composer or panel must be accounted for here.
         //
         // We add an additional 1ms to allow for processing time and
@@ -979,7 +979,7 @@
         return BAD_VALUE;
     }
 
-    mScheduler->getDisplayStatInfo(stats);
+    mScheduler->getDisplayStatInfo(stats, systemTime());
     return NO_ERROR;
 }
 
@@ -1037,7 +1037,7 @@
         // switch.
         mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());
         // As we called to set period, we will call to onRefreshRateChangeCompleted once
-        // DispSync model is locked.
+        // VsyncController model is locked.
         modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);
 
         updatePhaseConfiguration(refreshRate);
@@ -1824,11 +1824,10 @@
 
 nsecs_t SurfaceFlinger::calculateExpectedPresentTime(nsecs_t now) const {
     DisplayStatInfo stats;
-    mScheduler->getDisplayStatInfo(&stats);
-    const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime(now);
+    mScheduler->getDisplayStatInfo(&stats, now);
     // Inflate the expected present time if we're targetting the next vsync.
-    return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? presentTime
-                                                          : presentTime + stats.vsyncPeriod;
+    return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? stats.vsyncTime
+                                                          : stats.vsyncTime + stats.vsyncPeriod;
 }
 
 void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) {
@@ -1874,7 +1873,7 @@
     // smaller than a typical frame duration, but should not be so small
     // that it reports reasonable drift as a missed frame.
     DisplayStatInfo stats;
-    mScheduler->getDisplayStatInfo(&stats);
+    mScheduler->getDisplayStatInfo(&stats, systemTime());
     const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
     const nsecs_t previousPresentTime = previousFramePresentTime();
     const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed",
@@ -2247,7 +2246,7 @@
     getBE().mDisplayTimeline.push(presentFenceTime);
 
     DisplayStatInfo stats;
-    mScheduler->getDisplayStatInfo(&stats);
+    mScheduler->getDisplayStatInfo(&stats, systemTime());
 
     // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might
     // be sampled a little later than when we started doing work for this frame,
@@ -4352,7 +4351,7 @@
     } else {
         static const std::unordered_map<std::string, Dumper> dumpers = {
                 {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)},
-                {"--dispsync"s, dumper([this](std::string& s) { mScheduler->dumpVSync(s); })},
+                {"--dispsync"s, dumper([this](std::string& s) { mScheduler->dumpVsync(s); })},
                 {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
                 {"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)},
                 {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
@@ -4499,7 +4498,7 @@
     }
 
     mScheduler->dump(mAppConnectionHandle, result);
-    mScheduler->getPrimaryDispSync().dump(result);
+    mScheduler->dumpVsync(result);
 }
 
 void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const {
@@ -5531,12 +5530,9 @@
     auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) {
         traverseLayersInLayerStack(layerStack, args.uid, visitor);
     };
-    ScreenCaptureResults captureResults;
-    status_t status = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
-                                          args.pixelFormat, captureResults);
-    captureResults.result = status;
-    captureListener->onScreenCaptureComplete(captureResults);
-    return status;
+
+    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+                               args.pixelFormat, captureListener);
 }
 
 status_t SurfaceFlinger::setSchedFifo(bool enabled) {
@@ -5609,12 +5605,8 @@
         traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor);
     };
 
-    ScreenCaptureResults captureResults;
-    status_t status = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
-                                          ui::PixelFormat::RGBA_8888, captureResults);
-    captureResults.result = status;
-    captureListener->onScreenCaptureComplete(captureResults);
-    return status;
+    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size,
+                               ui::PixelFormat::RGBA_8888, captureListener);
 }
 
 status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
@@ -5705,7 +5697,7 @@
                                                  captureSecureLayers);
     });
 
-    auto traverseLayers = [parent, args, &excludeLayers](const LayerVector::Visitor& visitor) {
+    auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) {
         parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
             if (!layer->isVisible()) {
                 return;
@@ -5727,18 +5719,14 @@
         });
     };
 
-    ScreenCaptureResults captureResults;
-    status_t status = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
-                                          args.pixelFormat, captureResults);
-    captureResults.result = status;
-    captureListener->onScreenCaptureComplete(captureResults);
-    return status;
+    return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
+                               args.pixelFormat, captureListener);
 }
 
 status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
                                              TraverseLayersFunction traverseLayers,
                                              ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
-                                             ScreenCaptureResults& captureResults) {
+                                             const sp<IScreenCaptureListener>& captureListener) {
     ATRACE_CALL();
 
     // TODO(b/116112787) Make buffer usage a parameter.
@@ -5748,54 +5736,56 @@
             getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
                                              static_cast<android_pixel_format>(reqPixelFormat),
                                              1 /* layerCount */, usage, "screenshot");
-
     return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer,
-                               false /* regionSampling */, captureResults);
+                               false /* regionSampling */, captureListener);
 }
 
 status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
                                              TraverseLayersFunction traverseLayers,
-                                             const sp<GraphicBuffer>& buffer, bool regionSampling,
-                                             ScreenCaptureResults& captureResults) {
+                                             sp<GraphicBuffer>& buffer, bool regionSampling,
+                                             const sp<IScreenCaptureListener>& captureListener) {
+    ATRACE_CALL();
+
+    if (captureListener == nullptr) {
+        ALOGE("capture screen must provide a capture listener callback");
+        return BAD_VALUE;
+    }
+
     const int uid = IPCThreadState::self()->getCallingUid();
     const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM;
 
-    status_t result;
-    int syncFd;
+    static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable {
+        if (mRefreshPending) {
+            ALOGW("Skipping screenshot for now");
+            captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, regionSampling,
+                                captureListener);
+            return;
+        }
+        ScreenCaptureResults captureResults;
+        std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
+        if (!renderArea) {
+            ALOGW("Skipping screen capture because of invalid render area.");
+            captureResults.result = NO_MEMORY;
+            captureListener->onScreenCaptureComplete(captureResults);
+            return;
+        }
 
-    do {
-        std::tie(result, syncFd) =
-                schedule([&]() -> std::pair<status_t, int> {
-                    if (mRefreshPending) {
-                        ALOGW("Skipping screenshot for now");
-                        return {EAGAIN, -1};
-                    }
-                    std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
-                    if (!renderArea) {
-                        ALOGW("Skipping screen capture because of invalid render area.");
-                        return {NO_MEMORY, -1};
-                    }
+        status_t result = NO_ERROR;
+        int syncFd = -1;
+        renderArea->render([&] {
+            result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, forSystem, &syncFd,
+                                            regionSampling, captureResults);
+        });
 
-                    status_t result = NO_ERROR;
-                    int fd = -1;
+        if (result == NO_ERROR) {
+            sync_wait(syncFd, -1);
+            close(syncFd);
+        }
+        captureResults.result = result;
+        captureListener->onScreenCaptureComplete(captureResults);
+    }));
 
-                    Mutex::Autolock lock(mStateLock);
-                    renderArea->render([&] {
-                        result = renderScreenImplLocked(*renderArea, traverseLayers, buffer.get(),
-                                                        forSystem, &fd, regionSampling,
-                                                        captureResults);
-                    });
-
-                    return {result, fd};
-                }).get();
-    } while (result == EAGAIN);
-
-    if (result == NO_ERROR) {
-        sync_wait(syncFd, -1);
-        close(syncFd);
-    }
-
-    return result;
+    return NO_ERROR;
 }
 
 status_t SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea,
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index fb79989..e0a6073 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -219,7 +219,7 @@
     // If fences from sync Framework are supported.
     static bool hasSyncFramework;
 
-    // The offset in nanoseconds to use when DispSync timestamps present fence
+    // The offset in nanoseconds to use when VsyncController timestamps present fence
     // signaling time.
     static int64_t dispSyncPresentTimeOffset;
 
@@ -781,14 +781,14 @@
     // Boot animation, on/off animations and screen capture
     void startBootAnim();
 
-    status_t renderScreenImplLocked(const RenderArea& renderArea,
-                                    TraverseLayersFunction traverseLayers,
-                                    const sp<GraphicBuffer>& buffer, bool forSystem, int* outSyncFd,
-                                    bool regionSampling, ScreenCaptureResults& captureResults);
+    status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction,
+                                    const sp<GraphicBuffer>&, bool forSystem, int* outSyncFd,
+                                    bool regionSampling, ScreenCaptureResults&);
     status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize,
-                                 ui::PixelFormat, ScreenCaptureResults& captureResults);
-    status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, const sp<GraphicBuffer>&,
-                                 bool regionSampling, ScreenCaptureResults& captureResults);
+                                 ui::PixelFormat, const sp<IScreenCaptureListener>&);
+    status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, sp<GraphicBuffer>&,
+                                 bool regionSampling, const sp<IScreenCaptureListener>&);
+
     sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock);
     sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock);
 
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 2e52155..fb08e69 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -37,10 +37,10 @@
 #include "SurfaceInterceptor.h"
 
 #include "DisplayHardware/ComposerHal.h"
-#include "Scheduler/DispSync.h"
 #include "Scheduler/MessageQueue.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VsyncConfiguration.h"
+#include "Scheduler/VsyncController.h"
 
 namespace android::surfaceflinger {
 
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index a0dd999..753476e 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -34,7 +34,6 @@
 class EffectLayer;
 class ContainerLayer;
 class DisplayDevice;
-class DispSync;
 class GraphicBuffer;
 class HWComposer;
 class IGraphicBufferConsumer;
@@ -56,6 +55,7 @@
 
 namespace scheduler {
 class VsyncConfiguration;
+class VsyncController;
 class RefreshRateConfigs;
 } // namespace scheduler
 
diff --git a/services/surfaceflinger/tests/DetachChildren_test.cpp b/services/surfaceflinger/tests/DetachChildren_test.cpp
index b6c2fe2..3261308 100644
--- a/services/surfaceflinger/tests/DetachChildren_test.cpp
+++ b/services/surfaceflinger/tests/DetachChildren_test.cpp
@@ -256,6 +256,58 @@
     }
 }
 
+/**
+ * Tests that a deferring transaction on an already detached layer will be dropped gracefully and
+ * allow the barrier layer to dequeue buffers.
+ *
+ * Fixes b/150924737 - buffer cannot be latched because it waits for a detached layer
+ * to commit its pending states.
+ */
+TEST_F(DetachChildren, DeferredTransactionOnDetachedChildren) {
+    Color childColor = {200, 200, 200, 255};
+    Rect childBounds = Rect(74, 74, 84, 84);
+
+    sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+    sp<SurfaceControl> childNewClient =
+            createSurface(newComposerClient, "New Child Test Surface", childBounds.width(),
+                          childBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get());
+    ASSERT_TRUE(childNewClient->isValid());
+
+    TransactionUtils::fillSurfaceRGBA8(childNewClient, childColor);
+
+    Transaction()
+            .show(childNewClient)
+            .setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left,
+                         childBounds.top - mMainSurfaceBounds.top)
+            .apply();
+
+    {
+        mCapture = screenshot();
+        mCapture->expectBorder(childBounds, mMainSurfaceColor);
+        mCapture->expectColor(childBounds, childColor);
+    }
+
+    Transaction().detachChildren(mMainSurface).apply();
+    Transaction()
+            .setCrop_legacy(childNewClient, {0, 0, childBounds.width(), childBounds.height()})
+            .deferTransactionUntil_legacy(childNewClient, mMainSurface->getHandle(),
+                                          mMainSurface->getSurface()->getNextFrameNumber())
+            .apply();
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mMainSurface, Color::RED,
+                                                      mMainSurfaceBounds.width(),
+                                                      mMainSurfaceBounds.height()));
+
+    // BufferLayer can still dequeue buffers even though there's a detached layer with a
+    // deferred transaction.
+    {
+        SCOPED_TRACE("new buffer");
+        mCapture = screenshot();
+        mCapture->expectBorder(childBounds, Color::RED);
+        mCapture->expectColor(childBounds, childColor);
+    }
+}
+
 TEST_F(DetachChildren, ReparentParentLayerOfDetachedChildren) {
     Color childColor = {200, 200, 200, 255};
     Rect childBounds = Rect(74, 74, 94, 94);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 8d4aa0c..a90f672 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -68,13 +68,14 @@
         "mock/DisplayHardware/MockComposer.cpp",
         "mock/DisplayHardware/MockDisplay.cpp",
         "mock/DisplayHardware/MockPowerAdvisor.cpp",
-        "mock/MockDispSync.cpp",
         "mock/MockEventThread.cpp",
+        "mock/MockFrameTracer.cpp",
         "mock/MockMessageQueue.cpp",
         "mock/MockNativeWindowSurface.cpp",
         "mock/MockSurfaceInterceptor.cpp",
         "mock/MockTimeStats.cpp",
-        "mock/MockFrameTracer.cpp",
+        "mock/MockVsyncController.cpp",
+        "mock/MockVSyncTracker.cpp",
         "mock/system/window/MockNativeWindow.cpp",
     ],
     static_libs: [
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 4843f05..159a215 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -41,10 +41,10 @@
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockPowerAdvisor.h"
-#include "mock/MockDispSync.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockMessageQueue.h"
 #include "mock/MockTimeStats.h"
+#include "mock/MockVsyncController.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
@@ -142,17 +142,19 @@
                         new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
                                                   ISurfaceComposer::eConfigChangedSuppress)));
 
-        auto primaryDispSync = std::make_unique<mock::DispSync>();
+        auto vsyncController = std::make_unique<mock::VsyncController>();
+        auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
 
-        EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
-        EXPECT_CALL(*primaryDispSync, getPeriod())
+        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*vsyncTracker, currentPeriod())
                 .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
-        EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
 
         constexpr ISchedulerCallback* kCallback = nullptr;
         constexpr bool kHasMultipleConfigs = true;
-        mFlinger.setupScheduler(std::move(primaryDispSync), std::move(eventThread),
-                                std::move(sfEventThread), kCallback, kHasMultipleConfigs);
+        mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                                std::move(eventThread), std::move(sfEventThread), kCallback,
+                                kHasMultipleConfigs);
 
         // Layer history should be created if there are multiple configs.
         ASSERT_TRUE(mFlinger.scheduler()->hasLayerHistory());
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index b9c9fe7..b939b9a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -45,12 +45,12 @@
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
 #include "mock/DisplayHardware/MockPowerAdvisor.h"
-#include "mock/MockDispSync.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockMessageQueue.h"
 #include "mock/MockNativeWindowSurface.h"
 #include "mock/MockSchedulerCallback.h"
 #include "mock/MockSurfaceInterceptor.h"
+#include "mock/MockVsyncController.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
@@ -155,7 +155,8 @@
     mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
     mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor();
 
-    mock::DispSync* mPrimaryDispSync = new mock::DispSync;
+    mock::VsyncController* mVsyncController = new mock::VsyncController;
+    mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
     mock::SchedulerCallback mSchedulerCallback;
     mock::EventThread* mEventThread = new mock::EventThread;
     mock::EventThread* mSFEventThread = new mock::EventThread;
@@ -213,7 +214,8 @@
             .WillOnce(Return(new EventThreadConnection(mSFEventThread, ResyncCallback(),
                                                        ISurfaceComposer::eConfigChangedSuppress)));
 
-    mFlinger.setupScheduler(std::unique_ptr<DispSync>(mPrimaryDispSync),
+    mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController),
+                            std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker),
                             std::unique_ptr<EventThread>(mEventThread),
                             std::unique_ptr<EventThread>(mSFEventThread), &mSchedulerCallback);
 }
@@ -3128,7 +3130,7 @@
     // processing.
     EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
 
-    EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
 
     // --------------------------------------------------------------------
     // Invocation
@@ -3259,15 +3261,14 @@
 };
 
 struct DispSyncIsSupportedVariant {
-    static void setupBeginResyncCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mPrimaryDispSync, setPeriod(DEFAULT_REFRESH_RATE)).Times(1);
-        EXPECT_CALL(*test->mPrimaryDispSync, beginResync()).Times(1);
+    static void setupResetModelCallExpectations(DisplayTransactionTest* test) {
+        EXPECT_CALL(*test->mVsyncController, startPeriodTransition(DEFAULT_REFRESH_RATE)).Times(1);
+        EXPECT_CALL(*test->mVSyncTracker, resetModel()).Times(1);
     }
 };
 
 struct DispSyncNotSupportedVariant {
-    static void setupBeginResyncCallExpectations(DisplayTransactionTest* /* test */) {}
-
+    static void setupResetModelCallExpectations(DisplayTransactionTest* /* test */) {}
 };
 
 // --------------------------------------------------------------------
@@ -3290,7 +3291,7 @@
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
         Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
-        Case::DispSync::setupBeginResyncCallExpectations(test);
+        Case::DispSync::setupResetModelCallExpectations(test);
         Case::setupRepaintEverythingCallExpectations(test);
     }
 
@@ -3353,7 +3354,7 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
-        Case::DispSync::setupBeginResyncCallExpectations(test);
+        Case::DispSync::setupResetModelCallExpectations(test);
         Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE);
     }
 };
@@ -3371,7 +3372,7 @@
     template <typename Case>
     static void setupCallExpectations(DisplayTransactionTest* test) {
         Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test);
-        Case::DispSync::setupBeginResyncCallExpectations(test);
+        Case::DispSync::setupResetModelCallExpectations(test);
         Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON);
     }
 };
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index f6bf05a..409f90d 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -14,10 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
@@ -31,8 +27,8 @@
 #include "Layer.h"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockDispSync.h"
 #include "mock/MockEventThread.h"
+#include "mock/MockVsyncController.h"
 
 namespace android {
 
@@ -64,7 +60,7 @@
     static constexpr int32_t PRIORITY_UNSET = -1;
 
     void setupScheduler();
-    void setupComposer(int virtualDisplayCount);
+    void setupComposer(uint32_t virtualDisplayCount);
     sp<BufferQueueLayer> createBufferQueueLayer();
     sp<BufferStateLayer> createBufferStateLayer();
     sp<EffectLayer> createEffectLayer();
@@ -139,17 +135,18 @@
             .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
                                                        ISurfaceComposer::eConfigChangedSuppress)));
 
-    auto primaryDispSync = std::make_unique<mock::DispSync>();
+    auto vsyncController = std::make_unique<mock::VsyncController>();
+    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
 
-    EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
-    EXPECT_CALL(*primaryDispSync, getPeriod())
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*vsyncTracker, currentPeriod())
             .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
-    EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
-    mFlinger.setupScheduler(std::move(primaryDispSync), std::move(eventThread),
-                            std::move(sfEventThread));
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                            std::move(eventThread), std::move(sfEventThread));
 }
 
-void RefreshRateSelectionTest::setupComposer(int virtualDisplayCount) {
+void RefreshRateSelectionTest::setupComposer(uint32_t virtualDisplayCount) {
     mComposer = new Hwc2::mock::Composer();
     EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
     mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
@@ -281,6 +278,3 @@
 
 } // namespace
 } // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index 6c01f85..d4591fc 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -32,9 +32,9 @@
 #pragma clang diagnostic pop // ignored "-Wconversion"
 #include "TestableSurfaceFlinger.h"
 #include "mock/DisplayHardware/MockComposer.h"
-#include "mock/MockDispSync.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockMessageQueue.h"
+#include "mock/MockVsyncController.h"
 
 namespace android {
 
@@ -175,14 +175,15 @@
             .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
                                                        ISurfaceComposer::eConfigChangedSuppress)));
 
-    auto primaryDispSync = std::make_unique<mock::DispSync>();
+    auto vsyncController = std::make_unique<mock::VsyncController>();
+    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
 
-    EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
-    EXPECT_CALL(*primaryDispSync, getPeriod())
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*vsyncTracker, currentPeriod())
             .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
-    EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0));
-    mFlinger.setupScheduler(std::move(primaryDispSync), std::move(eventThread),
-                            std::move(sfEventThread));
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                            std::move(eventThread), std::move(sfEventThread));
 }
 
 void SetFrameRateTest::setupComposer(uint32_t virtualDisplayCount) {
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index ebb2d76..db3e0bd 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -19,12 +19,13 @@
 #include <gmock/gmock.h>
 #include <gui/ISurfaceComposer.h>
 
-#include "Scheduler/DispSync.h"
 #include "Scheduler/EventThread.h"
 #include "Scheduler/LayerHistory.h"
 #include "Scheduler/Scheduler.h"
 #include "Scheduler/VSyncTracker.h"
-#include "mock/MockDispSync.h"
+#include "Scheduler/VsyncController.h"
+#include "mock/MockVSyncTracker.h"
+#include "mock/MockVsyncController.h"
 
 namespace android {
 
@@ -32,14 +33,16 @@
 public:
     TestableScheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
                       bool useContentDetectionV2)
-          : TestableScheduler(std::make_unique<mock::DispSync>(), configs, callback,
+          : TestableScheduler(std::make_unique<mock::VsyncController>(),
+                              std::make_unique<mock::VSyncTracker>(), configs, callback,
                               useContentDetectionV2) {}
 
-    TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
+    TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
+                      std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
                       const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback,
                       bool useContentDetectionV2)
-          : Scheduler({std::move(primaryDispSync), nullptr, nullptr}, configs, callback,
-                      createLayerHistory(configs, useContentDetectionV2),
+          : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr}, configs,
+                      callback, createLayerHistory(configs, useContentDetectionV2),
                       {.supportKernelTimer = false,
                        .useContentDetection = true,
                        .useContentDetectionV2 = useContentDetectionV2}) {}
@@ -95,7 +98,7 @@
         // not report a leaked object, since the Scheduler instance may
         // still be referenced by something despite our best efforts to destroy
         // it after each test is done.
-        mVsyncSchedule.sync.reset();
+        mVsyncSchedule.controller.reset();
         mConnections.clear();
     }
 };
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 8c4232d..b024568 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -189,7 +189,8 @@
     }
 
     // The ISchedulerCallback argument can be nullptr for a no-op implementation.
-    void setupScheduler(std::unique_ptr<DispSync> primaryDispSync,
+    void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
+                        std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
                         std::unique_ptr<EventThread> appEventThread,
                         std::unique_ptr<EventThread> sfEventThread,
                         ISchedulerCallback* callback = nullptr, bool hasMultipleConfigs = false) {
@@ -217,9 +218,9 @@
         mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs());
 
         constexpr bool kUseContentDetectionV2 = false;
-        mScheduler =
-                new TestableScheduler(std::move(primaryDispSync), *mFlinger->mRefreshRateConfigs,
-                                      *(callback ?: this), kUseContentDetectionV2);
+        mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                                           *mFlinger->mRefreshRateConfigs, *(callback ?: this),
+                                           kUseContentDetectionV2);
 
         mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
         mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 44b3dc0..28415bc 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -31,9 +31,9 @@
 
 #include "TestableScheduler.h"
 #include "TestableSurfaceFlinger.h"
-#include "mock/MockDispSync.h"
 #include "mock/MockEventThread.h"
 #include "mock/MockMessageQueue.h"
+#include "mock/MockVsyncController.h"
 
 namespace android {
 
@@ -75,11 +75,12 @@
                         new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
                                                   ISurfaceComposer::eConfigChangedSuppress)));
 
-        EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0));
-        EXPECT_CALL(*mPrimaryDispSync, getPeriod())
+        EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*mVSyncTracker, currentPeriod())
                 .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
 
-        mFlinger.setupScheduler(std::unique_ptr<mock::DispSync>(mPrimaryDispSync),
+        mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
+                                std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
                                 std::move(eventThread), std::move(sfEventThread));
     }
 
@@ -89,7 +90,8 @@
     std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
 
     mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
-    mock::DispSync* mPrimaryDispSync = new mock::DispSync();
+    mock::VsyncController* mVsyncController = new mock::VsyncController();
+    mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker();
 
     struct TransactionInfo {
         Vector<ComposerState> states;
@@ -123,7 +125,7 @@
         ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
         // called in SurfaceFlinger::signalTransaction
         EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillOnce(Return(systemTime()));
+        EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillOnce(Return(systemTime()));
         TransactionInfo transaction;
         setupSingle(transaction, flags, syncInputWindows,
                     /*desiredPresentTime*/ -1);
@@ -156,7 +158,7 @@
         // first check will see desired present time has not passed,
         // but afterwards it will look like the desired present time has passed
         nsecs_t time = systemTime();
-        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
+        EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
                 .WillOnce(Return(time + nsecs_t(5 * 1e8)));
         TransactionInfo transaction;
         setupSingle(transaction, flags, syncInputWindows,
@@ -179,7 +181,7 @@
         // called in SurfaceFlinger::signalTransaction
         nsecs_t time = systemTime();
         EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
-        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
+        EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
                 .WillOnce(Return(time + nsecs_t(5 * 1e8)));
         // transaction that should go on the pending thread
         TransactionInfo transactionA;
@@ -244,7 +246,7 @@
     EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
 
     // nsecs_t time = systemTime();
-    EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_))
+    EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_))
             .WillOnce(Return(nsecs_t(5 * 1e8)))
             .WillOnce(Return(s2ns(2)));
     TransactionInfo transactionA; // transaction to go on pending queue
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 3408fed..1e5139c 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -65,7 +65,7 @@
     bool addVsyncTimestamp(nsecs_t) final { return true; }
 
     nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        std::lock_guard lock(mMutex);
         auto const normalized_to_base = time_point - mBase;
         auto const floor = (normalized_to_base) % mPeriod;
         if (floor == 0) {
@@ -75,13 +75,13 @@
     }
 
     void set_interval(nsecs_t interval, nsecs_t last_known) {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        std::lock_guard lock(mMutex);
         mPeriod = interval;
         mBase = last_known;
     }
 
     nsecs_t currentPeriod() const final {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        std::lock_guard lock(mMutex);
         return mPeriod;
     }
 
@@ -118,11 +118,11 @@
                  .earliestVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration});
 
         for (auto i = 0u; i < iterations - 1; i++) {
-            std::unique_lock<decltype(mMutex)> lk(mMutex);
-            mCv.wait(lk, [&] { return mCalled; });
+            std::unique_lock lock(mMutex);
+            mCv.wait(lock, [&] { return mCalled; });
             mCalled = false;
             auto last = mLastTarget;
-            lk.unlock();
+            lock.unlock();
 
             onEachFrame(last);
 
@@ -132,8 +132,8 @@
         }
 
         // wait for the last callback.
-        std::unique_lock<decltype(mMutex)> lk(mMutex);
-        mCv.wait(lk, [&] { return mCalled; });
+        std::unique_lock lock(mMutex);
+        mCv.wait(lock, [&] { return mCalled; });
     }
 
     void with_callback_times(std::function<void(std::vector<nsecs_t> const&)> const& fn) const {
@@ -142,7 +142,7 @@
 
 private:
     void callback_called(nsecs_t time) {
-        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        std::lock_guard lock(mMutex);
         mCallbackTimes.push_back(time);
         mCalled = true;
         mLastTarget = time;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 24e243f..69731fd 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -144,18 +144,18 @@
     operator VSyncDispatch::CallbackToken() const { return mToken; }
 
     void pause(nsecs_t, nsecs_t) {
-        std::unique_lock<std::mutex> lk(mMutex);
+        std::unique_lock lock(mMutex);
         mPause = true;
         mCv.notify_all();
 
-        mCv.wait_for(lk, mPauseAmount, [this] { return !mPause; });
+        mCv.wait_for(lock, mPauseAmount, [this] { return !mPause; });
 
         mResourcePresent = (mResource.lock() != nullptr);
     }
 
     bool waitForPause() {
-        std::unique_lock<std::mutex> lk(mMutex);
-        auto waiting = mCv.wait_for(lk, 10s, [this] { return mPause; });
+        std::unique_lock lock(mMutex);
+        auto waiting = mCv.wait_for(lock, 10s, [this] { return mPause; });
         return waiting;
     }
 
@@ -164,7 +164,7 @@
     bool resourcePresent() { return mResourcePresent; }
 
     void unpause() {
-        std::unique_lock<std::mutex> lk(mMutex);
+        std::unique_lock lock(mMutex);
         mPause = false;
         mCv.notify_all();
     }
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 0eae01e..0dcaf26 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -70,24 +70,24 @@
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
-std::shared_ptr<FenceTime> generateInvalidFence() {
+std::shared_ptr<android::FenceTime> generateInvalidFence() {
     sp<Fence> fence = new Fence();
-    return std::make_shared<FenceTime>(fence);
+    return std::make_shared<android::FenceTime>(fence);
 }
 
-std::shared_ptr<FenceTime> generatePendingFence() {
+std::shared_ptr<android::FenceTime> generatePendingFence() {
     sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
-    return std::make_shared<FenceTime>(fence);
+    return std::make_shared<android::FenceTime>(fence);
 }
 
-void signalFenceWithTime(std::shared_ptr<FenceTime> const& fence, nsecs_t time) {
-    FenceTime::Snapshot snap(time);
+void signalFenceWithTime(std::shared_ptr<android::FenceTime> const& fence, nsecs_t time) {
+    android::FenceTime::Snapshot snap(time);
     fence->applyTrustedSnapshot(snap);
 }
 
-std::shared_ptr<FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
+std::shared_ptr<android::FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
     sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
-    std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
+    std::shared_ptr<android::FenceTime> ft = std::make_shared<android::FenceTime>(fence);
     signalFenceWithTime(ft, time);
     return ft;
 }
@@ -152,7 +152,7 @@
 }
 
 TEST_F(VSyncReactorTest, limitsPendingFences) {
-    std::array<std::shared_ptr<FenceTime>, kPendingLimit * 2> fences;
+    std::array<std::shared_ptr<android::FenceTime>, kPendingLimit * 2> fences;
     std::array<nsecs_t, fences.size()> fakeTimes;
     std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); });
     std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable {
@@ -193,86 +193,48 @@
     mReactor.setIgnorePresentFences(true);
 
     nsecs_t const newPeriod = 5000;
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
-    EXPECT_TRUE(mReactor.addResyncSample(0, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_FALSE(mReactor.addResyncSample(newPeriod, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 
     EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 }
 
-TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
-    nsecs_t const fakeTimestamp = 4839;
-    EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
-    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
-            .Times(1)
-            .WillOnce(Return(fakeTimestamp));
-
-    EXPECT_THAT(mReactor.computeNextRefresh(0, mMockClock->now()), Eq(fakeTimestamp));
-}
-
-TEST_F(VSyncReactorTest, queriesTrackerForExpectedPresentTime) {
-    nsecs_t const fakeTimestamp = 4839;
-    EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
-    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
-            .Times(1)
-            .WillOnce(Return(fakeTimestamp));
-
-    EXPECT_THAT(mReactor.expectedPresentTime(mMockClock->now()), Eq(fakeTimestamp));
-}
-
-TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
-    nsecs_t const fakeTimestamp = 4839;
-    nsecs_t const fakePeriod = 1010;
-    nsecs_t const mFakeNow = 2214;
-    int const numPeriodsOut = 3;
-    EXPECT_CALL(*mMockClock, now()).WillOnce(Return(mFakeNow));
-    EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
-    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(mFakeNow + numPeriodsOut * fakePeriod))
-            .WillOnce(Return(fakeTimestamp));
-    EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut, mMockClock->now()), Eq(fakeTimestamp));
-}
-
-TEST_F(VSyncReactorTest, getPeriod) {
-    nsecs_t const fakePeriod = 1010;
-    EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
-    EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod));
-}
-
 TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) {
     nsecs_t const newPeriod = 5000;
     EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
     bool periodFlushed = true;
-    EXPECT_TRUE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 
-    EXPECT_TRUE(mReactor.addResyncSample(20000, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(20000, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 
     Mock::VerifyAndClearExpectations(mMockTracker.get());
     EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
 
-    EXPECT_FALSE(mReactor.addResyncSample(25000, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(25000, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 }
 
 TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) {
     nsecs_t sampleTime = 0;
     nsecs_t const newPeriod = 5000;
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
     bool periodFlushed = true;
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 
-    mReactor.setPeriod(period);
-    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    mReactor.startPeriodTransition(period);
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 }
 
@@ -281,16 +243,18 @@
     nsecs_t const secondPeriod = 5000;
     nsecs_t const thirdPeriod = 2000;
 
-    mReactor.setPeriod(secondPeriod);
+    mReactor.startPeriodTransition(secondPeriod);
     bool periodFlushed = true;
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    mReactor.setPeriod(thirdPeriod);
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
+    mReactor.startPeriodTransition(thirdPeriod);
+    EXPECT_TRUE(
+            mReactor.addHwVsyncTimestamp(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += thirdPeriod, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(
+            mReactor.addHwVsyncTimestamp(sampleTime += thirdPeriod, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 }
 
@@ -305,9 +269,10 @@
     nsecs_t skewyPeriod = period >> 1;
     bool periodFlushed = false;
     nsecs_t sampleTime = 0;
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(
+            mReactor.addHwVsyncTimestamp(sampleTime += skewyPeriod, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 }
 
@@ -325,22 +290,22 @@
 
 TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
     nsecs_t const newPeriod = 5000;
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
     EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 }
 
 TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) {
     nsecs_t const newPeriod = 5000;
     EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
     bool periodFlushed = true;
-    EXPECT_TRUE(mReactor.addResyncSample(5000, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(5000, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
     Mock::VerifyAndClearExpectations(mMockTracker.get());
 
     EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
-    EXPECT_FALSE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 }
 
@@ -349,7 +314,7 @@
     bool periodFlushed = false;
 
     EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
-    EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(fakeTimestamp, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 }
 
@@ -357,23 +322,23 @@
     bool periodFlushed = false;
     nsecs_t const newPeriod = 4000;
 
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
     auto time = 0;
     auto constexpr numTimestampSubmissions = 10;
     for (auto i = 0; i < numTimestampSubmissions; i++) {
         time += period;
-        EXPECT_TRUE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+        EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
         EXPECT_FALSE(periodFlushed);
     }
 
     time += newPeriod;
-    EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 
     for (auto i = 0; i < numTimestampSubmissions; i++) {
         time += newPeriod;
-        EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+        EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
         EXPECT_FALSE(periodFlushed);
     }
 }
@@ -382,14 +347,14 @@
     auto time = 0;
     bool periodFlushed = false;
     nsecs_t const newPeriod = 4000;
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
     time += period;
-    mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
+    mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed);
     EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 
     time += newPeriod;
-    mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
+    mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed);
 
     EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 }
@@ -398,7 +363,7 @@
     auto time = 0;
     bool periodFlushed = false;
     nsecs_t const newPeriod = 4000;
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
     static auto constexpr numSamplesWithNewPeriod = 4;
     Sequence seq;
@@ -412,20 +377,20 @@
             .WillRepeatedly(Return(false));
     EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(numSamplesWithNewPeriod);
 
-    EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
 
-    EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
     // confirmed period, but predictor wants numRequest samples. This one and prior are valid.
-    EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
-    EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
-    EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
 }
 
 TEST_F(VSyncReactorTest, hwVsyncturnsOffOnConfirmationWhenTrackerDoesntRequest) {
     auto time = 0;
     bool periodFlushed = false;
     nsecs_t const newPeriod = 4000;
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
     Sequence seq;
     EXPECT_CALL(*mMockTracker, needsMoreSamples())
@@ -434,9 +399,9 @@
             .WillRepeatedly(Return(false));
     EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
 
-    EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
-    EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
-    EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed));
 }
 
 TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) {
@@ -445,7 +410,7 @@
     nsecs_t const newPeriod1 = 4000;
     nsecs_t const newPeriod2 = 7000;
 
-    mReactor.setPeriod(newPeriod1);
+    mReactor.startPeriodTransition(newPeriod1);
 
     Sequence seq;
     EXPECT_CALL(*mMockTracker, needsMoreSamples())
@@ -458,22 +423,17 @@
             .WillRepeatedly(Return(false));
     EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(7);
 
-    EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
-    EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed));
     // confirmed period, but predictor wants numRequest samples. This one and prior are valid.
-    EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
-    EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
 
-    mReactor.setPeriod(newPeriod2);
-    EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed));
-    EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
-    EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
-    EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed));
-}
-
-TEST_F(VSyncReactorTest, beginResyncResetsModel) {
-    EXPECT_CALL(*mMockTracker, resetModel());
-    mReactor.beginResync();
+    mReactor.startPeriodTransition(newPeriod2);
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed));
 }
 
 TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) {
@@ -482,13 +442,13 @@
     mReactor.setIgnorePresentFences(true);
 
     nsecs_t const newPeriod = 5000;
-    mReactor.setPeriod(newPeriod);
+    mReactor.startPeriodTransition(newPeriod);
 
-    EXPECT_TRUE(mReactor.addResyncSample(0, 0, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_TRUE(mReactor.addResyncSample(newPeriod, 0, &periodFlushed));
+    EXPECT_TRUE(mReactor.addHwVsyncTimestamp(newPeriod, 0, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_FALSE(mReactor.addResyncSample(newPeriod, newPeriod, &periodFlushed));
+    EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, newPeriod, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 
     EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
@@ -505,25 +465,25 @@
 
     // First, set the same period, which should only be confirmed when we receive two
     // matching callbacks
-    idleReactor.setPeriod(10000);
-    EXPECT_TRUE(idleReactor.addResyncSample(0, 0, &periodFlushed));
+    idleReactor.startPeriodTransition(10000);
+    EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 0, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
     // Correct period but incorrect timestamp delta
-    EXPECT_TRUE(idleReactor.addResyncSample(0, 10000, &periodFlushed));
+    EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 10000, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
     // Correct period and correct timestamp delta
-    EXPECT_FALSE(idleReactor.addResyncSample(10000, 10000, &periodFlushed));
+    EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(10000, 10000, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 
     // Then, set a new period, which should be confirmed as soon as we receive a callback
     // reporting the new period
     nsecs_t const newPeriod = 5000;
-    idleReactor.setPeriod(newPeriod);
+    idleReactor.startPeriodTransition(newPeriod);
     // Incorrect timestamp delta and period
-    EXPECT_TRUE(idleReactor.addResyncSample(20000, 10000, &periodFlushed));
+    EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(20000, 10000, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
     // Incorrect timestamp delta but correct period
-    EXPECT_FALSE(idleReactor.addResyncSample(20000, 5000, &periodFlushed));
+    EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(20000, 5000, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 
     EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0)));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
similarity index 87%
rename from services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
rename to services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
index bbd9f77..8a18123 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "mock/MockDispSync.h"
+#include "mock/MockVSyncTracker.h"
 #include <thread>
 
 using namespace std::chrono_literals;
@@ -22,8 +22,8 @@
 namespace mock {
 
 // Explicit default instantiation is recommended.
-DispSync::DispSync() = default;
-DispSync::~DispSync() = default;
+VSyncTracker::VSyncTracker() = default;
+VSyncTracker::~VSyncTracker() = default;
 
 } // namespace mock
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
new file mode 100644
index 0000000..03ddc85
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "Scheduler/VSyncTracker.h"
+
+namespace android::mock {
+
+class VSyncTracker : public android::scheduler::VSyncTracker {
+public:
+    VSyncTracker();
+    ~VSyncTracker() override;
+
+    MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
+    MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+    MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+    MOCK_METHOD1(setPeriod, void(nsecs_t));
+    MOCK_METHOD0(resetModel, void());
+    MOCK_CONST_METHOD0(needsMoreSamples, bool());
+    MOCK_CONST_METHOD1(dump, void(std::string&));
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp
similarity index 79%
copy from services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
copy to services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp
index bbd9f77..25ae1bd 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp
@@ -14,16 +14,14 @@
  * limitations under the License.
  */
 
-#include "mock/MockDispSync.h"
+#include "mock/MockVsyncController.h"
 #include <thread>
 
 using namespace std::chrono_literals;
-namespace android {
-namespace mock {
+namespace android::mock {
 
 // Explicit default instantiation is recommended.
-DispSync::DispSync() = default;
-DispSync::~DispSync() = default;
+VsyncController::VsyncController() = default;
+VsyncController::~VsyncController() = default;
 
-} // namespace mock
-} // namespace android
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
similarity index 63%
rename from services/surfaceflinger/tests/unittests/mock/MockDispSync.h
rename to services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
index 41445c8..1d87546 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -18,26 +18,20 @@
 
 #include <gmock/gmock.h>
 
-#include "Scheduler/DispSync.h"
+#include "Scheduler/VsyncController.h"
 
 namespace android {
 namespace mock {
 
-class DispSync : public android::DispSync {
+class VsyncController : public android::scheduler::VsyncController {
 public:
-    DispSync();
-    ~DispSync() override;
+    VsyncController();
+    ~VsyncController() override;
 
     MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
-    MOCK_METHOD0(beginResync, void());
-    MOCK_METHOD3(addResyncSample, bool(nsecs_t, std::optional<nsecs_t>, bool*));
-    MOCK_METHOD1(setPeriod, void(nsecs_t));
-    MOCK_METHOD0(getPeriod, nsecs_t());
-    MOCK_METHOD0(getIntendedPeriod, nsecs_t());
-    MOCK_METHOD1(setRefreshSkipCount, void(int));
-    MOCK_CONST_METHOD2(computeNextRefresh, nsecs_t(int, nsecs_t));
+    MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*));
+    MOCK_METHOD1(startPeriodTransition, void(nsecs_t));
     MOCK_METHOD1(setIgnorePresentFences, void(bool));
-    MOCK_METHOD1(expectedPresentTime, nsecs_t(nsecs_t));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp
index 3f8cd67..f2870b0 100644
--- a/services/vibratorservice/VibratorCallbackScheduler.cpp
+++ b/services/vibratorservice/VibratorCallbackScheduler.cpp
@@ -71,14 +71,17 @@
 
 void CallbackScheduler::loop() {
     while (true) {
-        std::lock_guard<std::mutex> lock(mMutex);
+        std::unique_lock<std::mutex> lock(mMutex);
         if (mFinished) {
             // Destructor was called, so let the callback thread die.
             break;
         }
         while (!mQueue.empty() && mQueue.top().isExpired()) {
-            mQueue.top().run();
+            DelayedCallback callback = mQueue.top();
             mQueue.pop();
+            lock.unlock();
+            callback.run();
+            lock.lock();
         }
         if (mQueue.empty()) {
             // Wait until a new callback is scheduled.
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index ce20aeb..9672644 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -501,6 +501,10 @@
     }
     if (externalControlResult.withDefault(false)) {
         capabilities |= Capabilities::EXTERNAL_CONTROL;
+
+        if (amplitudeResult.withDefault(false)) {
+            capabilities |= Capabilities::EXTERNAL_AMPLITUDE_CONTROL;
+        }
     }
 
     return HalResult<Capabilities>::fromReturn(externalControlResult, capabilities);
diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp
new file mode 100644
index 0000000..c1a03a1
--- /dev/null
+++ b/services/vibratorservice/benchmarks/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_benchmark {
+    name: "libvibratorservice_benchmarks",
+    srcs: [
+        "VibratorHalControllerBenchmarks.cpp",
+    ],
+    shared_libs: [
+        "libbinder",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+        "libvibratorservice",
+        "android.hardware.vibrator-cpp",
+        "android.hardware.vibrator@1.0",
+        "android.hardware.vibrator@1.1",
+        "android.hardware.vibrator@1.2",
+        "android.hardware.vibrator@1.3",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+}
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
new file mode 100644
index 0000000..2bf9360
--- /dev/null
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "PowerHalControllerBenchmarks"
+
+#include <benchmark/benchmark.h>
+#include <vibratorservice/VibratorHalController.h>
+
+using ::android::enum_range;
+using ::benchmark::Counter;
+using ::benchmark::Fixture;
+using ::benchmark::kMicrosecond;
+using ::benchmark::State;
+using ::benchmark::internal::Benchmark;
+
+using namespace android;
+using namespace std::chrono_literals;
+
+class VibratorBench : public Fixture {
+public:
+    void SetUp(State& /*state*/) override { mController.init(); }
+
+    void TearDown(State& /*state*/) override { mController.off(); }
+
+    static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); }
+
+    static void DefaultArgs(Benchmark* /*b*/) {
+        // none
+    }
+
+protected:
+    vibrator::HalController mController;
+
+    auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
+
+    bool hasCapabilities(vibrator::HalResult<vibrator::Capabilities> result,
+                         vibrator::Capabilities query) {
+        if (!result.isOk()) {
+            return false;
+        }
+        return (result.value() | query) == query;
+    }
+};
+
+#define BENCHMARK_WRAPPER(fixt, test, code)                \
+    BENCHMARK_DEFINE_F(fixt, test)                         \
+    /* NOLINTNEXTLINE */                                   \
+    (State& state){code} BENCHMARK_REGISTER_F(fixt, test) \
+            ->Apply(fixt::DefaultConfig)                   \
+            ->Apply(fixt::DefaultArgs)
+
+BENCHMARK_WRAPPER(VibratorBench, init, {
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        state.ResumeTiming();
+        controller.init();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, initCached, {
+    for (auto _ : state) {
+        mController.init();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, ping, {
+    for (auto _ : state) {
+        mController.ping();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, tryReconnect, {
+    for (auto _ : state) {
+        mController.tryReconnect();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, on, {
+    auto duration = 60s;
+    auto callback = []() {};
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mController.on(duration, callback);
+        state.PauseTiming();
+        mController.off();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, off, {
+    auto duration = 60s;
+    auto callback = []() {};
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        mController.on(duration, callback);
+        state.ResumeTiming();
+        mController.off();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setAmplitude, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::AMPLITUDE_CONTROL)) {
+        return;
+    }
+
+    auto duration = 60s;
+    auto callback = []() {};
+    auto amplitude = UINT8_MAX;
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        controller.on(duration, callback);
+        state.ResumeTiming();
+        controller.setAmplitude(amplitude);
+        state.PauseTiming();
+        controller.off();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::AMPLITUDE_CONTROL)) {
+        return;
+    }
+
+    auto duration = 6000s;
+    auto callback = []() {};
+    auto amplitude = UINT8_MAX;
+
+    mController.on(duration, callback);
+
+    for (auto _ : state) {
+        mController.setAmplitude(amplitude);
+    }
+
+    mController.off();
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalControl, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::EXTERNAL_CONTROL)) {
+        return;
+    }
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        state.ResumeTiming();
+        controller.setExternalControl(true);
+        state.PauseTiming();
+        controller.setExternalControl(false);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::EXTERNAL_CONTROL)) {
+        return;
+    }
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mController.setExternalControl(true);
+        state.PauseTiming();
+        mController.setExternalControl(false);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL)) {
+        return;
+    }
+
+    auto amplitude = UINT8_MAX;
+
+    mController.setExternalControl(true);
+
+    for (auto _ : state) {
+        mController.setAmplitude(amplitude);
+    }
+
+    mController.setExternalControl(false);
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getCapabilities, {
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        state.ResumeTiming();
+        controller.getCapabilities();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getCapabilitiesCached, {
+    // First call to cache values.
+    mController.getCapabilities();
+
+    for (auto _ : state) {
+        mController.getCapabilities();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedEffects, {
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        state.ResumeTiming();
+        controller.getSupportedEffects();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedEffectsCached, {
+    // First call to cache values.
+    mController.getSupportedEffects();
+
+    for (auto _ : state) {
+        mController.getSupportedEffects();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitives, {
+    for (auto _ : state) {
+        state.PauseTiming();
+        vibrator::HalController controller;
+        controller.init();
+        state.ResumeTiming();
+        controller.getSupportedPrimitives();
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitivesCached, {
+    // First call to cache values.
+    mController.getSupportedPrimitives();
+
+    for (auto _ : state) {
+        mController.getSupportedPrimitives();
+    }
+});
+
+class VibratorEffectsBench : public VibratorBench {
+public:
+    static void DefaultArgs(Benchmark* b) {
+        vibrator::HalController controller;
+        auto effectsResult = controller.getSupportedEffects();
+        if (!effectsResult.isOk()) {
+            return;
+        }
+
+        std::vector<hardware::vibrator::Effect> supported = effectsResult.value();
+        b->ArgNames({"Effect", "Strength"});
+        for (const auto& effect : enum_range<hardware::vibrator::Effect>()) {
+            if (std::find(supported.begin(), supported.end(), effect) == supported.end()) {
+                continue;
+            }
+            for (const auto& strength : enum_range<hardware::vibrator::EffectStrength>()) {
+                b->Args({static_cast<long>(effect), static_cast<long>(strength)});
+            }
+        }
+    }
+
+protected:
+    auto getEffect(const State& state) const {
+        return static_cast<hardware::vibrator::Effect>(this->getOtherArg(state, 0));
+    }
+
+    auto getStrength(const State& state) const {
+        return static_cast<hardware::vibrator::EffectStrength>(this->getOtherArg(state, 1));
+    }
+};
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::ALWAYS_ON_CONTROL)) {
+        return;
+    }
+
+    int32_t id = 1;
+    auto effect = getEffect(state);
+    auto strength = getStrength(state);
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mController.alwaysOnEnable(id, effect, strength);
+        state.PauseTiming();
+        mController.alwaysOnDisable(id);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::ALWAYS_ON_CONTROL)) {
+        return;
+    }
+
+    int32_t id = 1;
+    auto effect = getEffect(state);
+    auto strength = getStrength(state);
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        mController.alwaysOnEnable(id, effect, strength);
+        state.ResumeTiming();
+        mController.alwaysOnDisable(id);
+    }
+});
+
+BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, {
+    auto effect = getEffect(state);
+    auto strength = getStrength(state);
+    auto callback = []() {};
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mController.performEffect(effect, strength, callback);
+        state.PauseTiming();
+        mController.off();
+    }
+});
+
+class VibratorPrimitivesBench : public VibratorBench {
+public:
+    static void DefaultArgs(Benchmark* b) {
+        vibrator::HalController controller;
+        auto primitivesResult = controller.getSupportedPrimitives();
+        if (!primitivesResult.isOk()) {
+            return;
+        }
+
+        std::vector<hardware::vibrator::CompositePrimitive> supported = primitivesResult.value();
+        b->ArgNames({"Primitive"});
+        for (const auto& primitive : enum_range<hardware::vibrator::CompositePrimitive>()) {
+            if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
+                continue;
+            }
+            b->Args({static_cast<long>(primitive)});
+        }
+    }
+
+protected:
+    auto getPrimitive(const State& state) const {
+        return static_cast<hardware::vibrator::CompositePrimitive>(this->getOtherArg(state, 0));
+    }
+};
+
+BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, {
+    auto capabilitiesResult = mController.getCapabilities();
+
+    if (!hasCapabilities(capabilitiesResult, vibrator::Capabilities::COMPOSE_EFFECTS)) {
+        return;
+    }
+
+    hardware::vibrator::CompositeEffect effect;
+    effect.primitive = getPrimitive(state);
+    effect.scale = 1.0f;
+    effect.delayMs = 0;
+
+    std::vector<hardware::vibrator::CompositeEffect> effects;
+    effects.push_back(effect);
+    auto callback = []() {};
+
+    for (auto _ : state) {
+        state.ResumeTiming();
+        mController.performComposedEffect(effects, callback);
+        state.PauseTiming();
+        mController.off();
+    }
+});
+
+BENCHMARK_MAIN();
\ No newline at end of file
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
index 5de6257..08652f4 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp
@@ -118,7 +118,8 @@
 
     auto result = mWrapper->getCapabilities();
     ASSERT_TRUE(result.isOk());
-    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL,
+    ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL |
+                      vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL,
               result.value());
 }