Merge "Retrieve a list of composition settings from front end layer"
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index be7e3e1..acca11a 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -76,6 +76,7 @@
     defaults: ["dumpstate_cflag_defaults"],
     shared_libs: [
         "android.hardware.dumpstate@1.0",
+        "android.hardware.dumpstate@1.1",
         "libziparchive",
         "libbase",
         "libbinder",
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 312db67..3a79357 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -64,6 +64,8 @@
 #include <android-base/unique_fd.h>
 #include <android/content/pm/IPackageManagerNative.h>
 #include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
+#include <android/hardware/dumpstate/1.1/IDumpstateDevice.h>
+#include <android/hardware/dumpstate/1.1/types.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <android/os/IIncidentCompanion.h>
 #include <binder/IServiceManager.h>
@@ -85,7 +87,11 @@
 #include "DumpstateService.h"
 #include "dumpstate.h"
 
-using ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
+using IDumpstateDevice_1_0 = ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
+using IDumpstateDevice_1_1 = ::android::hardware::dumpstate::V1_1::IDumpstateDevice;
+using ::android::hardware::dumpstate::V1_1::DumpstateMode;
+using ::android::hardware::dumpstate::V1_1::DumpstateStatus;
+using ::android::hardware::dumpstate::V1_1::toString;
 using ::std::literals::chrono_literals::operator""ms;
 using ::std::literals::chrono_literals::operator""s;
 
@@ -135,6 +141,11 @@
 static char cmdline_buf[16384] = "(unknown)";
 static const char *dump_traces_path = nullptr;
 static const uint64_t USER_CONSENT_TIMEOUT_MS = 30 * 1000;
+// Because telephony reports are significantly faster to collect (< 10 seconds vs. > 2 minutes),
+// it's often the case that they time out far too quickly for consent with such a hefty dialog for
+// the user to read. For telephony reports only, we increase the default timeout to 2 minutes to
+// roughly match full reports' durations.
+static const uint64_t TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS = 2 * 60 * 1000;
 
 // TODO: variables and functions below should be part of dumpstate object
 
@@ -149,6 +160,7 @@
 #define RECOVERY_DATA_DIR "/data/misc/recovery"
 #define UPDATE_ENGINE_LOG_DIR "/data/misc/update_engine_log"
 #define LOGPERSIST_DATA_DIR "/data/misc/logd"
+#define PREREBOOT_DATA_DIR "/data/misc/prereboot"
 #define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
 #define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref"
 #define XFRM_STAT_PROC_FILE "/proc/net/xfrm_stat"
@@ -879,6 +891,14 @@
                CommandOptions::WithTimeoutInMs(timeout_ms).Build());
 }
 
+static void DoRadioLogcat() {
+    unsigned long timeout_ms = logcat_timeout({"radio"});
+    RunCommand(
+        "RADIO LOG",
+        {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+        CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */);
+}
+
 static void DoLogcat() {
     unsigned long timeout_ms;
     // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags");
@@ -897,11 +917,7 @@
         "STATS LOG",
         {"logcat", "-b", "stats", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
         CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */);
-    timeout_ms = logcat_timeout({"radio"});
-    RunCommand(
-        "RADIO LOG",
-        {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
-        CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */);
+    DoRadioLogcat();
 
     RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
 
@@ -1429,11 +1445,14 @@
     RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
 
     /* Binder state is expensive to look at as it uses a lot of memory. */
-    DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
-    DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
-    DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
-    DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats");
-    DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state");
+    std::string binder_logs_dir = access("/dev/binderfs/binder_logs", R_OK) ?
+            "/sys/kernel/debug/binder" : "/dev/binderfs/binder_logs";
+
+    DumpFile("BINDER FAILED TRANSACTION LOG", binder_logs_dir + "/failed_transaction_log");
+    DumpFile("BINDER TRANSACTION LOG", binder_logs_dir + "/transaction_log");
+    DumpFile("BINDER TRANSACTIONS", binder_logs_dir + "/transactions");
+    DumpFile("BINDER STATS", binder_logs_dir + "/stats");
+    DumpFile("BINDER STATE", binder_logs_dir + "/state");
 
     /* Add window and surface trace files. */
     if (!PropertiesHelper::IsUserBuild()) {
@@ -1573,6 +1592,7 @@
         ds.AddDir(PROFILE_DATA_DIR_CUR, true);
         ds.AddDir(PROFILE_DATA_DIR_REF, true);
     }
+    ds.AddDir(PREREBOOT_DATA_DIR, false);
     add_mountinfo();
     DumpIpTablesAsRoot();
     DumpDynamicPartitionInfo();
@@ -1611,8 +1631,10 @@
     return status;
 }
 
-// This method collects common dumpsys for telephony and wifi
-static void DumpstateRadioCommon() {
+// This method collects common dumpsys for telephony and wifi. Typically, wifi
+// reports are fine to include all information, but telephony reports on user
+// builds need to strip some content (see DumpstateTelephonyOnly).
+static void DumpstateRadioCommon(bool include_sensitive_info = true) {
     DumpIpTablesAsRoot();
 
     ds.AddDir(LOGPERSIST_DATA_DIR, false);
@@ -1621,27 +1643,51 @@
         return;
     }
 
-    do_dmesg();
-    DoLogcat();
+    // We need to be picky about some stuff for telephony reports on user builds.
+    if (!include_sensitive_info) {
+        // Only dump the radio log buffer (other buffers and dumps contain too much unrelated info).
+        DoRadioLogcat();
+    } else {
+        // Contains various system properties and process startup info.
+        do_dmesg();
+        // Logs other than the radio buffer may contain package/component names and potential PII.
+        DoLogcat();
+        // Too broad for connectivity problems.
+        DoKmsg();
+        // Contains unrelated hardware info (camera, NFC, biometrics, ...).
+        DumpHals();
+    }
+
     DumpPacketStats();
-    DoKmsg();
     DumpIpAddrAndRules();
     dump_route_tables();
-    DumpHals();
-
     RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
                CommandOptions::WithTimeout(10).Build());
 }
 
-// This method collects dumpsys for telephony debugging only
+// We use "telephony" here for legacy reasons, though this now really means "connectivity" (cellular
+// + wifi + networking). This method collects dumpsys for connectivity debugging only. General rules
+// for what can be included on user builds: all reported information MUST directly relate to
+// connectivity debugging or customer support and MUST NOT contain unrelated personally identifiable
+// information. This information MUST NOT identify user-installed packages (UIDs are OK, package
+// names are not), and MUST NOT contain logs of user application traffic.
+// TODO(b/148168577) rename this and other related fields/methods to "connectivity" instead.
 static void DumpstateTelephonyOnly() {
     DurationReporter duration_reporter("DUMPSTATE");
 
     const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
 
-    DumpstateRadioCommon();
+    const bool include_sensitive_info = !PropertiesHelper::IsUserBuild();
 
-    RunCommand("SYSTEM PROPERTIES", {"getprop"});
+    DumpstateRadioCommon(include_sensitive_info);
+
+    if (include_sensitive_info) {
+        // Contains too much unrelated PII, and given the unstructured nature of sysprops, we can't
+        // really cherrypick all of the connectivity-related ones. Apps generally have no business
+        // reading these anyway, and there should be APIs to supply the info in a more app-friendly
+        // way.
+        RunCommand("SYSTEM PROPERTIES", {"getprop"});
+    }
 
     printf("========================================================\n");
     printf("== Android Framework Services\n");
@@ -1649,15 +1695,28 @@
 
     RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(),
                SEC_TO_MSEC(10));
-    RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(),
-               SEC_TO_MSEC(10));
-    RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
+    // TODO(b/146521742) build out an argument to include bound services here for user builds
     RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(),
                SEC_TO_MSEC(10));
     RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(),
                SEC_TO_MSEC(10));
-    RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(),
+    RunDumpsys("DUMPSYS", {"netpolicy"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
+    RunDumpsys("DUMPSYS", {"network_management"}, CommandOptions::WithTimeout(90).Build(),
                SEC_TO_MSEC(10));
+    if (include_sensitive_info) {
+        // Contains raw IP addresses, omit from reports on user builds.
+        RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
+        // Contains raw destination IP/MAC addresses, omit from reports on user builds.
+        RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(),
+                   SEC_TO_MSEC(10));
+        // Contains package/component names, omit from reports on user builds.
+        RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(),
+                   SEC_TO_MSEC(10));
+        // Contains package names, but should be relatively simple to remove them (also contains
+        // UIDs already), omit from reports on user builds.
+        RunDumpsys("BATTERYSTATS", {"deviceidle"}, CommandOptions::WithTimeout(90).Build(),
+                   SEC_TO_MSEC(10));
+    }
 
     printf("========================================================\n");
     printf("== Running Application Services\n");
@@ -1665,18 +1724,24 @@
 
     RunDumpsys("TELEPHONY SERVICES", {"activity", "service", "TelephonyDebugService"});
 
-    printf("========================================================\n");
-    printf("== Running Application Services (non-platform)\n");
-    printf("========================================================\n");
+    if (include_sensitive_info) {
+        printf("========================================================\n");
+        printf("== Running Application Services (non-platform)\n");
+        printf("========================================================\n");
 
-    RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
-            DUMPSYS_COMPONENTS_OPTIONS);
+        // Contains package/component names and potential PII, omit from reports on user builds.
+        // To get dumps of the active CarrierService(s) on user builds, we supply an argument to the
+        // carrier_config dumpsys instead.
+        RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
+                   DUMPSYS_COMPONENTS_OPTIONS);
 
-    printf("========================================================\n");
-    printf("== Checkins\n");
-    printf("========================================================\n");
+        printf("========================================================\n");
+        printf("== Checkins\n");
+        printf("========================================================\n");
 
-    RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
+        // Contains package/component names, omit from reports on user builds.
+        RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
+    }
 
     printf("========================================================\n");
     printf("== dumpstate: done (id %d)\n", ds.id_);
@@ -1833,8 +1898,8 @@
             std::bind([](std::string path) { android::os::UnlinkAndLogOnError(path); }, paths[i])));
     }
 
-    sp<IDumpstateDevice> dumpstate_device(IDumpstateDevice::getService());
-    if (dumpstate_device == nullptr) {
+    sp<IDumpstateDevice_1_0> dumpstate_device_1_0(IDumpstateDevice_1_0::getService());
+    if (dumpstate_device_1_0 == nullptr) {
         MYLOGE("No IDumpstateDevice implementation\n");
         return;
     }
@@ -1865,29 +1930,54 @@
         handle.get()->data[i] = fd.release();
     }
 
-    // Given that bugreport is required to diagnose failures, it's better to
-    // set an arbitrary amount of timeout for IDumpstateDevice than to block the
-    // rest of bugreport. In the timeout case, we will kill dumpstate board HAL
-    // and grab whatever dumped
-    std::packaged_task<bool()>
-            dumpstate_task([paths, dumpstate_device, &handle]() -> bool {
-            android::hardware::Return<void> status = dumpstate_device->dumpstateBoard(handle.get());
+    // Given that bugreport is required to diagnose failures, it's better to set an arbitrary amount
+    // of timeout for IDumpstateDevice than to block the rest of bugreport. In the timeout case, we
+    // will kill the HAL and grab whatever it dumped in time.
+    constexpr size_t timeout_sec = 30;
+    // Prefer version 1.1 if available. New devices launching with R are no longer allowed to
+    // implement just 1.0.
+    const char* descriptor_to_kill;
+    using DumpstateBoardTask = std::packaged_task<bool()>;
+    DumpstateBoardTask dumpstate_board_task;
+    sp<IDumpstateDevice_1_1> dumpstate_device_1_1(
+        IDumpstateDevice_1_1::castFrom(dumpstate_device_1_0));
+    if (dumpstate_device_1_1 != nullptr) {
+        MYLOGI("Using IDumpstateDevice v1.1");
+        descriptor_to_kill = IDumpstateDevice_1_1::descriptor;
+        dumpstate_board_task = DumpstateBoardTask([this, dumpstate_device_1_1, &handle]() -> bool {
+            ::android::hardware::Return<DumpstateStatus> status =
+                dumpstate_device_1_1->dumpstateBoard_1_1(handle.get(), options_->dumpstate_hal_mode,
+                                                         SEC_TO_MSEC(timeout_sec));
+            if (!status.isOk()) {
+                MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
+                return false;
+            } else if (status != DumpstateStatus::OK) {
+                MYLOGE("dumpstateBoard failed with DumpstateStatus::%s\n", toString(status).c_str());
+                return false;
+            }
+            return true;
+        });
+    } else {
+        MYLOGI("Using IDumpstateDevice v1.0");
+        descriptor_to_kill = IDumpstateDevice_1_0::descriptor;
+        dumpstate_board_task = DumpstateBoardTask([dumpstate_device_1_0, &handle]() -> bool {
+            ::android::hardware::Return<void> status =
+                dumpstate_device_1_0->dumpstateBoard(handle.get());
             if (!status.isOk()) {
                 MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
                 return false;
             }
             return true;
         });
+    }
+    auto result = dumpstate_board_task.get_future();
+    std::thread(std::move(dumpstate_board_task)).detach();
 
-    auto result = dumpstate_task.get_future();
-    std::thread(std::move(dumpstate_task)).detach();
-
-    constexpr size_t timeout_sec = 30;
     if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) {
         MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate vendor HAL\n", timeout_sec);
-        if (!android::base::SetProperty("ctl.interface_restart",
-                                        android::base::StringPrintf("%s/default",
-                                                                    IDumpstateDevice::descriptor))) {
+        if (!android::base::SetProperty(
+                "ctl.interface_restart",
+                android::base::StringPrintf("%s/default", descriptor_to_kill))) {
             MYLOGE("Couldn't restart dumpstate HAL\n");
         }
     }
@@ -1919,15 +2009,14 @@
             continue;
         }
         AddZipEntry(kDumpstateBoardFiles[i], paths[i]);
+        printf("*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str());
     }
-
-    printf("*** See dumpstate-board.txt entry ***\n");
 }
 
 static void ShowUsage() {
     fprintf(stderr,
             "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-d] [-p] "
-            "[-z]] [-s] [-S] [-q] [-P] [-R] [-V version]\n"
+            "[-z] [-s] [-S] [-q] [-P] [-R] [-V version]\n"
             "  -h: display this help message\n"
             "  -b: play sound file instead of vibrate, at beginning of job\n"
             "  -e: play sound file instead of vibrate, at end of job\n"
@@ -2137,32 +2226,40 @@
     switch (mode) {
         case Dumpstate::BugreportMode::BUGREPORT_FULL:
             options->do_fb = true;
+            options->dumpstate_hal_mode = DumpstateMode::FULL;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
             // Currently, the dumpstate binder is only used by Shell to update progress.
             options->do_start_service = true;
             options->do_progress_updates = true;
             options->do_fb = false;
+            options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_REMOTE:
             options->do_vibrate = false;
             options->is_remote_mode = true;
             options->do_fb = false;
+            options->dumpstate_hal_mode = DumpstateMode::REMOTE;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_WEAR:
             options->do_start_service = true;
             options->do_progress_updates = true;
             options->do_zip_file = true;
             options->do_fb = true;
+            options->dumpstate_hal_mode = DumpstateMode::WEAR;
             break;
+        // TODO(b/148168577) rename TELEPHONY everywhere to CONNECTIVITY.
         case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY:
             options->telephony_only = true;
+            options->do_progress_updates = true;
             options->do_fb = false;
+            options->dumpstate_hal_mode = DumpstateMode::CONNECTIVITY;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_WIFI:
             options->wifi_only = true;
             options->do_zip_file = true;
             options->do_fb = false;
+            options->dumpstate_hal_mode = DumpstateMode::WIFI;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
             break;
@@ -2173,11 +2270,13 @@
     MYLOGI(
         "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_fb: %d "
         "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
-        "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s args: %s\n",
+        "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
+        "args: %s\n",
         options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
         options.do_fb, options.is_remote_mode, options.show_header_only, options.do_start_service,
         options.telephony_only, options.wifi_only, options.do_progress_updates,
-        options.bugreport_fd.get(), options.bugreport_mode.c_str(), options.args.c_str());
+        options.bugreport_fd.get(), options.bugreport_mode.c_str(),
+        toString(options.dumpstate_hal_mode).c_str(), options.args.c_str());
 }
 
 void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
@@ -2641,8 +2740,13 @@
     if (consent_result == UserConsentResult::UNAVAILABLE) {
         // User has not responded yet.
         uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs();
-        if (elapsed_ms < USER_CONSENT_TIMEOUT_MS) {
-            uint delay_seconds = (USER_CONSENT_TIMEOUT_MS - elapsed_ms) / 1000;
+        // Telephony is a fast report type, particularly on user builds where information may be
+        // more aggressively limited. To give the user time to read the consent dialog, increase the
+        // timeout.
+        uint64_t timeout_ms = options_->telephony_only ? TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS
+                                                       : USER_CONSENT_TIMEOUT_MS;
+        if (elapsed_ms < timeout_ms) {
+            uint delay_seconds = (timeout_ms - elapsed_ms) / 1000;
             MYLOGD("Did not receive user consent yet; going to wait for %d seconds", delay_seconds);
             sleep(delay_seconds);
         }
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 7d9b113..111c098 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -27,6 +27,7 @@
 
 #include <android-base/macros.h>
 #include <android-base/unique_fd.h>
+#include <android/hardware/dumpstate/1.1/types.h>
 #include <android/os/BnIncidentAuthListener.h>
 #include <android/os/IDumpstate.h>
 #include <android/os/IDumpstateListener.h>
@@ -366,6 +367,11 @@
         bool wifi_only = false;
         // Whether progress updates should be published.
         bool do_progress_updates = false;
+        // The mode we'll use when calling IDumpstateDevice::dumpstateBoard.
+        // TODO(b/148168577) get rid of the AIDL values, replace them with the HAL values instead.
+        // The HAL is actually an API surface that can be validated, while the AIDL is not (@hide).
+        ::android::hardware::dumpstate::V1_1::DumpstateMode dumpstate_hal_mode =
+            ::android::hardware::dumpstate::V1_1::DumpstateMode::DEFAULT;
         // File descriptor to output zip file.
         android::base::unique_fd bugreport_fd;
         // File descriptor to screenshot file.
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 1ae073c..76b9960 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -36,20 +36,22 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <cutils/properties.h>
 #include <android-base/unique_fd.h>
+#include <android/hardware/dumpstate/1.1/types.h>
+#include <cutils/properties.h>
 
 namespace android {
 namespace os {
 namespace dumpstate {
 
+using ::android::hardware::dumpstate::V1_1::DumpstateMode;
 using ::testing::EndsWith;
 using ::testing::HasSubstr;
-using ::testing::IsNull;
 using ::testing::IsEmpty;
+using ::testing::IsNull;
 using ::testing::NotNull;
-using ::testing::StrEq;
 using ::testing::StartsWith;
+using ::testing::StrEq;
 using ::testing::Test;
 using ::testing::internal::CaptureStderr;
 using ::testing::internal::CaptureStdout;
@@ -174,6 +176,7 @@
     EXPECT_FALSE(options_.do_fb);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeAdbBugreport) {
@@ -200,6 +203,7 @@
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) {
@@ -224,6 +228,7 @@
     EXPECT_FALSE(options_.do_fb);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeFullBugReport) {
@@ -231,6 +236,7 @@
     EXPECT_TRUE(options_.do_add_date);
     EXPECT_TRUE(options_.do_fb);
     EXPECT_TRUE(options_.do_zip_file);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::FULL);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -249,6 +255,7 @@
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_TRUE(options_.do_start_service);
     EXPECT_FALSE(options_.do_fb);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -265,6 +272,7 @@
     EXPECT_TRUE(options_.is_remote_mode);
     EXPECT_FALSE(options_.do_vibrate);
     EXPECT_FALSE(options_.do_fb);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::REMOTE);
 
     // Other options retain default values
     EXPECT_FALSE(options_.use_control_socket);
@@ -280,6 +288,7 @@
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_TRUE(options_.do_start_service);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WEAR);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -295,12 +304,13 @@
     EXPECT_FALSE(options_.do_fb);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.telephony_only);
+    EXPECT_TRUE(options_.do_progress_updates);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::CONNECTIVITY);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
     EXPECT_FALSE(options_.use_control_socket);
     EXPECT_FALSE(options_.show_header_only);
-    EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
     EXPECT_FALSE(options_.use_socket);
 }
@@ -311,6 +321,7 @@
     EXPECT_FALSE(options_.do_fb);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.wifi_only);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WIFI);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -337,6 +348,7 @@
     EXPECT_TRUE(options_.do_add_date);
     EXPECT_TRUE(options_.do_fb);
     EXPECT_TRUE(options_.do_zip_file);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -375,6 +387,7 @@
     EXPECT_FALSE(options_.do_fb);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializePartial2) {
@@ -403,6 +416,7 @@
     EXPECT_FALSE(options_.do_zip_file);
     EXPECT_FALSE(options_.use_socket);
     EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeHelp) {
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 5597bcd..a427c8d 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -29,6 +29,7 @@
 #include <utils/Log.h>
 #include <utils/Vector.h>
 
+#include <iostream>
 #include <fcntl.h>
 #include <getopt.h>
 #include <stdio.h>
@@ -231,14 +232,14 @@
     const size_t N = services.size();
     if (N > 1) {
         // first print a list of the current services
-        aout << "Currently running services:" << endl;
+        std::cout << "Currently running services:" << std::endl;
 
         for (size_t i=0; i<N; i++) {
             sp<IBinder> service = sm_->checkService(services[i]);
 
             if (service != nullptr) {
                 bool skipped = IsSkipped(skippedServices, services[i]);
-                aout << "  " << services[i] << (skipped ? " (skipped)" : "") << endl;
+                std::cout << "  " << services[i] << (skipped ? " (skipped)" : "") << std::endl;
             }
         }
     }
@@ -263,10 +264,10 @@
                           asProto, elapsedDuration, bytesWritten);
 
             if (status == TIMED_OUT) {
-                aout << endl
+                std::cout << std::endl
                      << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs
-                     << "ms) EXPIRED ***" << endl
-                     << endl;
+                     << "ms) EXPIRED ***" << std::endl
+                     << std::endl;
             }
 
             if (addSeparator) {
@@ -332,14 +333,14 @@
                                   const Vector<String16>& args) {
     sp<IBinder> service = sm_->checkService(serviceName);
     if (service == nullptr) {
-        aerr << "Can't find service: " << serviceName << endl;
+        std::cerr << "Can't find service: " << serviceName << std::endl;
         return NAME_NOT_FOUND;
     }
 
     int sfd[2];
     if (pipe(sfd) != 0) {
-        aerr << "Failed to create pipe to dump service info for " << serviceName << ": "
-             << strerror(errno) << endl;
+        std::cerr << "Failed to create pipe to dump service info for " << serviceName << ": "
+             << strerror(errno) << std::endl;
         return -errno;
     }
 
@@ -359,13 +360,13 @@
             err = dumpPidToFd(service, remote_end);
             break;
         default:
-            aerr << "Unknown dump type" << static_cast<int>(type) << endl;
+            std::cerr << "Unknown dump type" << static_cast<int>(type) << std::endl;
             return;
         }
 
         if (err != OK) {
-            aerr << "Error dumping service info status_t: " << statusToString(err) << " "
-                 << serviceName << endl;
+            std::cerr << "Error dumping service info status_t: " << statusToString(err) << " "
+                 << serviceName << std::endl;
         }
     });
     return OK;
@@ -422,8 +423,8 @@
 
         int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
         if (rc < 0) {
-            aerr << "Error in poll while dumping service " << serviceName << " : "
-                 << strerror(errno) << endl;
+            std::cerr << "Error in poll while dumping service " << serviceName << " : "
+                 << strerror(errno) << std::endl;
             status = -errno;
             break;
         } else if (rc == 0) {
@@ -434,8 +435,8 @@
         char buf[4096];
         rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf)));
         if (rc < 0) {
-            aerr << "Failed to read while dumping service " << serviceName << ": "
-                 << strerror(errno) << endl;
+            std::cerr << "Failed to read while dumping service " << serviceName << ": "
+                 << strerror(errno) << std::endl;
             status = -errno;
             break;
         } else if (rc == 0) {
@@ -444,8 +445,8 @@
         }
 
         if (!WriteFully(fd, buf, rc)) {
-            aerr << "Failed to write while dumping service " << serviceName << ": "
-                 << strerror(errno) << endl;
+            std::cerr << "Failed to write while dumping service " << serviceName << ": "
+                 << strerror(errno) << std::endl;
             status = -errno;
             break;
         }
diff --git a/cmds/dumpsys/main.cpp b/cmds/dumpsys/main.cpp
index 8ba0eba..fa4cc97 100644
--- a/cmds/dumpsys/main.cpp
+++ b/cmds/dumpsys/main.cpp
@@ -21,10 +21,9 @@
 #include "dumpsys.h"
 
 #include <binder/IServiceManager.h>
-#include <binder/TextOutput.h>
 
+#include <iostream>
 #include <signal.h>
-#include <stdio.h>
 
 using namespace android;
 
@@ -34,7 +33,7 @@
     fflush(stdout);
     if (sm == nullptr) {
         ALOGE("Unable to get default service manager!");
-        aerr << "dumpsys: Unable to get default service manager!" << endl;
+        std::cerr << "dumpsys: Unable to get default service manager!" << std::endl;
         return 20;
     }
 
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 08d4657..4288735 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -2684,7 +2684,7 @@
 }
 
 // Mount volume's CE and DE storage to mirror
-binder::Status InstalldNativeService::onPrivateVolumeMounted(
+binder::Status InstalldNativeService::tryMountDataMirror(
         const std::unique_ptr<std::string>& uuid) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
@@ -2696,24 +2696,50 @@
     }
 
     const char* uuid_ = uuid->c_str();
-    // Mount CE mirror
+
     std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
     std::lock_guard<std::recursive_mutex> lock(mLock);
-    if (fs_prepare_dir(mirrorVolCePath.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
+    if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
         return error("Failed to create CE mirror");
     }
-    auto cePath = StringPrintf("%s/user_ce", create_data_path(uuid_).c_str());
+
+    std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+    if (fs_prepare_dir(mirrorVolDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+        return error("Failed to create DE mirror");
+    }
+
+    auto cePath = StringPrintf("%s/user", create_data_path(uuid_).c_str());
+    auto dePath = StringPrintf("%s/user_de", create_data_path(uuid_).c_str());
+
+    if (access(cePath.c_str(), F_OK) != 0) {
+        return error("Cannot access CE path: " + cePath);
+    }
+    if (access(dePath.c_str(), F_OK) != 0) {
+        return error("Cannot access DE path: " + dePath);
+    }
+
+    struct stat ceStat, mirrorCeStat;
+    if (stat(cePath.c_str(), &ceStat) != 0) {
+        return error("Failed to stat " + cePath);
+    }
+    if (stat(mirrorVolCePath.c_str(), &mirrorCeStat) != 0) {
+        return error("Failed to stat " + mirrorVolCePath);
+    }
+
+    if (mirrorCeStat.st_ino == ceStat.st_ino) {
+        // As it's being called by prepareUserStorage, it can be called multiple times.
+        // Hence, we if we mount it already, we should skip it.
+        LOG(WARNING) << "CE dir is mounted already: " + cePath;
+        return ok();
+    }
+
+    // Mount CE mirror
     if (TEMP_FAILURE_RETRY(mount(cePath.c_str(), mirrorVolCePath.c_str(), NULL,
             MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
         return error("Failed to mount " + mirrorVolCePath);
     }
 
     // Mount DE mirror
-    std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
-    if (fs_prepare_dir(mirrorVolDePath.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
-        return error("Failed to create DE mirror");
-    }
-    auto dePath = StringPrintf("%s/user_de", create_data_path(uuid_).c_str());
     if (TEMP_FAILURE_RETRY(mount(dePath.c_str(), mirrorVolDePath.c_str(), NULL,
             MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
         return error("Failed to mount " + mirrorVolDePath);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index eb35fd3..27c59b0 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -155,7 +155,7 @@
     binder::Status invalidateMounts();
     binder::Status isQuotaSupported(const std::unique_ptr<std::string>& volumeUuid,
             bool* _aidl_return);
-    binder::Status onPrivateVolumeMounted(const std::unique_ptr<std::string>& volumeUuid);
+    binder::Status tryMountDataMirror(const std::unique_ptr<std::string>& volumeUuid);
     binder::Status onPrivateVolumeRemoved(const std::unique_ptr<std::string>& volumeUuid);
 
     binder::Status prepareAppProfile(const std::string& packageName,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 80d9703..f2e86ba 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -116,7 +116,7 @@
             int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags);
     void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
             int userId, long ceSnapshotInode, int snapshotId, int storageFlags);
-    void onPrivateVolumeMounted(@nullable @utf8InCpp String volumeUuid);
+    void tryMountDataMirror(@nullable @utf8InCpp String volumeUuid);
     void onPrivateVolumeRemoved(@nullable @utf8InCpp String volumeUuid);
 
     void migrateLegacyObbData();
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index e434afd..70bbc33 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -299,9 +299,10 @@
 // Namespace for Android Runtime flags applied during boot time.
 static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
 // Feature flag name for running the JIT in Zygote experiment, b/119800099.
-static const char* ENABLE_APEX_IMAGE = "enable_apex_image";
-// Location of the apex image.
-static const char* kApexImage = "/system/framework/apex.art";
+static const char* ENABLE_JITZYGOTE_IMAGE = "enable_apex_image";
+// Location of the JIT Zygote image.
+static const char* kJitZygoteImage =
+    "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof";
 
 // Phenotype property name for enabling profiling the boot class path.
 static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
@@ -405,9 +406,9 @@
                 GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault);
 
         std::string boot_image;
-        std::string use_apex_image =
+        std::string use_jitzygote_image =
             server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
-                                                                 ENABLE_APEX_IMAGE,
+                                                                 ENABLE_JITZYGOTE_IMAGE,
                                                                  /*default_value=*/ "");
 
         std::string profile_boot_class_path = GetProperty("dalvik.vm.profilebootclasspath", "");
@@ -417,10 +418,10 @@
                 PROFILE_BOOT_CLASS_PATH,
                 /*default_value=*/ profile_boot_class_path);
 
-        if (use_apex_image == "true" || profile_boot_class_path == "true") {
-          boot_image = StringPrintf("-Ximage:%s", kApexImage);
+        if (use_jitzygote_image == "true" || profile_boot_class_path == "true") {
+          boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
         } else {
-          boot_image = MapPropertyToArg("dalvik.vm.boot-image", "-Ximage:%s");
+          boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
         }
 
         // clang FORTIFY doesn't let us use strlen in constant array bounds, so we
@@ -513,7 +514,8 @@
         AddArg(instruction_set_variant_arg);
         AddArg(instruction_set_features_arg);
 
-        AddRuntimeArg(boot_image);
+        AddArg(boot_image);
+
         AddRuntimeArg(bootclasspath);
         AddRuntimeArg(dex2oat_Xms_arg);
         AddRuntimeArg(dex2oat_Xmx_arg);
@@ -1271,6 +1273,11 @@
 Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path,
         bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) {
 
+    // We don't create an image for secondary dex files.
+    if (is_secondary_dex) {
+        return Dex2oatFileWrapper();
+    }
+
     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.
diff --git a/include/input/Input.h b/include/input/Input.h
index cf0814c..14a7288 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -73,6 +73,19 @@
     AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
 };
 
+/**
+ * Allowed VerifiedKeyEvent flags. All other flags from KeyEvent do not get verified.
+ * These values must be kept in sync with VerifiedKeyEvent.java
+ */
+constexpr int32_t VERIFIED_KEY_EVENT_FLAGS = AKEY_EVENT_FLAG_CANCELED;
+
+/**
+ * Allowed VerifiedMotionEventFlags. All other flags from MotionEvent do not get verified.
+ * These values must be kept in sync with VerifiedMotionEvent.java
+ */
+constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS =
+        AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
 enum {
     /* Used when a motion event is not associated with any display.
      * Typically used for non-pointer events. */
@@ -718,6 +731,55 @@
     bool mInTouchMode;
 };
 
+/**
+ * Base class for verified events.
+ * Do not create a VerifiedInputEvent explicitly.
+ * Use helper functions to create them from InputEvents.
+ */
+struct __attribute__((__packed__)) VerifiedInputEvent {
+    enum class Type : int32_t {
+        KEY = AINPUT_EVENT_TYPE_KEY,
+        MOTION = AINPUT_EVENT_TYPE_MOTION,
+    };
+
+    Type type;
+    int32_t deviceId;
+    nsecs_t eventTimeNanos;
+    uint32_t source;
+    int32_t displayId;
+};
+
+/**
+ * Same as KeyEvent, but only contains the data that can be verified.
+ * If you update this class, you must also update VerifiedKeyEvent.java
+ */
+struct __attribute__((__packed__)) VerifiedKeyEvent : public VerifiedInputEvent {
+    int32_t action;
+    nsecs_t downTimeNanos;
+    int32_t flags;
+    int32_t keyCode;
+    int32_t scanCode;
+    int32_t metaState;
+    int32_t repeatCount;
+};
+
+/**
+ * Same as MotionEvent, but only contains the data that can be verified.
+ * If you update this class, you must also update VerifiedMotionEvent.java
+ */
+struct __attribute__((__packed__)) VerifiedMotionEvent : public VerifiedInputEvent {
+    float rawX;
+    float rawY;
+    int32_t actionMasked;
+    nsecs_t downTimeNanos;
+    int32_t flags;
+    int32_t metaState;
+    int32_t buttonState;
+};
+
+VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event);
+VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event);
+
 /*
  * Input event factory.
  */
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 5f9d400..bc541f4 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -170,7 +170,6 @@
     name: "libbinder_aidl_test_stub",
     local_include_dir: "aidl",
     srcs: [":libbinder_aidl"],
-    visibility: [":__subpackages__"],
     vendor_available: true,
     backend: {
         java: {
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 4dcd07a..9e89c57 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -1230,6 +1230,11 @@
                 if (error < NO_ERROR) reply.setError(error);
                 sendReply(reply, 0);
             } else {
+                if (error != OK || reply.dataSize() != 0) {
+                    alog << "oneway function results will be dropped but finished with status "
+                         << statusToString(error)
+                         << " and parcel size " << reply.dataSize() << endl;
+                }
                 LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
             }
 
diff --git a/libs/binder/fuzzer/Android.bp b/libs/binder/fuzzer/Android.bp
index 521d36f..d2b4d52 100644
--- a/libs/binder/fuzzer/Android.bp
+++ b/libs/binder/fuzzer/Android.bp
@@ -16,7 +16,6 @@
     ],
     static_libs: [
         "libbase",
-        "libbinderthreadstate",
         "libcgrouprc",
         "libcgrouprc_format",
         "libcutils",
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index e752c45..75dcdc8 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -24,10 +24,13 @@
 
 #include <android-base/logging.h>
 #include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <private/android_filesystem_config.h>
 
 using DeathRecipient = ::android::IBinder::DeathRecipient;
 
 using ::android::IBinder;
+using ::android::IResultReceiver;
 using ::android::Parcel;
 using ::android::sp;
 using ::android::status_t;
@@ -158,6 +161,45 @@
 
         binder_status_t status = getClass()->onTransact(this, code, &in, &out);
         return PruneStatusT(status);
+    } else if (code == SHELL_COMMAND_TRANSACTION) {
+        int in = data.readFileDescriptor();
+        int out = data.readFileDescriptor();
+        int err = data.readFileDescriptor();
+
+        int argc = data.readInt32();
+        std::vector<String8> utf8Args;          // owns memory of utf8s
+        std::vector<const char*> utf8Pointers;  // what can be passed over NDK API
+        for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
+            utf8Args.push_back(String8(data.readString16()));
+            utf8Pointers.push_back(utf8Args[i].c_str());
+        }
+
+        data.readStrongBinder();  // skip over the IShellCallback
+        sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(data.readStrongBinder());
+
+        // Shell commands should only be callable by ADB.
+        uid_t uid = AIBinder_getCallingUid();
+        if (uid != AID_ROOT && uid != AID_SHELL) {
+            if (resultReceiver != nullptr) {
+                resultReceiver->send(-1);
+            }
+            return STATUS_PERMISSION_DENIED;
+        }
+
+        // Check that the file descriptors are valid.
+        if (in == STATUS_BAD_TYPE || out == STATUS_BAD_TYPE || err == STATUS_BAD_TYPE) {
+            if (resultReceiver != nullptr) {
+                resultReceiver->send(-1);
+            }
+            return STATUS_BAD_VALUE;
+        }
+
+        binder_status_t status = getClass()->handleShellCommand(
+                this, in, out, err, utf8Pointers.data(), utf8Pointers.size());
+        if (resultReceiver != nullptr) {
+            resultReceiver->send(status);
+        }
+        return status;
     } else {
         return BBinder::onTransact(code, data, reply, flags);
     }
@@ -266,6 +308,13 @@
     clazz->onDump = onDump;
 }
 
+void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz,
+                                          AIBinder_handleShellCommand handleShellCommand) {
+    CHECK(clazz != nullptr) << "setHandleShellCommand requires non-null clazz";
+
+    clazz->handleShellCommand = handleShellCommand;
+}
+
 void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
     CHECK(who == mWho);
 
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 5cb68c2..5779427 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android/binder_ibinder.h>
+#include <android/binder_shell.h>
 #include "ibinder_internal.h"
 
 #include <atomic>
@@ -115,6 +116,7 @@
 
     // optional methods for a class
     AIBinder_onDump onDump;
+    AIBinder_handleShellCommand handleShellCommand;
 
    private:
     // This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to
diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
index 83a1048..7331ba2 100644
--- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
@@ -30,6 +30,11 @@
 #include <android/binder_auto_utils.h>
 #include <android/binder_ibinder.h>
 
+#if __has_include(<android/binder_shell.h>)
+#include <android/binder_shell.h>
+#define HAS_BINDER_SHELL_COMMAND
+#endif  //_has_include
+
 #include <assert.h>
 
 #include <memory>
@@ -108,7 +113,15 @@
     /**
      * Dumps information about the interface. By default, dumps nothing.
      */
-    virtual inline binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/);
+    virtual inline binder_status_t dump(int fd, const char** args, uint32_t numArgs);
+
+#ifdef HAS_BINDER_SHELL_COMMAND
+    /**
+     * Process shell commands. By default, does nothing.
+     */
+    virtual inline binder_status_t handleShellCommand(int in, int out, int err, const char** argv,
+                                                      uint32_t argc);
+#endif
 
     /**
      * Interprets this binder as this underlying interface if this has stored an ICInterface in the
@@ -136,6 +149,11 @@
         static inline void onDestroy(void* userData);
         static inline binder_status_t onDump(AIBinder* binder, int fd, const char** args,
                                              uint32_t numArgs);
+
+#ifdef HAS_BINDER_SHELL_COMMAND
+        static inline binder_status_t handleShellCommand(AIBinder* binder, int in, int out, int err,
+                                                         const char** argv, uint32_t argc);
+#endif
     };
 };
 
@@ -191,6 +209,13 @@
     return STATUS_OK;
 }
 
+#ifdef HAS_BINDER_SHELL_COMMAND
+binder_status_t ICInterface::handleShellCommand(int /*in*/, int /*out*/, int /*err*/,
+                                                const char** /*argv*/, uint32_t /*argc*/) {
+    return STATUS_OK;
+}
+#endif
+
 std::shared_ptr<ICInterface> ICInterface::asInterface(AIBinder* binder) {
     return ICInterfaceData::getInterface(binder);
 }
@@ -203,9 +228,12 @@
         return nullptr;
     }
 
-    // We can't know if this method is overriden by a subclass interface, so we must register
-    // ourselves. The default (nothing to dump) is harmless.
+    // We can't know if these methods are overridden by a subclass interface, so we must register
+    // ourselves. The defaults are harmless.
     AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump);
+#ifdef HAS_BINDER_SHELL_COMMAND
+    AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand);
+#endif
     return clazz;
 }
 
@@ -234,6 +262,15 @@
     return interface->dump(fd, args, numArgs);
 }
 
+#ifdef HAS_BINDER_SHELL_COMMAND
+binder_status_t ICInterface::ICInterfaceData::handleShellCommand(AIBinder* binder, int in, int out,
+                                                                 int err, const char** argv,
+                                                                 uint32_t argc) {
+    std::shared_ptr<ICInterface> interface = getInterface(binder);
+    return interface->handleShellCommand(in, out, err, argv, argc);
+}
+#endif
+
 template <typename INTERFACE>
 SpAIBinder BnCInterface<INTERFACE>::asBinder() {
     std::lock_guard<std::mutex> l(mMutex);
diff --git a/libs/binder/ndk/include_platform/android/binder_shell.h b/libs/binder/ndk/include_platform/android/binder_shell.h
new file mode 100644
index 0000000..17b38b0
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_shell.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/binder_ibinder.h>
+
+__BEGIN_DECLS
+
+/**
+ * Function to execute a shell command.
+ *
+ * Available since API level 30.
+ *
+ * \param binder the binder executing the command
+ * \param in input file descriptor, should be flushed, ownership is not passed
+ * \param out output file descriptor, should be flushed, ownership is not passed
+ * \param err error file descriptor, should be flushed, ownership is not passed
+ * \param argv array of null-terminated strings for command (may be null if argc
+ * is 0)
+ * \param argc length of argv array
+ *
+ * \return binder_status_t result of transaction
+ */
+typedef binder_status_t (*AIBinder_handleShellCommand)(AIBinder* binder, int in, int out, int err,
+                                                       const char** argv, uint32_t argc);
+
+/**
+ * This sets the implementation of handleShellCommand for a class.
+ *
+ * If this isn't set, nothing will be executed when handleShellCommand is called.
+ *
+ * Available since API level 30.
+ *
+ * \param handleShellCommand function to call when a shell transaction is
+ * received
+ */
+void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz,
+                                          AIBinder_handleShellCommand handleShellCommand)
+        __INTRODUCED_IN(30);
+
+__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index f3158d7..7e72f22 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -110,6 +110,7 @@
     AIBinder_markSystemStability; # apex
     AIBinder_markVendorStability; # llndk
     AIBinder_markVintfStability; # apex llndk
+    AIBinder_Class_setHandleShellCommand; # apex llndk
   local:
     *;
 };
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp
index 513d8c2..cb4b20f 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/test/Android.bp
@@ -60,6 +60,7 @@
     defaults: ["test_libbinder_ndk_test_defaults"],
     srcs: ["libbinder_ndk_unit_test.cpp"],
     static_libs: [
+        "IBinderNdkUnitTest-cpp",
         "IBinderNdkUnitTest-ndk_platform",
     ],
     test_suites: ["general-tests"],
diff --git a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
index 51dd169..fd30d87 100644
--- a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <IBinderNdkUnitTest.h>
 #include <aidl/BnBinderNdkUnitTest.h>
 #include <aidl/BnEmpty.h>
 #include <android-base/logging.h>
@@ -26,13 +27,16 @@
 // warning: this is assuming that libbinder_ndk is using the same copy
 // of libbinder that we are.
 #include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <binder/IServiceManager.h>
+#include <binder/IShellCallback.h>
 
 #include <sys/prctl.h>
 #include <chrono>
 #include <condition_variable>
 #include <mutex>
 
-using ::android::sp;
+using namespace android;
 
 constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
 constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest";
@@ -48,6 +52,14 @@
         android::IPCThreadState::self()->flushCommands();
         return ndk::ScopedAStatus::ok();
     }
+    binder_status_t handleShellCommand(int /*in*/, int out, int /*err*/, const char** args,
+                                       uint32_t numArgs) override {
+        for (uint32_t i = 0; i < numArgs; i++) {
+            dprintf(out, "%s", args[i]);
+        }
+        fsync(out);
+        return STATUS_OK;
+    }
 };
 
 int generatedService() {
@@ -296,6 +308,92 @@
     EXPECT_TRUE(destroyed);
 }
 
+class MyResultReceiver : public BnResultReceiver {
+   public:
+    Mutex mMutex;
+    Condition mCondition;
+    bool mHaveResult = false;
+    int32_t mResult = 0;
+
+    virtual void send(int32_t resultCode) {
+        AutoMutex _l(mMutex);
+        mResult = resultCode;
+        mHaveResult = true;
+        mCondition.signal();
+    }
+
+    int32_t waitForResult() {
+        AutoMutex _l(mMutex);
+        while (!mHaveResult) {
+            mCondition.wait(mMutex);
+        }
+        return mResult;
+    }
+};
+
+class MyShellCallback : public BnShellCallback {
+   public:
+    virtual int openFile(const String16& /*path*/, const String16& /*seLinuxContext*/,
+                         const String16& /*mode*/) {
+        // Empty implementation.
+        return 0;
+    }
+};
+
+bool ReadFdToString(int fd, std::string* content) {
+    char buf[64];
+    ssize_t n;
+    while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
+        content->append(buf, n);
+    }
+    return (n == 0) ? true : false;
+}
+
+std::string shellCmdToString(sp<IBinder> unitTestService, const std::vector<const char*>& args) {
+    int inFd[2] = {-1, -1};
+    int outFd[2] = {-1, -1};
+    int errFd[2] = {-1, -1};
+
+    EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, inFd));
+    EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, outFd));
+    EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, errFd));
+
+    sp<MyShellCallback> cb = new MyShellCallback();
+    sp<MyResultReceiver> resultReceiver = new MyResultReceiver();
+
+    Vector<String16> argsVec;
+    for (int i = 0; i < args.size(); i++) {
+        argsVec.add(String16(args[i]));
+    }
+    status_t error = IBinder::shellCommand(unitTestService, inFd[0], outFd[0], errFd[0], argsVec,
+                                           cb, resultReceiver);
+    EXPECT_EQ(error, android::OK);
+
+    status_t res = resultReceiver->waitForResult();
+    EXPECT_EQ(res, android::OK);
+
+    close(inFd[0]);
+    close(inFd[1]);
+    close(outFd[0]);
+    close(errFd[0]);
+    close(errFd[1]);
+
+    std::string ret;
+    EXPECT_TRUE(ReadFdToString(outFd[1], &ret));
+    close(outFd[1]);
+    return ret;
+}
+
+TEST(NdkBinder, UseHandleShellCommand) {
+    static const sp<android::IServiceManager> sm(android::defaultServiceManager());
+    sp<IBinder> testService = sm->getService(String16(kBinderNdkUnitTestService));
+
+    EXPECT_EQ("", shellCmdToString(testService, {}));
+    EXPECT_EQ("", shellCmdToString(testService, {"", ""}));
+    EXPECT_EQ("Hello world!", shellCmdToString(testService, {"Hello ", "world!"}));
+    EXPECT_EQ("CMD", shellCmdToString(testService, {"C", "M", "D"}));
+}
+
 int main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
 
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 5a7f9a9..3ee8187 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -40,7 +40,7 @@
     },
 
     srcs: ["binderDriverInterfaceTest.cpp"],
-    test_suites: ["device-tests"],
+    test_suites: ["device-tests", "vts-core"],
 }
 
 cc_test {
@@ -69,7 +69,7 @@
         "libbinder",
         "libutils",
     ],
-    test_suites: ["device-tests"],
+    test_suites: ["device-tests", "vts-core"],
     require_root: true,
 }
 
@@ -131,7 +131,7 @@
         "liblog",
         "libutils",
     ],
-    test_suites: ["device-tests"],
+    test_suites: ["device-tests", "vts-core"],
     require_root: true,
 }
 
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index 4655e1d..c186110 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -63,15 +63,3 @@
     ],
     require_root: true,
 }
-
-// TODO(b/148692216): remove empty lib
-cc_library {
-    name: "libbinderthreadstate",
-    recovery_available: true,
-    vendor_available: false,
-    vndk: {
-        enabled: true,
-        support_system_process: true,
-    },
-    host_supported: true,
-}
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 31a399b..b6b81bb 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -58,6 +58,7 @@
 static std::set<uint32_t> gAllFreqs;
 static unique_fd gTisMapFd;
 static unique_fd gConcurrentMapFd;
+static unique_fd gUidLastUpdateMapFd;
 
 static std::optional<std::vector<uint32_t>> readNumbersFromFile(const std::string &path) {
     std::string data;
@@ -144,6 +145,10 @@
             unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
     if (gConcurrentMapFd < 0) return false;
 
+    gUidLastUpdateMapFd =
+            unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_last_update_map")};
+    if (gUidLastUpdateMapFd < 0) return false;
+
     gInitialized = true;
     return true;
 }
@@ -263,6 +268,18 @@
     return out;
 }
 
+static std::optional<bool> uidUpdatedSince(uint32_t uid, uint64_t lastUpdate,
+                                           uint64_t *newLastUpdate) {
+    uint64_t uidLastUpdate;
+    if (findMapEntry(gUidLastUpdateMapFd, &uid, &uidLastUpdate)) return {};
+    // Updates that occurred during the previous read may have been missed. To mitigate
+    // this, don't ignore entries updated up to 1s before *lastUpdate
+    constexpr uint64_t NSEC_PER_SEC = 1000000000;
+    if (uidLastUpdate + NSEC_PER_SEC < lastUpdate) return false;
+    if (uidLastUpdate > *newLastUpdate) *newLastUpdate = uidLastUpdate;
+    return true;
+}
+
 // Retrieve the times in ns that each uid spent running at each CPU freq.
 // Return contains no value on error, otherwise it contains a map from uids to vectors of vectors
 // using the format:
@@ -271,6 +288,14 @@
 // where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq.
 std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
 getUidsCpuFreqTimes() {
+    return getUidsUpdatedCpuFreqTimes(nullptr);
+}
+
+// Retrieve the times in ns that each uid spent running at each CPU freq, excluding UIDs that have
+// not run since before lastUpdate.
+// Return format is the same as getUidsCpuFreqTimes()
+std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
+getUidsUpdatedCpuFreqTimes(uint64_t *lastUpdate) {
     if (!gInitialized && !initGlobals()) return {};
     time_key_t key, prevKey;
     std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> map;
@@ -282,8 +307,14 @@
     std::vector<std::vector<uint64_t>> mapFormat;
     for (const auto &freqList : gPolicyFreqs) mapFormat.emplace_back(freqList.size(), 0);
 
+    uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0;
     std::vector<tis_val_t> vals(gNCpus);
     do {
+        if (lastUpdate) {
+            auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate);
+            if (!uidUpdated.has_value()) return {};
+            if (!*uidUpdated) continue;
+        }
         if (findMapEntry(gTisMapFd, &key, vals.data())) return {};
         if (map.find(key.uid) == map.end()) map.emplace(key.uid, mapFormat);
 
@@ -299,8 +330,9 @@
             }
         }
         prevKey = key;
-    } while (!getNextMapKey(gTisMapFd, &prevKey, &key));
+    } while (prevKey = key, !getNextMapKey(gTisMapFd, &prevKey, &key));
     if (errno != ENOENT) return {};
+    if (lastUpdate && newLastUpdate > *lastUpdate) *lastUpdate = newLastUpdate;
     return map;
 }
 
@@ -365,6 +397,15 @@
 // where ai is the ns spent running concurrently with tasks on i other cpus and pi_j is the ns spent
 // running on the ith cluster, concurrently with tasks on j other cpus in the same cluster.
 std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes() {
+    return getUidsUpdatedConcurrentTimes(nullptr);
+}
+
+// Retrieve the times in ns that each uid spent running concurrently with each possible number of
+// other tasks on each cluster (policy times) and overall (active times), excluding UIDs that have
+// not run since before lastUpdate.
+// Return format is the same as getUidsConcurrentTimes()
+std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsUpdatedConcurrentTimes(
+        uint64_t *lastUpdate) {
     if (!gInitialized && !initGlobals()) return {};
     time_key_t key, prevKey;
     std::unordered_map<uint32_t, concurrent_time_t> ret;
@@ -379,7 +420,13 @@
     std::vector<concurrent_val_t> vals(gNCpus);
     std::vector<uint64_t>::iterator activeBegin, activeEnd, policyBegin, policyEnd;
 
+    uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0;
     do {
+        if (lastUpdate) {
+            auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate);
+            if (!uidUpdated.has_value()) return {};
+            if (!*uidUpdated) continue;
+        }
         if (findMapEntry(gConcurrentMapFd, &key, vals.data())) return {};
         if (ret.find(key.uid) == ret.end()) ret.emplace(key.uid, retFormat);
 
@@ -405,8 +452,7 @@
                                std::plus<uint64_t>());
             }
         }
-        prevKey = key;
-    } while (!getNextMapKey(gConcurrentMapFd, &prevKey, &key));
+    } while (prevKey = key, !getNextMapKey(gConcurrentMapFd, &prevKey, &key));
     if (errno != ENOENT) return {};
     for (const auto &[key, value] : ret) {
         if (!verifyConcurrentTimes(value)) {
@@ -414,6 +460,7 @@
             if (val.has_value()) ret[key] = val.value();
         }
     }
+    if (lastUpdate && newLastUpdate > *lastUpdate) *lastUpdate = newLastUpdate;
     return ret;
 }
 
@@ -446,6 +493,8 @@
             return false;
         if (deleteMapEntry(gConcurrentMapFd, &key) && errno != ENOENT) return false;
     }
+
+    if (deleteMapEntry(gUidLastUpdateMapFd, &uid) && errno != ENOENT) return false;
     return true;
 }
 
diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h
index 49469d8..b7600f5 100644
--- a/libs/cputimeinstate/cputimeinstate.h
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -26,6 +26,8 @@
 std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid);
 std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
     getUidsCpuFreqTimes();
+std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
+    getUidsUpdatedCpuFreqTimes(uint64_t *lastUpdate);
 std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs();
 
 struct concurrent_time_t {
@@ -35,6 +37,8 @@
 
 std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry = true);
 std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes();
+std::optional<std::unordered_map<uint32_t, concurrent_time_t>>
+    getUidsUpdatedConcurrentTimes(uint64_t *lastUpdate);
 bool clearUidTimes(unsigned int uid);
 
 } // namespace bpf
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index 23d87fd..ea2a200 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -115,71 +115,169 @@
 }
 
 TEST(TimeInStateTest, AllUidTimeInState) {
-    vector<size_t> sizes;
-    auto map = getUidsCpuFreqTimes();
-    ASSERT_TRUE(map.has_value());
+    uint64_t zero = 0;
+    auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
+    for (const auto &map : maps) {
+        ASSERT_TRUE(map.has_value());
 
-    ASSERT_FALSE(map->empty());
+        ASSERT_FALSE(map->empty());
 
-    auto firstEntry = map->begin()->second;
-    for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size());
+        vector<size_t> sizes;
+        auto firstEntry = map->begin()->second;
+        for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size());
 
-    for (const auto &vec : *map) {
-        ASSERT_EQ(vec.second.size(), sizes.size());
-        for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]);
+        for (const auto &vec : *map) {
+            ASSERT_EQ(vec.second.size(), sizes.size());
+            for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]);
+        }
+    }
+}
+
+void TestCheckUpdate(const std::vector<std::vector<uint64_t>> &before,
+                     const std::vector<std::vector<uint64_t>> &after) {
+    ASSERT_EQ(before.size(), after.size());
+    uint64_t sumBefore = 0, sumAfter = 0;
+    for (size_t i = 0; i < before.size(); ++i) {
+        ASSERT_EQ(before[i].size(), after[i].size());
+        for (size_t j = 0; j < before[i].size(); ++j) {
+            // Times should never decrease
+            ASSERT_LE(before[i][j], after[i][j]);
+        }
+        sumBefore += std::accumulate(before[i].begin(), before[i].end(), (uint64_t)0);
+        sumAfter += std::accumulate(after[i].begin(), after[i].end(), (uint64_t)0);
+    }
+    ASSERT_LE(sumBefore, sumAfter);
+    ASSERT_LE(sumAfter - sumBefore, NSEC_PER_SEC);
+}
+
+TEST(TimeInStateTest, AllUidUpdatedTimeInState) {
+    uint64_t lastUpdate = 0;
+    auto map1 = getUidsUpdatedCpuFreqTimes(&lastUpdate);
+    ASSERT_TRUE(map1.has_value());
+    ASSERT_FALSE(map1->empty());
+    ASSERT_NE(lastUpdate, (uint64_t)0);
+    uint64_t oldLastUpdate = lastUpdate;
+
+    // Sleep briefly to trigger a context switch, ensuring we see at least one update.
+    struct timespec ts;
+    ts.tv_sec = 0;
+    ts.tv_nsec = 1000000;
+    nanosleep (&ts, NULL);
+
+    auto map2 = getUidsUpdatedCpuFreqTimes(&lastUpdate);
+    ASSERT_TRUE(map2.has_value());
+    ASSERT_FALSE(map2->empty());
+    ASSERT_NE(lastUpdate, oldLastUpdate);
+
+    bool someUidsExcluded = false;
+    for (const auto &[uid, v] : *map1) {
+        if (map2->find(uid) == map2->end()) {
+            someUidsExcluded = true;
+            break;
+        }
+    }
+    ASSERT_TRUE(someUidsExcluded);
+
+    for (const auto &[uid, newTimes] : *map2) {
+        ASSERT_NE(map1->find(uid), map1->end());
+        ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid], newTimes));
     }
 }
 
 TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) {
-    auto map = getUidsCpuFreqTimes();
-    ASSERT_TRUE(map.has_value());
-    ASSERT_FALSE(map->empty());
+    uint64_t zero = 0;
+    auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
+    for (const auto &map : maps) {
+        ASSERT_TRUE(map.has_value());
+        ASSERT_FALSE(map->empty());
 
-    for (const auto &kv : *map) {
-        uint32_t uid = kv.first;
-        auto times1 = kv.second;
-        auto times2 = getUidCpuFreqTimes(uid);
-        ASSERT_TRUE(times2.has_value());
+        for (const auto &kv : *map) {
+            uint32_t uid = kv.first;
+            auto times1 = kv.second;
+            auto times2 = getUidCpuFreqTimes(uid);
+            ASSERT_TRUE(times2.has_value());
 
-        ASSERT_EQ(times1.size(), times2->size());
-        for (uint32_t i = 0; i < times1.size(); ++i) {
-            ASSERT_EQ(times1[i].size(), (*times2)[i].size());
-            for (uint32_t j = 0; j < times1[i].size(); ++j) {
-                ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC);
+            ASSERT_EQ(times1.size(), times2->size());
+            for (uint32_t i = 0; i < times1.size(); ++i) {
+                ASSERT_EQ(times1[i].size(), (*times2)[i].size());
+                for (uint32_t j = 0; j < times1[i].size(); ++j) {
+                    ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC);
+                }
             }
         }
     }
 }
 
 TEST(TimeInStateTest, AllUidConcurrentTimes) {
-    auto map = getUidsConcurrentTimes();
-    ASSERT_TRUE(map.has_value());
-    ASSERT_FALSE(map->empty());
+    uint64_t zero = 0;
+    auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
+    for (const auto &map : maps) {
+        ASSERT_TRUE(map.has_value());
+        ASSERT_FALSE(map->empty());
 
-    auto firstEntry = map->begin()->second;
-    for (const auto &kv : *map) {
-        ASSERT_EQ(kv.second.active.size(), firstEntry.active.size());
-        ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size());
-        for (size_t i = 0; i < kv.second.policy.size(); ++i) {
-            ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size());
+        auto firstEntry = map->begin()->second;
+        for (const auto &kv : *map) {
+            ASSERT_EQ(kv.second.active.size(), firstEntry.active.size());
+            ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size());
+            for (size_t i = 0; i < kv.second.policy.size(); ++i) {
+                ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size());
+            }
         }
     }
 }
 
-TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) {
-    auto map = getUidsConcurrentTimes();
-    ASSERT_TRUE(map.has_value());
-    for (const auto &kv : *map) {
-        uint32_t uid = kv.first;
-        auto times1 = kv.second;
-        auto times2 = getUidConcurrentTimes(uid);
-        ASSERT_TRUE(times2.has_value());
-        for (uint32_t i = 0; i < times1.active.size(); ++i) {
-            ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC);
+TEST(TimeInStateTest, AllUidUpdatedConcurrentTimes) {
+    uint64_t lastUpdate = 0;
+    auto map1 = getUidsUpdatedConcurrentTimes(&lastUpdate);
+    ASSERT_TRUE(map1.has_value());
+    ASSERT_FALSE(map1->empty());
+    ASSERT_NE(lastUpdate, (uint64_t)0);
+
+    // Sleep briefly to trigger a context switch, ensuring we see at least one update.
+    struct timespec ts;
+    ts.tv_sec = 0;
+    ts.tv_nsec = 1000000;
+    nanosleep (&ts, NULL);
+
+    uint64_t oldLastUpdate = lastUpdate;
+    auto map2 = getUidsUpdatedConcurrentTimes(&lastUpdate);
+    ASSERT_TRUE(map2.has_value());
+    ASSERT_FALSE(map2->empty());
+    ASSERT_NE(lastUpdate, oldLastUpdate);
+
+    bool someUidsExcluded = false;
+    for (const auto &[uid, v] : *map1) {
+        if (map2->find(uid) == map2->end()) {
+            someUidsExcluded = true;
+            break;
         }
-        for (uint32_t i = 0; i < times1.policy.size(); ++i) {
-            for (uint32_t j = 0; j < times1.policy[i].size(); ++j) {
-                ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC);
+    }
+    ASSERT_TRUE(someUidsExcluded);
+
+    for (const auto &[uid, newTimes] : *map2) {
+        ASSERT_NE(map1->find(uid), map1->end());
+        ASSERT_NO_FATAL_FAILURE(TestCheckUpdate({(*map1)[uid].active},{newTimes.active}));
+        ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid].policy, newTimes.policy));
+    }
+}
+
+TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) {
+    uint64_t zero = 0;
+    auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
+    for (const auto &map : maps) {
+        ASSERT_TRUE(map.has_value());
+        for (const auto &kv : *map) {
+            uint32_t uid = kv.first;
+            auto times1 = kv.second;
+            auto times2 = getUidConcurrentTimes(uid);
+            ASSERT_TRUE(times2.has_value());
+            for (uint32_t i = 0; i < times1.active.size(); ++i) {
+                ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC);
+            }
+            for (uint32_t i = 0; i < times1.policy.size(); ++i) {
+                for (uint32_t j = 0; j < times1.policy[i].size(); ++j) {
+                    ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC);
+                }
             }
         }
     }
@@ -242,45 +340,51 @@
 }
 
 TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) {
-    auto map = getUidsCpuFreqTimes();
-    ASSERT_TRUE(map.has_value());
+    uint64_t zero = 0;
+    auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)};
+    for (const auto &map : maps) {
+        ASSERT_TRUE(map.has_value());
 
-    bool foundLargeValue = false;
-    for (const auto &kv : *map) {
-        for (const auto &timeVec : kv.second) {
-            for (const auto &time : timeVec) {
-                ASSERT_LE(time, NSEC_PER_YEAR);
-                if (time > UINT32_MAX) foundLargeValue = true;
+        bool foundLargeValue = false;
+        for (const auto &kv : *map) {
+            for (const auto &timeVec : kv.second) {
+                for (const auto &time : timeVec) {
+                    ASSERT_LE(time, NSEC_PER_YEAR);
+                    if (time > UINT32_MAX) foundLargeValue = true;
+                }
             }
         }
+        // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
+        // uint64_t as expected, we should have some times higher than that.
+        ASSERT_TRUE(foundLargeValue);
     }
-    // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
-    // uint64_t as expected, we should have some times higher than that.
-    ASSERT_TRUE(foundLargeValue);
 }
 
 TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) {
-    auto concurrentMap = getUidsConcurrentTimes();
-    ASSERT_TRUE(concurrentMap);
+    uint64_t zero = 0;
+    auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)};
+    for (const auto &concurrentMap : maps) {
+        ASSERT_TRUE(concurrentMap);
 
-    bool activeFoundLargeValue = false;
-    bool policyFoundLargeValue = false;
-    for (const auto &kv : *concurrentMap) {
-        for (const auto &time : kv.second.active) {
-            ASSERT_LE(time, NSEC_PER_YEAR);
-            if (time > UINT32_MAX) activeFoundLargeValue = true;
-        }
-        for (const auto &policyTimeVec : kv.second.policy) {
-            for (const auto &time : policyTimeVec) {
+        bool activeFoundLargeValue = false;
+        bool policyFoundLargeValue = false;
+        for (const auto &kv : *concurrentMap) {
+            for (const auto &time : kv.second.active) {
                 ASSERT_LE(time, NSEC_PER_YEAR);
-                if (time > UINT32_MAX) policyFoundLargeValue = true;
+                if (time > UINT32_MAX) activeFoundLargeValue = true;
+            }
+            for (const auto &policyTimeVec : kv.second.policy) {
+                for (const auto &time : policyTimeVec) {
+                    ASSERT_LE(time, NSEC_PER_YEAR);
+                    if (time > UINT32_MAX) policyFoundLargeValue = true;
+                }
             }
         }
+        // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
+        // uint64_t as expected, we should have some times higher than that.
+        ASSERT_TRUE(activeFoundLargeValue);
+        ASSERT_TRUE(policyFoundLargeValue);
     }
-    // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
-    // uint64_t as expected, we should have some times higher than that.
-    ASSERT_TRUE(activeFoundLargeValue);
-    ASSERT_TRUE(policyFoundLargeValue);
 }
 
 TEST(TimeInStateTest, AllUidTimesConsistent) {
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 4e8569f..603e7e5 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -734,12 +734,7 @@
     if (err) {
         return err;
     }
-    err = encodeInteger<int64_t>(static_cast<int32_t>(input.verticalSubsampling), output);
-    if (err) {
-        return err;
-    }
-
-    return encodeRect(input.crop, output);
+    return encodeInteger<int64_t>(static_cast<int32_t>(input.verticalSubsampling), output);
 }
 
 status_t decodePlaneLayout(InputHidlVec* input, PlaneLayout* output) {
@@ -780,12 +775,7 @@
     if (err) {
         return err;
     }
-    err = decodeInteger<int64_t>(input, &output->verticalSubsampling);
-    if (err) {
-        return err;
-    }
-
-    return decodeRect(input, &output->crop);
+    return decodeInteger<int64_t>(input, &output->verticalSubsampling);
 }
 
 status_t encodePlaneLayoutsHelper(const std::vector<PlaneLayout>& planeLayouts, OutputHidlVec* outOutputHidlVec) {
@@ -831,6 +821,49 @@
     output->clear();
 }
 
+status_t encodeCropHelper(const std::vector<Rect>& crops, OutputHidlVec* outOutputHidlVec) {
+    status_t err = encodeInteger<int64_t>(static_cast<int64_t>(crops.size()), outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+
+    for (const auto& crop : crops) {
+        err = encodeRect(crop, outOutputHidlVec);
+        if (err) {
+            return err;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t decodeCropHelper(InputHidlVec* inputHidlVec, std::vector<Rect>* outCrops) {
+    int64_t size = 0;
+    status_t err = decodeInteger<int64_t>(inputHidlVec, &size);
+    if (err) {
+        return err;
+    }
+    if (size < 0) {
+        return BAD_VALUE;
+    }
+
+    for (size_t i = 0; i < size; i++) {
+        outCrops->emplace_back();
+        err = decodeRect(inputHidlVec, &outCrops->back());
+        if (err) {
+            return err;
+        }
+    }
+    return NO_ERROR;
+}
+
+void clearCrop(std::vector<Rect>* output) {
+    if (!output) {
+        return;
+    }
+    output->clear();
+}
+
 status_t encodeSmpte2086Helper(const Smpte2086& smpte2086, OutputHidlVec* outOutputHidlVec) {
     status_t err = encodeXyColor(smpte2086.primaryRed, outOutputHidlVec);
     if (err) {
@@ -1043,6 +1076,14 @@
                   decodePlaneLayoutsHelper, clearPlaneLayouts);
 }
 
+status_t encodeCrop(const std::vector<Rect>& crop, hidl_vec<uint8_t>* outCrop) {
+    return encodeMetadata(MetadataType_Crop, crop, outCrop, encodeCropHelper);
+}
+
+status_t decodeCrop(const hidl_vec<uint8_t>& crop, std::vector<Rect>* outCrop) {
+    return decodeMetadata(MetadataType_Crop, crop, outCrop, decodeCropHelper, clearCrop);
+}
+
 status_t encodeDataspace(const Dataspace& dataspace, hidl_vec<uint8_t>* outDataspace) {
     return encodeMetadata(MetadataType_Dataspace, static_cast<int32_t>(dataspace), outDataspace,
                   encodeInteger);
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.cpp b/libs/gralloc/types/fuzzer/gralloctypes.cpp
index b5644be..dc22385 100644
--- a/libs/gralloc/types/fuzzer/gralloctypes.cpp
+++ b/libs/gralloc/types/fuzzer/gralloctypes.cpp
@@ -70,6 +70,7 @@
     GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeInterlaced),
     GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeChromaSiting),
     GRALLOCTYPES_DECODE(std::vector<aidl::android::hardware::graphics::common::PlaneLayout>, ::android::gralloc4::decodePlaneLayouts),
+    GRALLOCTYPES_DECODE(std::vector<aidl::android::hardware::graphics::common::Rect>, ::android::gralloc4::decodeCrop),
     GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::Dataspace, ::android::gralloc4::decodeDataspace),
     GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::BlendMode, ::android::gralloc4::decodeBlendMode),
     GRALLOCTYPES_DECODE(std::optional<aidl::android::hardware::graphics::common::Smpte2086>, ::android::gralloc4::decodeSmpte2086),
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index d855439..5ec4d0d 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -89,6 +89,24 @@
     return !(lhs == rhs);
 }
 
+inline bool operator==(const std::vector<aidl::android::hardware::graphics::common::Rect>& lhs,
+                const std::vector<aidl::android::hardware::graphics::common::Rect>& rhs) {
+    if (lhs.size() != rhs.size()) {
+        return false;
+    }
+    for (size_t i = 0; i < lhs.size(); i++) {
+        if (lhs[i] != rhs[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+inline bool operator!=(const std::vector<aidl::android::hardware::graphics::common::Rect>& lhs,
+                const std::vector<aidl::android::hardware::graphics::common::Rect>& rhs) {
+    return !(lhs == rhs);
+}
+
 inline bool operator==(const aidl::android::hardware::graphics::common::PlaneLayout& lhs,
                 const aidl::android::hardware::graphics::common::PlaneLayout& rhs) {
     if (lhs.offsetInBytes != rhs.offsetInBytes) {
@@ -115,9 +133,6 @@
     if (lhs.verticalSubsampling != rhs.verticalSubsampling) {
         return false;
     }
-    if (lhs.crop != rhs.crop) {
-        return false;
-    }
     if (lhs.components.size() != rhs.components.size()) {
         return false;
     }
@@ -286,6 +301,10 @@
         GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PLANE_LAYOUTS)
 };
 
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Crop = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::CROP)
+};
+
 static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Dataspace = {
         GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::DATASPACE)
 };
@@ -469,6 +488,9 @@
 status_t encodePlaneLayouts(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& planeLayouts, android::hardware::hidl_vec<uint8_t>* outPlaneLayouts);
 status_t decodePlaneLayouts(const android::hardware::hidl_vec<uint8_t>& planeLayouts, std::vector<aidl::android::hardware::graphics::common::PlaneLayout>* outPlaneLayouts);
 
+status_t encodeCrop(const std::vector<aidl::android::hardware::graphics::common::Rect>& crop, android::hardware::hidl_vec<uint8_t>* outCrop);
+status_t decodeCrop(const android::hardware::hidl_vec<uint8_t>& crop, std::vector<aidl::android::hardware::graphics::common::Rect>* outCrop);
+
 status_t encodeDataspace(const aidl::android::hardware::graphics::common::Dataspace& dataspace, android::hardware::hidl_vec<uint8_t>* outDataspace);
 status_t decodeDataspace(const android::hardware::hidl_vec<uint8_t>& dataspace, aidl::android::hardware::graphics::common::Dataspace* outDataspace);
 
diff --git a/libs/gralloc/types/tests/Gralloc4_test.cpp b/libs/gralloc/types/tests/Gralloc4_test.cpp
index dbe41f1..89cbf4a 100644
--- a/libs/gralloc/types/tests/Gralloc4_test.cpp
+++ b/libs/gralloc/types/tests/Gralloc4_test.cpp
@@ -321,10 +321,6 @@
     planeLayoutA.totalSizeInBytes = planeLayoutA.strideInBytes * height;
     planeLayoutA.horizontalSubsampling = 1;
     planeLayoutA.verticalSubsampling = 1;
-    planeLayoutA.crop.left = 0;
-    planeLayoutA.crop.top = 0;
-    planeLayoutA.crop.right = width;
-    planeLayoutA.crop.bottom = height;
 
     component.type = gralloc4::PlaneLayoutComponentType_A;
     component.offsetInBits = 0;
@@ -341,11 +337,6 @@
     planeLayoutRGB.totalSizeInBytes = planeLayoutRGB.strideInBytes * height;
     planeLayoutRGB.horizontalSubsampling = 1;
     planeLayoutRGB.verticalSubsampling = 1;
-    planeLayoutRGB.crop.left = 0;
-    planeLayoutRGB.crop.top = 0;
-    planeLayoutRGB.crop.right = width;
-    planeLayoutRGB.crop.bottom = height;
-
     component.type = gralloc4::PlaneLayoutComponentType_R;
     planeLayoutRGB.components.push_back(component);
     component.type = gralloc4::PlaneLayoutComponentType_G;
@@ -358,6 +349,33 @@
     ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(planeLayouts, gralloc4::encodePlaneLayouts, gralloc4::decodePlaneLayouts));
 }
 
+class Gralloc4TestCrop : public testing::Test { };
+
+TEST_F(Gralloc4TestCrop, Crop) {
+    std::vector<Rect> crops;
+    Rect crop1, crop2, crop3;
+
+    crop1.left = 0;
+    crop1.top = 0;
+    crop1.right = 64;
+    crop1.bottom = 64;
+    crops.push_back(crop1);
+
+    crop2.left = std::numeric_limits<int32_t>::min();
+    crop2.top = 0xFF;
+    crop2.right = std::numeric_limits<int32_t>::max();
+    crop2.bottom = 0xFFFF;
+    crops.push_back(crop2);
+
+    crop3.left = 0;
+    crop3.top = 0;
+    crop3.right = -1;
+    crop3.bottom = -1;
+    crops.push_back(crop3);
+
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(crops, gralloc4::encodeCrop, gralloc4::decodeCrop));
+}
+
 class Gralloc4TestDataspace : public testing::TestWithParam<Dataspace> { };
 
 INSTANTIATE_TEST_CASE_P(
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 40c044d..99d3856 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -55,7 +55,11 @@
         "DisplayEventReceiver.cpp",
         "GLConsumer.cpp",
         "GuiConfig.cpp",
+        "IConsumerListener.cpp",
         "IDisplayEventConnection.cpp",
+        "IGraphicBufferConsumer.cpp",
+        "IGraphicBufferProducer.cpp",
+        "IProducerListener.cpp",
         "IRegionSamplingListener.cpp",
         "ISurfaceComposer.cpp",
         "ISurfaceComposerClient.cpp",
@@ -63,22 +67,32 @@
         "LayerDebugInfo.cpp",
         "LayerMetadata.cpp",
         "LayerState.cpp",
+        "OccupancyTracker.cpp",
         "StreamSplitter.cpp",
         "Surface.cpp",
         "SurfaceControl.cpp",
         "SurfaceComposerClient.cpp",
         "SyncFeatures.cpp",
         "view/Surface.cpp",
+        "bufferqueue/1.0/B2HProducerListener.cpp",
+        "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
+        "bufferqueue/2.0/B2HProducerListener.cpp",
+        "bufferqueue/2.0/H2BGraphicBufferProducer.cpp",
     ],
 
     shared_libs: [
         "android.frameworks.bufferhub@1.0",
+        "libbinder",
         "libbufferhub",
         "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
         "libinput",
         "libpdx_default_transport",
     ],
 
+    export_shared_lib_headers: [
+        "libbinder",
+    ],
+
     // bufferhub is not used when building libgui for vendors
     target: {
         vendor: {
@@ -118,6 +132,7 @@
 
     cflags: [
         "-DNO_BUFFERHUB",
+        "-DNO_BINDER",
     ],
 
     defaults: ["libgui_bufferqueue-defaults"],
@@ -140,19 +155,11 @@
         "FrameTimestamps.cpp",
         "GLConsumerUtils.cpp",
         "HdrMetadata.cpp",
-        "IConsumerListener.cpp",
-        "IGraphicBufferConsumer.cpp",
-        "IGraphicBufferProducer.cpp",
-        "IProducerListener.cpp",
-        "OccupancyTracker.cpp",
-        "bufferqueue/1.0/B2HProducerListener.cpp",
+        "QueueBufferInputOutput.cpp",
         "bufferqueue/1.0/Conversion.cpp",
-        "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
         "bufferqueue/1.0/H2BProducerListener.cpp",
         "bufferqueue/1.0/WProducerListener.cpp",
         "bufferqueue/2.0/B2HGraphicBufferProducer.cpp",
-        "bufferqueue/2.0/B2HProducerListener.cpp",
-        "bufferqueue/2.0/H2BGraphicBufferProducer.cpp",
         "bufferqueue/2.0/H2BProducerListener.cpp",
         "bufferqueue/2.0/types.cpp",
     ],
@@ -189,7 +196,6 @@
         "android.hardware.graphics.common@1.2",
         "android.hidl.token@1.0-utils",
         "libbase",
-        "libbinder",
         "libcutils",
         "libEGL",
         "libGLESv2",
@@ -212,7 +218,6 @@
     ],
 
     export_shared_lib_headers: [
-        "libbinder",
         "libEGL",
         "libnativewindow",
         "libui",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index e2f5d31..f6493bf 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -174,7 +174,9 @@
     }
     mPendingReleaseItem.item = std::move(mSubmitted.front());
     mSubmitted.pop();
-    processNextBufferLocked();
+    if (mNextTransaction == nullptr) {
+        processNextBufferLocked();
+    }
     mCallbackCV.notify_all();
     decStrong((void*)transactionCallbackThunk);
 }
@@ -252,8 +254,13 @@
 
 void BLASTBufferQueue::onFrameAvailable(const BufferItem& /*item*/) {
     ATRACE_CALL();
-    std::lock_guard _lock{mMutex};
+    std::unique_lock _lock{mMutex};
 
+    if (mNextTransaction != nullptr) {
+        while (mNumFrameAvailable > 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS) {
+            mCallbackCV.wait(_lock);
+        }
+    }
     // add to shadow queue
     mNumFrameAvailable++;
     processNextBufferLocked();
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 9b74fef..4435265 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -66,6 +66,8 @@
           mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
           ##__VA_ARGS__)
 
+ConsumerListener::~ConsumerListener() = default;
+
 BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
     mCore(core),
     mSlots(core->mSlots),
@@ -291,8 +293,9 @@
 
         ATRACE_INT(mCore->mConsumerName.string(),
                 static_cast<int32_t>(mCore->mQueue.size()));
+#ifndef NO_BINDER
         mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
-
+#endif
         VALIDATE_CONSISTENCY();
     }
 
@@ -765,7 +768,12 @@
 status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush,
         std::vector<OccupancyTracker::Segment>* outHistory) {
     std::lock_guard<std::mutex> lock(mCore->mMutex);
+#ifndef NO_BINDER
     *outHistory = mCore->mOccupancyTracker.getSegmentHistory(forceFlush);
+#else
+    (void)forceFlush;
+    outHistory->clear();
+#endif
     return NO_ERROR;
 }
 
@@ -798,7 +806,7 @@
 
     bool denied = false;
     const uid_t uid = BufferQueueThreadState::getCallingUid();
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER)
     // permission check can't be done for vendors as vendors have no access to
     // the PermissionController. We need to do a runtime check as well, since
     // the system variant of libgui can be loaded in a vendor process. For eg:
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 6b11a54..9e86838 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -67,6 +67,7 @@
           ##__VA_ARGS__)
 
 static constexpr uint32_t BQ_LAYER_COUNT = 1;
+ProducerListener::~ProducerListener() = default;
 
 BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
         bool consumerIsSurfaceFlinger) :
@@ -1005,8 +1006,9 @@
 
         ATRACE_INT(mCore->mConsumerName.string(),
                 static_cast<int32_t>(mCore->mQueue.size()));
+#ifndef NO_BINDER
         mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
-
+#endif
         // Take a ticket for the callback functions
         callbackTicket = mNextCallbackTicket++;
 
@@ -1252,6 +1254,7 @@
             if (listener != nullptr) {
                 // Set up a death notification so that we can disconnect
                 // automatically if the remote producer dies
+#ifndef NO_BINDER
                 if (IInterface::asBinder(listener)->remoteBinder() != nullptr) {
                     status = IInterface::asBinder(listener)->linkToDeath(
                             static_cast<IBinder::DeathRecipient*>(this));
@@ -1261,6 +1264,7 @@
                     }
                     mCore->mLinkedToDeath = listener;
                 }
+#endif
                 mCore->mConnectedProducerListener = listener;
                 mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify();
             }
@@ -1329,6 +1333,7 @@
                 if (mCore->mConnectedApi == api) {
                     mCore->freeAllBuffersLocked();
 
+#ifndef NO_BINDER
                     // Remove our death notification callback if we have one
                     if (mCore->mLinkedToDeath != nullptr) {
                         sp<IBinder> token =
@@ -1338,6 +1343,7 @@
                         token->unlinkToDeath(
                                 static_cast<IBinder::DeathRecipient*>(this));
                     }
+#endif
                     mCore->mSharedBufferSlot =
                             BufferQueueCore::INVALID_BUFFER_SLOT;
                     mCore->mLinkedToDeath = nullptr;
diff --git a/libs/gui/BufferQueueThreadState.cpp b/libs/gui/BufferQueueThreadState.cpp
index c13030b..976c9b9 100644
--- a/libs/gui/BufferQueueThreadState.cpp
+++ b/libs/gui/BufferQueueThreadState.cpp
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
+#ifndef NO_BINDER
 #include <binder/IPCThreadState.h>
 #include <binderthreadstate/CallerUtils.h>
+#endif // NO_BINDER
 #include <hwbinder/IPCThreadState.h>
 #include <private/gui/BufferQueueThreadState.h>
 #include <unistd.h>
@@ -23,17 +25,25 @@
 namespace android {
 
 uid_t BufferQueueThreadState::getCallingUid() {
+#ifndef NO_BINDER
     if (getCurrentServingCall() == BinderCallType::HWBINDER) {
         return hardware::IPCThreadState::self()->getCallingUid();
     }
     return IPCThreadState::self()->getCallingUid();
+#else // NO_BINDER
+    return hardware::IPCThreadState::self()->getCallingUid();
+#endif // NO_BINDER
 }
 
 pid_t BufferQueueThreadState::getCallingPid() {
+#ifndef NO_BINDER
     if (getCurrentServingCall() == BinderCallType::HWBINDER) {
         return hardware::IPCThreadState::self()->getCallingPid();
     }
     return IPCThreadState::self()->getCallingPid();
+#else // NO_BINDER
+    return hardware::IPCThreadState::self()->getCallingPid();
+#endif // NO_BINDER
 }
 
 } // namespace android
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index 48cb4b9..f3bd90c 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -90,7 +90,6 @@
 // Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
 // clang warning -Wweak-vtables)
 BpConsumerListener::~BpConsumerListener() = default;
-ConsumerListener::~ConsumerListener() = default;
 
 IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener");
 
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 75876f2..7b5596e 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -1113,133 +1113,5 @@
     parcel.read(*this);
 }
 
-constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
-    return sizeof(timestamp) +
-            sizeof(isAutoTimestamp) +
-            sizeof(dataSpace) +
-            sizeof(crop) +
-            sizeof(scalingMode) +
-            sizeof(transform) +
-            sizeof(stickyTransform) +
-            sizeof(getFrameTimestamps);
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
-    return minFlattenedSize() +
-            fence->getFlattenedSize() +
-            surfaceDamage.getFlattenedSize() +
-            hdrMetadata.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
-    return fence->getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::flatten(
-        void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
-    if (size < getFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::write(buffer, size, timestamp);
-    FlattenableUtils::write(buffer, size, isAutoTimestamp);
-    FlattenableUtils::write(buffer, size, dataSpace);
-    FlattenableUtils::write(buffer, size, crop);
-    FlattenableUtils::write(buffer, size, scalingMode);
-    FlattenableUtils::write(buffer, size, transform);
-    FlattenableUtils::write(buffer, size, stickyTransform);
-    FlattenableUtils::write(buffer, size, getFrameTimestamps);
-
-    status_t result = fence->flatten(buffer, size, fds, count);
-    if (result != NO_ERROR) {
-        return result;
-    }
-    result = surfaceDamage.flatten(buffer, size);
-    if (result != NO_ERROR) {
-        return result;
-    }
-    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
-    return hdrMetadata.flatten(buffer, size);
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
-        void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
-    if (size < minFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::read(buffer, size, timestamp);
-    FlattenableUtils::read(buffer, size, isAutoTimestamp);
-    FlattenableUtils::read(buffer, size, dataSpace);
-    FlattenableUtils::read(buffer, size, crop);
-    FlattenableUtils::read(buffer, size, scalingMode);
-    FlattenableUtils::read(buffer, size, transform);
-    FlattenableUtils::read(buffer, size, stickyTransform);
-    FlattenableUtils::read(buffer, size, getFrameTimestamps);
-
-    fence = new Fence();
-    status_t result = fence->unflatten(buffer, size, fds, count);
-    if (result != NO_ERROR) {
-        return result;
-    }
-    result = surfaceDamage.unflatten(buffer, size);
-    if (result != NO_ERROR) {
-        return result;
-    }
-    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
-    return hdrMetadata.unflatten(buffer, size);
-}
-
-// ----------------------------------------------------------------------------
-constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
-    return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
-            sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount);
-}
-
-size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
-    return minFlattenedSize() + frameTimestamps.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
-    return frameTimestamps.getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
-        void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
-    if (size < getFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::write(buffer, size, width);
-    FlattenableUtils::write(buffer, size, height);
-    FlattenableUtils::write(buffer, size, transformHint);
-    FlattenableUtils::write(buffer, size, numPendingBuffers);
-    FlattenableUtils::write(buffer, size, nextFrameNumber);
-    FlattenableUtils::write(buffer, size, bufferReplaced);
-    FlattenableUtils::write(buffer, size, maxBufferCount);
-
-    return frameTimestamps.flatten(buffer, size, fds, count);
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
-        void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
-    if (size < minFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::read(buffer, size, width);
-    FlattenableUtils::read(buffer, size, height);
-    FlattenableUtils::read(buffer, size, transformHint);
-    FlattenableUtils::read(buffer, size, numPendingBuffers);
-    FlattenableUtils::read(buffer, size, nextFrameNumber);
-    FlattenableUtils::read(buffer, size, bufferReplaced);
-    FlattenableUtils::read(buffer, size, maxBufferCount);
-
-    return frameTimestamps.unflatten(buffer, size, fds, count);
-}
 
 }; // namespace android
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 808e336..5c81b9d 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -119,8 +119,6 @@
     return BBinder::onTransact(code, data, reply, flags);
 }
 
-ProducerListener::~ProducerListener() = default;
-
 DummyProducerListener::~DummyProducerListener() = default;
 
 bool BnProducerListener::needsReleaseNotify() {
diff --git a/libs/gui/QueueBufferInputOutput.cpp b/libs/gui/QueueBufferInputOutput.cpp
new file mode 100644
index 0000000..30f0ef6
--- /dev/null
+++ b/libs/gui/QueueBufferInputOutput.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2010 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 <inttypes.h>
+
+#define LOG_TAG "QueueBufferInputOutput"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
+    return sizeof(timestamp) +
+            sizeof(isAutoTimestamp) +
+            sizeof(dataSpace) +
+            sizeof(crop) +
+            sizeof(scalingMode) +
+            sizeof(transform) +
+            sizeof(stickyTransform) +
+            sizeof(getFrameTimestamps);
+}
+
+IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
+    parcel.read(*this);
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
+    return minFlattenedSize() +
+            fence->getFlattenedSize() +
+            surfaceDamage.getFlattenedSize() +
+            hdrMetadata.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
+    return fence->getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::flatten(
+        void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, timestamp);
+    FlattenableUtils::write(buffer, size, isAutoTimestamp);
+    FlattenableUtils::write(buffer, size, dataSpace);
+    FlattenableUtils::write(buffer, size, crop);
+    FlattenableUtils::write(buffer, size, scalingMode);
+    FlattenableUtils::write(buffer, size, transform);
+    FlattenableUtils::write(buffer, size, stickyTransform);
+    FlattenableUtils::write(buffer, size, getFrameTimestamps);
+
+    status_t result = fence->flatten(buffer, size, fds, count);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    result = surfaceDamage.flatten(buffer, size);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+    return hdrMetadata.flatten(buffer, size);
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
+        void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+    if (size < minFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, timestamp);
+    FlattenableUtils::read(buffer, size, isAutoTimestamp);
+    FlattenableUtils::read(buffer, size, dataSpace);
+    FlattenableUtils::read(buffer, size, crop);
+    FlattenableUtils::read(buffer, size, scalingMode);
+    FlattenableUtils::read(buffer, size, transform);
+    FlattenableUtils::read(buffer, size, stickyTransform);
+    FlattenableUtils::read(buffer, size, getFrameTimestamps);
+
+    fence = new Fence();
+    status_t result = fence->unflatten(buffer, size, fds, count);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    result = surfaceDamage.unflatten(buffer, size);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+    return hdrMetadata.unflatten(buffer, size);
+}
+
+////////////////////////////////////////////////////////////////////////
+constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
+    return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
+            sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount);
+}
+size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
+    return minFlattenedSize() + frameTimestamps.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
+    return frameTimestamps.getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
+        void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, width);
+    FlattenableUtils::write(buffer, size, height);
+    FlattenableUtils::write(buffer, size, transformHint);
+    FlattenableUtils::write(buffer, size, numPendingBuffers);
+    FlattenableUtils::write(buffer, size, nextFrameNumber);
+    FlattenableUtils::write(buffer, size, bufferReplaced);
+    FlattenableUtils::write(buffer, size, maxBufferCount);
+
+    return frameTimestamps.flatten(buffer, size, fds, count);
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
+        void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+    if (size < minFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, width);
+    FlattenableUtils::read(buffer, size, height);
+    FlattenableUtils::read(buffer, size, transformHint);
+    FlattenableUtils::read(buffer, size, numPendingBuffers);
+    FlattenableUtils::read(buffer, size, nextFrameNumber);
+    FlattenableUtils::read(buffer, size, bufferReplaced);
+    FlattenableUtils::read(buffer, size, maxBufferCount);
+
+    return frameTimestamps.unflatten(buffer, size, fds, count);
+}
+
+} // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 23532e7..278cc59 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1180,6 +1180,9 @@
         allocateBuffers();
         res = NO_ERROR;
         break;
+    case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER:
+        res = dispatchGetLastQueuedBuffer(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -1452,6 +1455,30 @@
     return NO_ERROR;
 }
 
+int Surface::dispatchGetLastQueuedBuffer(va_list args) {
+    AHardwareBuffer** buffer = va_arg(args, AHardwareBuffer**);
+    int* fence = va_arg(args, int*);
+    float* matrix = va_arg(args, float*);
+    sp<GraphicBuffer> graphicBuffer;
+    sp<Fence> spFence;
+
+    int result = mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, matrix);
+
+    if (graphicBuffer != nullptr) {
+        *buffer = reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get());
+        AHardwareBuffer_acquire(*buffer);
+    } else {
+        *buffer = nullptr;
+    }
+
+    if (spFence != nullptr) {
+        *fence = spFence->dup();
+    } else {
+        *fence = -1;
+    }
+    return result;
+}
+
 bool Surface::transformToDisplayInverse() {
     return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
diff --git a/libs/gui/bufferqueue/1.0/Conversion.cpp b/libs/gui/bufferqueue/1.0/Conversion.cpp
index 5cb3593..3e20a37 100644
--- a/libs/gui/bufferqueue/1.0/Conversion.cpp
+++ b/libs/gui/bufferqueue/1.0/Conversion.cpp
@@ -109,20 +109,6 @@
 }
 
 /**
- * \brief Convert `Return<void>` to `binder::Status`.
- *
- * \param[in] t The source `Return<void>`.
- * \return The corresponding `binder::Status`.
- */
-// convert: Return<void> -> ::android::binder::Status
-::android::binder::Status toBinderStatus(
-        Return<void> const& t) {
-    return ::android::binder::Status::fromExceptionCode(
-            toStatusT(t),
-            t.description().c_str());
-}
-
-/**
  * \brief Wrap `native_handle_t*` in `hidl_handle`.
  *
  * \param[in] nh The source `native_handle_t*`.
@@ -1337,57 +1323,6 @@
     return unflatten(&(t->surfaceDamage), buffer, size);
 }
 
-/**
- * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in
- * `HGraphicBufferProducer::QueueBufferInput`.
- *
- * \param[out] t The wrapper of type
- * `HGraphicBufferProducer::QueueBufferInput`.
- * \param[out] nh The underlying native handle for `t->fence`.
- * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`.
- *
- * If the return value is `true` and `t->fence` contains a valid file
- * descriptor, \p nh will be a newly created native handle holding that file
- * descriptor. \p nh needs to be deleted with `native_handle_delete()`
- * afterwards.
- */
-bool wrapAs(
-        HGraphicBufferProducer::QueueBufferInput* t,
-        native_handle_t** nh,
-        BGraphicBufferProducer::QueueBufferInput const& l) {
-
-    size_t const baseSize = l.getFlattenedSize();
-    std::unique_ptr<uint8_t[]> baseBuffer(
-            new (std::nothrow) uint8_t[baseSize]);
-    if (!baseBuffer) {
-        return false;
-    }
-
-    size_t const baseNumFds = l.getFdCount();
-    std::unique_ptr<int[]> baseFds(
-            new (std::nothrow) int[baseNumFds]);
-    if (!baseFds) {
-        return false;
-    }
-
-    void* buffer = static_cast<void*>(baseBuffer.get());
-    size_t size = baseSize;
-    int* fds = baseFds.get();
-    size_t numFds = baseNumFds;
-    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
-        return false;
-    }
-
-    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
-    size = baseSize;
-    int const* constFds = static_cast<int const*>(baseFds.get());
-    numFds = baseNumFds;
-    if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
-        return false;
-    }
-
-    return true;
-}
 
 /**
  * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to
diff --git a/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
index 2712f42..598c8de 100644
--- a/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
@@ -32,7 +32,11 @@
 using ::android::hardware::Return;
 
 H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base)
+#ifndef NO_BINDER
       : CBase{base} {
+#else
+      : mBase(base) {
+#endif
 }
 
 void H2BProducerListener::onBufferReleased() {
diff --git a/libs/gui/bufferqueue/1.0/WProducerListener.cpp b/libs/gui/bufferqueue/1.0/WProducerListener.cpp
index 78dc4e8..56b64b9 100644
--- a/libs/gui/bufferqueue/1.0/WProducerListener.cpp
+++ b/libs/gui/bufferqueue/1.0/WProducerListener.cpp
@@ -46,5 +46,7 @@
 bool LWProducerListener::needsReleaseNotify() {
     return static_cast<bool>(mBase->needsReleaseNotify());
 }
+void LWProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*slots*/) {
+}
 
 }  // namespace android
diff --git a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
index e891ec5..c76d771 100644
--- a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
@@ -249,6 +249,24 @@
     return {};
 }
 
+struct Obituary : public hardware::hidl_death_recipient {
+    wp<B2HGraphicBufferProducer> producer;
+    sp<HProducerListener> listener;
+    HConnectionType apiType;
+    Obituary(const wp<B2HGraphicBufferProducer>& p,
+             const sp<HProducerListener>& l,
+             HConnectionType t)
+        : producer(p), listener(l), apiType(t) {}
+
+    void serviceDied(uint64_t /* cookie */,
+            const wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
+        sp<B2HGraphicBufferProducer> dr = producer.promote();
+        if (dr != nullptr) {
+            (void)dr->disconnect(apiType);
+        }
+    }
+};
+
 Return<void> B2HGraphicBufferProducer::connect(
         sp<HProducerListener> const& hListener,
         HConnectionType hConnectionType,
@@ -270,6 +288,12 @@
                                &bOutput),
                 &hStatus) &&
             b2h(bOutput, &hOutput);
+#ifdef NO_BINDER
+    if (converted && hListener != nullptr) {
+        mObituary = new Obituary(this, hListener, hConnectionType);
+        hListener->linkToDeath(mObituary, 0);
+    }
+#endif // NO_BINDER
     _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, hOutput);
     return {};
 }
@@ -282,6 +306,12 @@
     }
     HStatus hStatus{};
     bool converted = b2h(mBase->disconnect(bConnectionType), &hStatus);
+#ifdef NO_BINDER
+    if (mObituary != nullptr) {
+        mObituary->listener->unlinkToDeath(mObituary);
+        mObituary.clear();
+    }
+#endif // NO_BINDER
     return {converted ? hStatus : HStatus::UNKNOWN_ERROR};
 }
 
diff --git a/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
index b81a357..b2bd117 100644
--- a/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
+++ b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
@@ -32,7 +32,11 @@
 using ::android::hardware::Return;
 
 H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base)
+#ifndef NO_BINDER
       : CBase{base} {
+#else
+      : mBase(base) {
+#endif
 }
 
 void H2BProducerListener::onBufferReleased() {
@@ -48,6 +52,9 @@
     return static_cast<bool>(mBase);
 }
 
+void H2BProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*slots*/) {
+}
+
 }  // namespace utils
 }  // namespace V2_0
 }  // namespace bufferqueue
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index 2dec663..cbace5b 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -22,10 +22,15 @@
 
 namespace android {
 
+class IBinder;
 struct BufferSlot;
 
+#ifndef NO_BINDER
 class BufferQueueProducer : public BnGraphicBufferProducer,
                             private IBinder::DeathRecipient {
+#else
+class BufferQueueProducer : public BnGraphicBufferProducer {
+#endif
 public:
     friend class BufferQueue; // Needed to access binderDied
 
diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h
index 046f6e1..0ab2399 100644
--- a/libs/gui/include/gui/IConsumerListener.h
+++ b/libs/gui/include/gui/IConsumerListener.h
@@ -92,6 +92,7 @@
                                           FrameEventHistoryDelta* /*outDelta*/) {}
 };
 
+#ifndef NO_BINDER
 class IConsumerListener : public ConsumerListener, public IInterface {
 public:
     DECLARE_META_INTERFACE(ConsumerListener)
@@ -105,4 +106,11 @@
                         uint32_t flags = 0) override;
 };
 
+#else
+class IConsumerListener : public ConsumerListener {
+};
+class BnConsumerListener : public IConsumerListener {
+};
+#endif
+
 } // namespace android
diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
index 54f77b4..56fe949 100644
--- a/libs/gui/include/gui/IGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/IGraphicBufferConsumer.h
@@ -35,10 +35,14 @@
 class GraphicBuffer;
 class IConsumerListener;
 class NativeHandle;
-
+#ifndef NO_BINDER
 class IGraphicBufferConsumer : public IInterface {
 public:
     DECLARE_META_INTERFACE(GraphicBufferConsumer)
+#else
+class IGraphicBufferConsumer : public RefBase {
+public:
+#endif
 
     enum {
         // Returned by releaseBuffer, after which the consumer must free any references to the
@@ -292,6 +296,7 @@
     }
 };
 
+#ifndef NO_BINDER
 class BnGraphicBufferConsumer : public SafeBnInterface<IGraphicBufferConsumer> {
 public:
     BnGraphicBufferConsumer()
@@ -300,5 +305,9 @@
     status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
                         uint32_t flags = 0) override;
 };
+#else
+class BnGraphicBufferConsumer : public IGraphicBufferConsumer {
+};
+#endif
 
 } // namespace android
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 680d64e..87989da 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -45,6 +45,13 @@
 class NativeHandle;
 class Surface;
 
+using HGraphicBufferProducerV1_0 =
+        ::android::hardware::graphics::bufferqueue::V1_0::
+        IGraphicBufferProducer;
+using HGraphicBufferProducerV2_0 =
+        ::android::hardware::graphics::bufferqueue::V2_0::
+        IGraphicBufferProducer;
+
 /*
  * This class defines the Binder IPC interface for the producer side of
  * a queue of graphics buffers.  It's used to send graphics data from one
@@ -59,20 +66,15 @@
  *
  * This class was previously called ISurfaceTexture.
  */
-class IGraphicBufferProducer : public IInterface
-{
-public:
-    using HGraphicBufferProducerV1_0 =
-            ::android::hardware::graphics::bufferqueue::V1_0::
-            IGraphicBufferProducer;
-    using HGraphicBufferProducerV2_0 =
-            ::android::hardware::graphics::bufferqueue::V2_0::
-            IGraphicBufferProducer;
-
+#ifndef NO_BINDER
+class IGraphicBufferProducer : public IInterface {
     DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer,
                                   HGraphicBufferProducerV1_0,
                                   HGraphicBufferProducerV2_0)
-
+#else
+class IGraphicBufferProducer : public RefBase {
+#endif
+public:
     enum {
         // A flag returned by dequeueBuffer when the client needs to call
         // requestBuffer immediately thereafter.
@@ -640,6 +642,7 @@
     // Sets the apps intended frame rate.
     virtual status_t setFrameRate(float frameRate);
 
+#ifndef NO_BINDER
     // Static method exports any IGraphicBufferProducer object to a parcel. It
     // handles null producer as well.
     static status_t exportToParcel(const sp<IGraphicBufferProducer>& producer,
@@ -657,10 +660,11 @@
     // it writes a strong binder object; for BufferHub, it writes a
     // ProducerQueueParcelable object.
     virtual status_t exportToParcel(Parcel* parcel);
+#endif
 };
 
 // ----------------------------------------------------------------------------
-
+#ifndef NO_BINDER
 class BnGraphicBufferProducer : public BnInterface<IGraphicBufferProducer>
 {
 public:
@@ -669,6 +673,10 @@
                                     Parcel* reply,
                                     uint32_t flags = 0);
 };
+#else
+class BnGraphicBufferProducer : public IGraphicBufferProducer {
+};
+#endif
 
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index 32a3690..0b1f4b5 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -51,6 +51,7 @@
     virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) = 0; // Asynchronous
 };
 
+#ifndef NO_BINDER
 class IProducerListener : public ProducerListener, public IInterface
 {
 public:
@@ -73,6 +74,12 @@
     virtual void onBuffersDiscarded(const std::vector<int32_t>& slots);
 };
 
+#else
+class IProducerListener : public ProducerListener {
+};
+class BnProducerListener : public IProducerListener {
+};
+#endif
 class DummyProducerListener : public BnProducerListener
 {
 public:
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 0139507..4a353fc 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -262,6 +262,7 @@
     int dispatchAddDequeueInterceptor(va_list args);
     int dispatchAddPerformInterceptor(va_list args);
     int dispatchAddQueueInterceptor(va_list args);
+    int dispatchGetLastQueuedBuffer(va_list args);
     bool transformToDisplayInverse();
 
 protected:
diff --git a/libs/gui/include/gui/bufferqueue/1.0/Conversion.h b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
index 627845c..811dcbe 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
@@ -25,8 +25,6 @@
 #include <hidl/MQDescriptor.h>
 #include <hidl/Status.h>
 
-#include <binder/Binder.h>
-#include <binder/Status.h>
 #include <ui/FenceTime.h>
 #include <cutils/native_handle.h>
 #include <gui/IGraphicBufferProducer.h>
@@ -127,15 +125,6 @@
  */
 
 /**
- * \brief Convert `Return<void>` to `binder::Status`.
- *
- * \param[in] t The source `Return<void>`.
- * \return The corresponding `binder::Status`.
- */
-// convert: Return<void> -> ::android::binder::Status
-::android::binder::Status toBinderStatus(Return<void> const& t);
-
-/**
  * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
  *
  * \param[in] t The source `Return<void>`.
diff --git a/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
index 211fdd5..cda5103 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
@@ -34,7 +34,12 @@
 using BProducerListener = ::android::IProducerListener;
 
 class H2BProducerListener
+#ifndef NO_BINDER
       : public H2BConverter<HProducerListener, BnProducerListener> {
+#else
+      : public BProducerListener {
+    sp<HProducerListener> mBase;
+#endif
 public:
     H2BProducerListener(sp<HProducerListener> const& base);
     virtual void onBufferReleased() override;
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
index 029dcc0..99ab085 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
@@ -49,7 +49,6 @@
 
 typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
 typedef ::android::IProducerListener BProducerListener;
-using ::android::BnGraphicBufferProducer;
 
 #ifndef LOG
 struct LOG_dummy {
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
index 51dff5b..197db26 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
@@ -20,7 +20,6 @@
 #include <hidl/MQDescriptor.h>
 #include <hidl/Status.h>
 
-#include <binder/IBinder.h>
 #include <gui/IProducerListener.h>
 
 #include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
@@ -55,6 +54,7 @@
     LWProducerListener(sp<HProducerListener> const& base);
     void onBufferReleased() override;
     bool needsReleaseNotify() override;
+    void onBuffersDiscarded(const std::vector<int32_t>& slots) override;
 };
 
 }  // namespace android
diff --git a/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
index 1c58167..16d054b 100644
--- a/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
@@ -45,6 +45,7 @@
 using ::android::hardware::hidl_vec;
 
 using ::android::hardware::graphics::common::V1_2::HardwareBuffer;
+struct Obituary;
 
 class B2HGraphicBufferProducer : public HGraphicBufferProducer {
 public:
@@ -108,6 +109,7 @@
 
 protected:
     sp<BGraphicBufferProducer> mBase;
+    sp<Obituary> mObituary;
 };
 
 
diff --git a/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
index 898920b..92650b7 100644
--- a/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
+++ b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
@@ -33,12 +33,20 @@
 
 using BProducerListener = ::android::IProducerListener;
 
+#ifndef NO_BINDER
 class H2BProducerListener
       : public H2BConverter<HProducerListener, BnProducerListener> {
+#else
+class H2BProducerListener
+      : public BProducerListener {
+    sp<HProducerListener> mBase;
+
+#endif
 public:
     H2BProducerListener(sp<HProducerListener> const& base);
     virtual void onBufferReleased() override;
     virtual bool needsReleaseNotify() override;
+    virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) override;
 };
 
 }  // namespace utils
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 85b0fd0..2a73dc0 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -57,6 +57,30 @@
     return "UNKNOWN";
 }
 
+VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
+    return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
+             event.getSource(), event.getDisplayId()},
+            event.getAction(),
+            event.getDownTime(),
+            event.getFlags() & VERIFIED_KEY_EVENT_FLAGS,
+            event.getKeyCode(),
+            event.getScanCode(),
+            event.getMetaState(),
+            event.getRepeatCount()};
+}
+
+VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event) {
+    return {{VerifiedInputEvent::Type::MOTION, event.getDeviceId(), event.getEventTime(),
+             event.getSource(), event.getDisplayId()},
+            event.getRawX(0),
+            event.getRawY(0),
+            event.getActionMasked(),
+            event.getDownTime(),
+            event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS,
+            event.getMetaState(),
+            event.getButtonState()};
+}
+
 void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
                             std::array<uint8_t, 32> hmac) {
     mDeviceId = deviceId;
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index c1c35e1..fb21d5e 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -10,12 +10,12 @@
         "LatencyStatistics_test.cpp",
         "TouchVideoFrame_test.cpp",
         "VelocityTracker_test.cpp",
+        "VerifiedInputEvent_test.cpp",
     ],
     cflags: [
         "-Wall",
         "-Wextra",
         "-Werror",
-        "-Wno-unused-variable",
     ],
     shared_libs: [
         "libinput",
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index dce1f29..d0f7618 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -46,7 +46,6 @@
 }
 
 TEST_F(PointerCoordsTest, AxisValues) {
-    float* valuePtr;
     PointerCoords coords;
     coords.clear();
 
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index d4bbf6c..885196f 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -38,6 +38,7 @@
     virtual void SetUp() {
         status_t result = InputChannel::openInputChannelPair("channel name",
                 serverChannel, clientChannel);
+        ASSERT_EQ(OK, result);
 
         mPublisher = new InputPublisher(serverChannel);
         mConsumer = new InputConsumer(clientChannel);
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index aa8a2d4..dd127fc 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -98,4 +98,34 @@
     static_assert(sizeof(InputMessage::Body::Focus) == 8);
 }
 
+// --- VerifiedInputEvent ---
+// Ensure that VerifiedInputEvent, VerifiedKeyEvent, VerifiedMotionEvent are packed.
+// We will treat them as byte collections when signing them. There should not be any uninitialized
+// data in-between fields. Otherwise, the padded data will affect the hmac value and verifications
+// will fail.
+
+void TestVerifiedEventSize() {
+    // VerifiedInputEvent
+    constexpr size_t VERIFIED_INPUT_EVENT_SIZE = sizeof(VerifiedInputEvent::type) +
+            sizeof(VerifiedInputEvent::deviceId) + sizeof(VerifiedInputEvent::eventTimeNanos) +
+            sizeof(VerifiedInputEvent::source) + sizeof(VerifiedInputEvent::displayId);
+    static_assert(sizeof(VerifiedInputEvent) == VERIFIED_INPUT_EVENT_SIZE);
+
+    // VerifiedKeyEvent
+    constexpr size_t VERIFIED_KEY_EVENT_SIZE = VERIFIED_INPUT_EVENT_SIZE +
+            sizeof(VerifiedKeyEvent::action) + sizeof(VerifiedKeyEvent::downTimeNanos) +
+            sizeof(VerifiedKeyEvent::flags) + sizeof(VerifiedKeyEvent::keyCode) +
+            sizeof(VerifiedKeyEvent::scanCode) + sizeof(VerifiedKeyEvent::metaState) +
+            sizeof(VerifiedKeyEvent::repeatCount);
+    static_assert(sizeof(VerifiedKeyEvent) == VERIFIED_KEY_EVENT_SIZE);
+
+    // VerifiedMotionEvent
+    constexpr size_t VERIFIED_MOTION_EVENT_SIZE = VERIFIED_INPUT_EVENT_SIZE +
+            sizeof(VerifiedMotionEvent::rawX) + sizeof(VerifiedMotionEvent::rawY) +
+            sizeof(VerifiedMotionEvent::actionMasked) + sizeof(VerifiedMotionEvent::downTimeNanos) +
+            sizeof(VerifiedMotionEvent::flags) + sizeof(VerifiedMotionEvent::metaState) +
+            sizeof(VerifiedMotionEvent::buttonState);
+    static_assert(sizeof(VerifiedMotionEvent) == VERIFIED_MOTION_EVENT_SIZE);
+}
+
 } // namespace android
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
new file mode 100644
index 0000000..a59dbe5
--- /dev/null
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <input/Input.h>
+
+namespace android {
+
+static KeyEvent getKeyEventWithFlags(int32_t flags) {
+    KeyEvent event;
+    event.initialize(2 /*deviceId*/, AINPUT_SOURCE_GAMEPAD, ADISPLAY_ID_DEFAULT, INVALID_HMAC,
+                     AKEY_EVENT_ACTION_DOWN, flags, AKEYCODE_BUTTON_X, 121 /*scanCode*/,
+                     AMETA_ALT_ON, 1 /*repeatCount*/, 1000 /*downTime*/, 2000 /*eventTime*/);
+    return event;
+}
+
+static MotionEvent getMotionEventWithFlags(int32_t flags) {
+    MotionEvent event;
+    constexpr size_t pointerCount = 1;
+    PointerProperties pointerProperties[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
+    for (size_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].clear();
+        pointerProperties[i].id = i;
+        pointerCoords[i].clear();
+    }
+
+    event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
+                     AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
+                     MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/,
+                     5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/,
+                     540 /*yCursorPosition*/, 100 /*downTime*/, 200 /*eventTime*/, pointerCount,
+                     pointerProperties, pointerCoords);
+    return event;
+}
+
+TEST(VerifiedKeyEventTest, ConvertKeyEventToVerifiedKeyEvent) {
+    KeyEvent event = getKeyEventWithFlags(0);
+    VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event);
+
+    ASSERT_EQ(VerifiedInputEvent::Type::KEY, verified.type);
+
+    ASSERT_EQ(event.getDeviceId(), verified.deviceId);
+    ASSERT_EQ(event.getEventTime(), verified.eventTimeNanos);
+    ASSERT_EQ(event.getSource(), verified.source);
+    ASSERT_EQ(event.getDisplayId(), verified.displayId);
+
+    ASSERT_EQ(event.getAction(), verified.action);
+    ASSERT_EQ(event.getDownTime(), verified.downTimeNanos);
+    ASSERT_EQ(event.getFlags() & VERIFIED_KEY_EVENT_FLAGS, verified.flags);
+    ASSERT_EQ(event.getKeyCode(), verified.keyCode);
+    ASSERT_EQ(event.getScanCode(), verified.scanCode);
+    ASSERT_EQ(event.getMetaState(), verified.metaState);
+    ASSERT_EQ(event.getRepeatCount(), verified.repeatCount);
+}
+
+TEST(VerifiedKeyEventTest, VerifiedKeyEventContainsOnlyVerifiedFlags) {
+    KeyEvent event = getKeyEventWithFlags(AKEY_EVENT_FLAG_CANCELED | AKEY_EVENT_FLAG_FALLBACK);
+    VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event);
+    ASSERT_EQ(AKEY_EVENT_FLAG_CANCELED, verified.flags);
+}
+
+TEST(VerifiedKeyEventTest, VerifiedKeyEventDoesNotContainUnverifiedFlags) {
+    KeyEvent event = getKeyEventWithFlags(AKEY_EVENT_FLAG_EDITOR_ACTION);
+    VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event);
+    ASSERT_EQ(0, verified.flags);
+}
+
+TEST(VerifiedMotionEventTest, ConvertMotionEventToVerifiedMotionEvent) {
+    MotionEvent event = getMotionEventWithFlags(0);
+    VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event);
+
+    ASSERT_EQ(VerifiedInputEvent::Type::MOTION, verified.type);
+
+    ASSERT_EQ(event.getDeviceId(), verified.deviceId);
+    ASSERT_EQ(event.getEventTime(), verified.eventTimeNanos);
+    ASSERT_EQ(event.getSource(), verified.source);
+    ASSERT_EQ(event.getDisplayId(), verified.displayId);
+
+    ASSERT_EQ(event.getRawX(0), verified.rawX);
+    ASSERT_EQ(event.getRawY(0), verified.rawY);
+    ASSERT_EQ(event.getAction(), verified.actionMasked);
+    ASSERT_EQ(event.getDownTime(), verified.downTimeNanos);
+    ASSERT_EQ(event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS, verified.flags);
+    ASSERT_EQ(event.getMetaState(), verified.metaState);
+    ASSERT_EQ(event.getButtonState(), verified.buttonState);
+}
+
+TEST(VerifiedMotionEventTest, VerifiedMotionEventContainsOnlyVerifiedFlags) {
+    MotionEvent event = getMotionEventWithFlags(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+                                                AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE);
+    VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event);
+    ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, verified.flags);
+}
+
+TEST(VerifiedMotionEventTest, VerifiedMotionEventDoesNotContainUnverifiedFlags) {
+    MotionEvent event = getMotionEventWithFlags(AMOTION_EVENT_FLAG_TAINTED);
+    VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event);
+    ASSERT_EQ(0, verified.flags);
+}
+
+} // namespace android
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index a1c9eb8..98b76fd 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -165,6 +165,14 @@
     return native_window_set_frame_rate(window, frameRate);
 }
 
+void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
+    if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
+        return;
+    }
+    window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
+}
+
+
 /**************************************************************************************************
  * vndk-stable
  **************************************************************************************************/
@@ -328,10 +336,6 @@
     return window->perform(window, NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR, interceptor, data);
 }
 
-void ANativeWindow_allocateBuffers(ANativeWindow* window) {
-    window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
-}
-
 int64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
     return query64(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID);
 }
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 262aee3..4b426c5 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -261,6 +261,17 @@
  */
 int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate) __INTRODUCED_IN(30);
 
+/**
+ * Provides a hint to the window that buffers should be preallocated ahead of
+ * time. Note that the window implementation is not guaranteed to preallocate
+ * any buffers, for instance if an implementation disallows allocation of new
+ * buffers, or if there is insufficient memory in the system to preallocate
+ * additional buffers
+ *
+ * Available since API level 30.
+ */
+void ANativeWindow_tryAllocateBuffers(ANativeWindow* window);
+
 #endif // __ANDROID_API__ >= 30
 
 #ifdef __cplusplus
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 121374b..f686147 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -253,6 +253,7 @@
     NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR         = 43,    /* private */
     NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR           = 44,    /* private */
     NATIVE_WINDOW_ALLOCATE_BUFFERS                = 45,    /* private */
+    NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER          = 46,    /* private */
     // clang-format on
 };
 
@@ -1018,4 +1019,34 @@
     return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate);
 }
 
+// ------------------------------------------------------------------------------------------------
+// Candidates for APEX visibility
+// These functions are planned to be made stable for APEX modules, but have not
+// yet been stabilized to a specific api version.
+// ------------------------------------------------------------------------------------------------
+
+/**
+ * Retrieves the last queued buffer for this window, along with the fence that
+ * fires when the buffer is ready to be read, and the 4x4 coordinate
+ * transform matrix that should be applied to the buffer's content. The
+ * transform matrix is represented in column-major order.
+ *
+ * If there was no buffer previously queued, then outBuffer will be NULL and
+ * the value of outFence will be -1.
+ *
+ * Note that if outBuffer is not NULL, then the caller will hold a reference
+ * onto the buffer. Accordingly, the caller must call AHardwareBuffer_release
+ * when the buffer is no longer needed so that the system may reclaim the
+ * buffer.
+ *
+ * \return NO_ERROR on success.
+ * \return NO_MEMORY if there was insufficient memory.
+ */
+static inline int ANativeWindow_getLastQueuedBuffer(ANativeWindow* window,
+                                                    AHardwareBuffer** outBuffer, int* outFence,
+                                                    float outTransformMatrix[16]) {
+    return window->perform(window, NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER, outBuffer, outFence,
+                           outTransformMatrix);
+}
+
 __END_DECLS
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index e0e20c3..154eb8e 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -17,7 +17,6 @@
     ANativeWindow_OemStorageGet; # llndk
     ANativeWindow_OemStorageSet; # llndk
     ANativeWindow_acquire;
-    ANativeWindow_allocateBuffers; # apex # introduced=30
     ANativeWindow_cancelBuffer; # llndk
     ANativeWindow_dequeueBuffer; # llndk
     ANativeWindow_getBuffersDataSpace; # introduced=28
@@ -51,6 +50,7 @@
     ANativeWindow_setSwapInterval; # llndk
     ANativeWindow_setFrameRate; # introduced=30
     ANativeWindow_setUsage; # llndk
+    ANativeWindow_tryAllocateBuffers; # introduced=30
     ANativeWindow_unlockAndPost;
   local:
     *;
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index e523836..e11b59f 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -984,18 +984,19 @@
     }
 
     std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
-    // Let's find the topmost layer requesting background blur (if any.)
-    // Blurs in multiple layers are not supported, given the cost of the shader.
-    const LayerSettings* blurLayer = nullptr;
+    // Gathering layers that requested blur, we'll need them to decide when to render to an
+    // offscreen buffer, and when to render to the native buffer.
+    std::deque<const LayerSettings*> blurLayers;
     if (CC_LIKELY(mBlurFilter != nullptr)) {
-        for (auto const layer : layers) {
+        for (auto layer : layers) {
             if (layer->backgroundBlurRadius > 0) {
-                blurLayer = layer;
+                blurLayers.push_back(layer);
             }
         }
     }
+    const auto blurLayersSize = blurLayers.size();
 
-    if (blurLayer == nullptr) {
+    if (blurLayersSize == 0) {
         fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache);
         if (fbo->getStatus() != NO_ERROR) {
             ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
@@ -1006,7 +1007,8 @@
         setViewportAndProjection(display.physicalDisplay, display.clip);
     } else {
         setViewportAndProjection(display.physicalDisplay, display.clip);
-        auto status = mBlurFilter->setAsDrawTarget(display, blurLayer->backgroundBlurRadius);
+        auto status =
+                mBlurFilter->setAsDrawTarget(display, blurLayers.front()->backgroundBlurRadius);
         if (status != NO_ERROR) {
             ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
                   buffer->handle);
@@ -1039,7 +1041,9 @@
                         .setCropCoords(2 /* size */)
                         .build();
     for (auto const layer : layers) {
-        if (blurLayer == layer) {
+        if (blurLayers.size() > 0 && blurLayers.front() == layer) {
+            blurLayers.pop_front();
+
             auto status = mBlurFilter->prepare();
             if (status != NO_ERROR) {
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
@@ -1048,18 +1052,26 @@
                 return status;
             }
 
-            fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
-                                                                  useFramebufferCache);
-            status = fbo->getStatus();
+            if (blurLayers.size() == 0) {
+                // Done blurring, time to bind the native FBO and render our blur onto it.
+                fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
+                                                                      useFramebufferCache);
+                status = fbo->getStatus();
+                setViewportAndProjection(display.physicalDisplay, display.clip);
+            } else {
+                // There's still something else to blur, so let's keep rendering to our FBO
+                // instead of to the display.
+                status = mBlurFilter->setAsDrawTarget(display,
+                                                      blurLayers.front()->backgroundBlurRadius);
+            }
             if (status != NO_ERROR) {
                 ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
                       buffer->handle);
                 checkErrors("Can't bind native framebuffer");
                 return status;
             }
-            setViewportAndProjection(display.physicalDisplay, display.clip);
 
-            status = mBlurFilter->render();
+            status = mBlurFilter->render(blurLayersSize > 1);
             if (status != NO_ERROR) {
                 ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
                       buffer->handle);
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
index f8b0a7a..b1a84bd 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -96,13 +96,17 @@
     mEngine.checkErrors("Drawing blur mesh");
 }
 
-status_t BlurFilter::render() {
+status_t BlurFilter::render(bool multiPass) {
     ATRACE_NAME("BlurFilter::render");
 
     // Now let's scale our blur up. It will be interpolated with the larger composited
     // texture for the first frames, to hide downscaling artifacts.
     GLfloat mix = fmin(1.0, mRadius / kMaxCrossFadeRadius);
-    if (mix >= 1) {
+
+    // When doing multiple passes, we cannot try to read mCompositionFbo, given that we'll
+    // be writing onto it. Let's disable the crossfade, otherwise we'd need 1 extra frame buffer,
+    // as large as the screen size.
+    if (mix >= 1 || multiPass) {
         mBlurredFbo.bindAsReadBuffer();
         glBlitFramebuffer(0, 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(), 0, 0,
                           mDisplayWidth, mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
index 67b3895..52dc8aa 100644
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -45,7 +45,7 @@
     // Execute blur passes, rendering to offscreen texture.
     virtual status_t prepare() = 0;
     // Render blur to the bound framebuffer (screen).
-    status_t render();
+    status_t render(bool multiPass);
 
 protected:
     uint32_t mRadius;
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 308e93a..5c80d55 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -40,6 +40,7 @@
         "libinputreporter",
         "libinputreader",
         "libbinder",
+        "libcrypto",
         "libcutils",
         "libhidlbase",
         "libinput",
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index a556aad..3f956a8 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -28,6 +28,7 @@
     ],
     shared_libs: [
         "libbase",
+        "libcrypto",
         "libcutils",
         "libinput",
         "libinputreporter",
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index f2b95e7..75bc0aa 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -60,7 +60,10 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/stringprintf.h>
 #include <binder/Binder.h>
+#include <input/InputDevice.h>
 #include <log/log.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
 #include <powermanager/PowerManager.h>
 #include <utils/Trace.h>
 
@@ -325,6 +328,55 @@
     return dispatchEntry;
 }
 
+static std::array<uint8_t, 128> getRandomKey() {
+    std::array<uint8_t, 128> key;
+    if (RAND_bytes(key.data(), key.size()) != 1) {
+        LOG_ALWAYS_FATAL("Can't generate HMAC key");
+    }
+    return key;
+}
+
+// --- HmacKeyManager ---
+
+HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {}
+
+std::array<uint8_t, 32> HmacKeyManager::sign(const VerifiedInputEvent& event) const {
+    size_t size;
+    switch (event.type) {
+        case VerifiedInputEvent::Type::KEY: {
+            size = sizeof(VerifiedKeyEvent);
+            break;
+        }
+        case VerifiedInputEvent::Type::MOTION: {
+            size = sizeof(VerifiedMotionEvent);
+            break;
+        }
+    }
+    std::vector<uint8_t> data;
+    const uint8_t* start = reinterpret_cast<const uint8_t*>(&event);
+    data.assign(start, start + size);
+    return sign(data);
+}
+
+std::array<uint8_t, 32> HmacKeyManager::sign(const std::vector<uint8_t>& data) const {
+    // SHA256 always generates 32-bytes result
+    std::array<uint8_t, 32> hash;
+    unsigned int hashLen = 0;
+    uint8_t* result = HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data.data(), data.size(),
+                           hash.data(), &hashLen);
+    if (result == nullptr) {
+        ALOGE("Could not sign the data using HMAC");
+        return INVALID_HMAC;
+    }
+
+    if (hashLen != hash.size()) {
+        ALOGE("HMAC-SHA256 has unexpected length");
+        return INVALID_HMAC;
+    }
+
+    return hash;
+}
+
 // --- InputDispatcher ---
 
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -3153,22 +3205,22 @@
     std::queue<EventEntry*> injectedEntries;
     switch (event->getType()) {
         case AINPUT_EVENT_TYPE_KEY: {
-            KeyEvent keyEvent;
-            keyEvent.initialize(*static_cast<const KeyEvent*>(event));
-            int32_t action = keyEvent.getAction();
+            const KeyEvent& incomingKey = static_cast<const KeyEvent&>(*event);
+            int32_t action = incomingKey.getAction();
             if (!validateKeyEvent(action)) {
                 return INPUT_EVENT_INJECTION_FAILED;
             }
 
-            int32_t flags = keyEvent.getFlags();
-            int32_t keyCode = keyEvent.getKeyCode();
-            int32_t metaState = keyEvent.getMetaState();
-            accelerateMetaShortcuts(keyEvent.getDeviceId(), action,
+            int32_t flags = incomingKey.getFlags();
+            int32_t keyCode = incomingKey.getKeyCode();
+            int32_t metaState = incomingKey.getMetaState();
+            accelerateMetaShortcuts(VIRTUAL_KEYBOARD_ID, action,
                                     /*byref*/ keyCode, /*byref*/ metaState);
-            keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(),
-                                keyEvent.getDisplayId(), INVALID_HMAC, action, flags, keyCode,
-                                keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(),
-                                keyEvent.getDownTime(), keyEvent.getEventTime());
+            KeyEvent keyEvent;
+            keyEvent.initialize(VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
+                                incomingKey.getDisplayId(), INVALID_HMAC, action, flags, keyCode,
+                                incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(),
+                                incomingKey.getDownTime(), incomingKey.getEventTime());
 
             if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) {
                 policyFlags |= POLICY_FLAG_VIRTUAL;
@@ -3186,11 +3238,10 @@
             mLock.lock();
             KeyEntry* injectedEntry =
                     new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(),
-                                 keyEvent.getDeviceId(), keyEvent.getSource(),
-                                 keyEvent.getDisplayId(), policyFlags, action, flags,
-                                 keyEvent.getKeyCode(), keyEvent.getScanCode(),
-                                 keyEvent.getMetaState(), keyEvent.getRepeatCount(),
-                                 keyEvent.getDownTime());
+                                 VIRTUAL_KEYBOARD_ID, keyEvent.getSource(), keyEvent.getDisplayId(),
+                                 policyFlags, action, flags, keyEvent.getKeyCode(),
+                                 keyEvent.getScanCode(), keyEvent.getMetaState(),
+                                 keyEvent.getRepeatCount(), keyEvent.getDownTime());
             injectedEntries.push(injectedEntry);
             break;
         }
@@ -3221,7 +3272,7 @@
             const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
             MotionEntry* injectedEntry =
                     new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
-                                    motionEvent->getDeviceId(), motionEvent->getSource(),
+                                    VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
                                     motionEvent->getDisplayId(), policyFlags, action, actionButton,
                                     motionEvent->getFlags(), motionEvent->getMetaState(),
                                     motionEvent->getButtonState(), motionEvent->getClassification(),
@@ -3238,7 +3289,7 @@
                 samplePointerCoords += pointerCount;
                 MotionEntry* nextInjectedEntry =
                         new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
-                                        motionEvent->getDeviceId(), motionEvent->getSource(),
+                                        VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
                                         motionEvent->getDisplayId(), policyFlags, action,
                                         actionButton, motionEvent->getFlags(),
                                         motionEvent->getMetaState(), motionEvent->getButtonState(),
@@ -3340,6 +3391,10 @@
     return injectionResult;
 }
 
+std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const InputEvent& event) {
+    return nullptr;
+}
+
 bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) {
     return injectorUid == 0 ||
             mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index d2aea80..ded59a5 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -56,6 +56,16 @@
 
 class Connection;
 
+class HmacKeyManager {
+public:
+    HmacKeyManager();
+    std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
+
+private:
+    std::array<uint8_t, 32> sign(const std::vector<uint8_t>& data) const;
+    const std::array<uint8_t, 128> mHmacKey;
+};
+
 /* Dispatches events to input targets.  Some functions of the input dispatcher, such as
  * identifying input targets, are controlled by a separate policy object.
  *
@@ -96,6 +106,8 @@
                                      int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
                                      uint32_t policyFlags) override;
 
+    virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
+
     virtual void setInputWindows(
             const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId,
             const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) override;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 3424f4c..6e98676 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -92,6 +92,13 @@
                                      int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
                                      uint32_t policyFlags) = 0;
 
+    /*
+     * Check whether InputEvent actually happened by checking the signature of the event.
+     *
+     * Return nullptr if the event cannot be verified.
+     */
+    virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) = 0;
+
     /* Sets the list of input windows.
      *
      * This method may be called on any thread (usually by the input manager).
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 7fed61f..1c08ab1 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -18,7 +18,16 @@
 
 #include "InputDevice.h"
 
-#include "InputMapper.h"
+#include "CursorInputMapper.h"
+#include "ExternalStylusInputMapper.h"
+#include "InputReaderContext.h"
+#include "JoystickInputMapper.h"
+#include "KeyboardInputMapper.h"
+#include "MultiTouchInputMapper.h"
+#include "RotaryEncoderInputMapper.h"
+#include "SingleTouchInputMapper.h"
+#include "SwitchInputMapper.h"
+#include "VibratorInputMapper.h"
 
 namespace android {
 
@@ -36,13 +45,7 @@
         mHasMic(false),
         mDropUntilNextSync(false) {}
 
-InputDevice::~InputDevice() {
-    size_t numMappers = mMappers.size();
-    for (size_t i = 0; i < numMappers; i++) {
-        delete mMappers[i];
-    }
-    mMappers.clear();
-}
+InputDevice::~InputDevice() {}
 
 bool InputDevice::isEnabled() {
     return getEventHub()->isDeviceEnabled(mId);
@@ -110,15 +113,80 @@
         }
     }
 
-    size_t numMappers = mMappers.size();
-    for (size_t i = 0; i < numMappers; i++) {
-        InputMapper* mapper = mMappers[i];
-        mapper->dump(dump);
-    }
+    for_each_mapper([&dump](InputMapper& mapper) { mapper.dump(dump); });
 }
 
-void InputDevice::addMapper(InputMapper* mapper) {
-    mMappers.push_back(mapper);
+void InputDevice::populateMappers() {
+    uint32_t classes = mClasses;
+    std::vector<std::unique_ptr<InputMapper>>& mappers = mMappers;
+
+    // External devices.
+    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
+        setExternal(true);
+    }
+
+    // Devices with mics.
+    if (classes & INPUT_DEVICE_CLASS_MIC) {
+        setMic(true);
+    }
+
+    // Switch-like devices.
+    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
+        mappers.push_back(std::make_unique<SwitchInputMapper>(this));
+    }
+
+    // Scroll wheel-like devices.
+    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
+        mappers.push_back(std::make_unique<RotaryEncoderInputMapper>(this));
+    }
+
+    // Vibrator-like devices.
+    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
+        mappers.push_back(std::make_unique<VibratorInputMapper>(this));
+    }
+
+    // Keyboard-like devices.
+    uint32_t keyboardSource = 0;
+    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
+    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
+    }
+    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
+        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
+    }
+    if (classes & INPUT_DEVICE_CLASS_DPAD) {
+        keyboardSource |= AINPUT_SOURCE_DPAD;
+    }
+    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
+    }
+
+    if (keyboardSource != 0) {
+        mappers.push_back(
+                std::make_unique<KeyboardInputMapper>(this, keyboardSource, keyboardType));
+    }
+
+    // Cursor-like devices.
+    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
+        mappers.push_back(std::make_unique<CursorInputMapper>(this));
+    }
+
+    // Touchscreens and touchpad devices.
+    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
+        mappers.push_back(std::make_unique<MultiTouchInputMapper>(this));
+    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
+        mappers.push_back(std::make_unique<SingleTouchInputMapper>(this));
+    }
+
+    // Joystick-like devices.
+    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
+        mappers.push_back(std::make_unique<JoystickInputMapper>(this));
+    }
+
+    // External stylus-like devices.
+    if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+        mappers.push_back(std::make_unique<ExternalStylusInputMapper>(this));
+    }
 }
 
 void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
@@ -193,10 +261,10 @@
             }
         }
 
-        for (InputMapper* mapper : mMappers) {
-            mapper->configure(when, config, changes);
-            mSources |= mapper->getSources();
-        }
+        for_each_mapper([this, when, config, changes](InputMapper& mapper) {
+            mapper.configure(when, config, changes);
+            mSources |= mapper.getSources();
+        });
 
         // If a device is just plugged but it might be disabled, we need to update some info like
         // axis range of touch from each InputMapper first, then disable it.
@@ -207,9 +275,7 @@
 }
 
 void InputDevice::reset(nsecs_t when) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->reset(when);
-    }
+    for_each_mapper([when](InputMapper& mapper) { mapper.reset(when); });
 
     mContext->updateGlobalMetaState();
 
@@ -244,32 +310,25 @@
             mDropUntilNextSync = true;
             reset(rawEvent->when);
         } else {
-            for (InputMapper* mapper : mMappers) {
-                mapper->process(rawEvent);
-            }
+            for_each_mapper([rawEvent](InputMapper& mapper) { mapper.process(rawEvent); });
         }
         --count;
     }
 }
 
 void InputDevice::timeoutExpired(nsecs_t when) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->timeoutExpired(when);
-    }
+    for_each_mapper([when](InputMapper& mapper) { mapper.timeoutExpired(when); });
 }
 
 void InputDevice::updateExternalStylusState(const StylusState& state) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->updateExternalStylusState(state);
-    }
+    for_each_mapper([state](InputMapper& mapper) { mapper.updateExternalStylusState(state); });
 }
 
 void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
     outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal,
                               mHasMic);
-    for (InputMapper* mapper : mMappers) {
-        mapper->populateDeviceInfo(outDeviceInfo);
-    }
+    for_each_mapper(
+            [outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); });
 }
 
 int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
@@ -286,11 +345,12 @@
 
 int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
     int32_t result = AKEY_STATE_UNKNOWN;
-    for (InputMapper* mapper : mMappers) {
-        if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+    for (auto& mapperPtr : mMappers) {
+        InputMapper& mapper = *mapperPtr;
+        if (sourcesMatchMask(mapper.getSources(), sourceMask)) {
             // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
             // value.  Otherwise, return AKEY_STATE_UP as long as one mapper reports it.
-            int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code);
+            int32_t currentResult = (mapper.*getStateFunc)(sourceMask, code);
             if (currentResult >= AKEY_STATE_DOWN) {
                 return currentResult;
             } else if (currentResult == AKEY_STATE_UP) {
@@ -304,51 +364,41 @@
 bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
                                         const int32_t* keyCodes, uint8_t* outFlags) {
     bool result = false;
-    for (InputMapper* mapper : mMappers) {
-        if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
-            result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
+    for_each_mapper([&result, sourceMask, numCodes, keyCodes, outFlags](InputMapper& mapper) {
+        if (sourcesMatchMask(mapper.getSources(), sourceMask)) {
+            result |= mapper.markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
         }
-    }
+    });
     return result;
 }
 
 void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
                           int32_t token) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->vibrate(pattern, patternSize, repeat, token);
-    }
+    for_each_mapper([pattern, patternSize, repeat, token](InputMapper& mapper) {
+        mapper.vibrate(pattern, patternSize, repeat, token);
+    });
 }
 
 void InputDevice::cancelVibrate(int32_t token) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->cancelVibrate(token);
-    }
+    for_each_mapper([token](InputMapper& mapper) { mapper.cancelVibrate(token); });
 }
 
 void InputDevice::cancelTouch(nsecs_t when) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->cancelTouch(when);
-    }
+    for_each_mapper([when](InputMapper& mapper) { mapper.cancelTouch(when); });
 }
 
 int32_t InputDevice::getMetaState() {
     int32_t result = 0;
-    for (InputMapper* mapper : mMappers) {
-        result |= mapper->getMetaState();
-    }
+    for_each_mapper([&result](InputMapper& mapper) { result |= mapper.getMetaState(); });
     return result;
 }
 
 void InputDevice::updateMetaState(int32_t keyCode) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->updateMetaState(keyCode);
-    }
+    for_each_mapper([keyCode](InputMapper& mapper) { mapper.updateMetaState(keyCode); });
 }
 
 void InputDevice::fadePointer() {
-    for (InputMapper* mapper : mMappers) {
-        mapper->fadePointer();
-    }
+    for_each_mapper([](InputMapper& mapper) { mapper.fadePointer(); });
 }
 
 void InputDevice::bumpGeneration() {
@@ -367,14 +417,8 @@
     }
 
     // No associated display port, check if some InputMapper is associated.
-    for (InputMapper* mapper : mMappers) {
-        std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplayId();
-        if (associatedDisplayId) {
-            return associatedDisplayId;
-        }
-    }
-
-    return std::nullopt;
+    return first_in_mappers<int32_t>(
+            [](InputMapper& mapper) { return mapper.getAssociatedDisplayId(); });
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 2023c6e..010729a 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -18,33 +18,22 @@
 
 #include "InputReader.h"
 
-#include "CursorInputMapper.h"
-#include "ExternalStylusInputMapper.h"
-#include "InputReaderContext.h"
-#include "JoystickInputMapper.h"
-#include "KeyboardInputMapper.h"
-#include "MultiTouchInputMapper.h"
-#include "RotaryEncoderInputMapper.h"
-#include "SingleTouchInputMapper.h"
-#include "SwitchInputMapper.h"
-#include "VibratorInputMapper.h"
-
+#include <android-base/stringprintf.h>
 #include <errno.h>
+#include <input/Keyboard.h>
+#include <input/VirtualKeyMap.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <log/log.h>
 #include <math.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <unistd.h>
-
-#include <log/log.h>
 #include <utils/Errors.h>
-
-#include <android-base/stringprintf.h>
-#include <input/Keyboard.h>
-#include <input/VirtualKeyMap.h>
 #include <utils/Thread.h>
 
+#include "InputDevice.h"
+
 using android::base::StringPrintf;
 
 namespace android {
@@ -261,74 +250,7 @@
                                              uint32_t classes) {
     InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
                                           controllerNumber, identifier, classes);
-
-    // External devices.
-    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
-        device->setExternal(true);
-    }
-
-    // Devices with mics.
-    if (classes & INPUT_DEVICE_CLASS_MIC) {
-        device->setMic(true);
-    }
-
-    // Switch-like devices.
-    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
-        device->addMapper(new SwitchInputMapper(device));
-    }
-
-    // Scroll wheel-like devices.
-    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
-        device->addMapper(new RotaryEncoderInputMapper(device));
-    }
-
-    // Vibrator-like devices.
-    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
-        device->addMapper(new VibratorInputMapper(device));
-    }
-
-    // Keyboard-like devices.
-    uint32_t keyboardSource = 0;
-    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
-    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
-        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
-    }
-    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
-        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
-    }
-    if (classes & INPUT_DEVICE_CLASS_DPAD) {
-        keyboardSource |= AINPUT_SOURCE_DPAD;
-    }
-    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
-        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
-    }
-
-    if (keyboardSource != 0) {
-        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
-    }
-
-    // Cursor-like devices.
-    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
-        device->addMapper(new CursorInputMapper(device));
-    }
-
-    // Touchscreens and touchpad devices.
-    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
-        device->addMapper(new MultiTouchInputMapper(device));
-    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
-        device->addMapper(new SingleTouchInputMapper(device));
-    }
-
-    // Joystick-like devices.
-    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
-        device->addMapper(new JoystickInputMapper(device));
-    }
-
-    // External stylus-like devices.
-    if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
-        device->addMapper(new ExternalStylusInputMapper(device));
-    }
-
+    device->populateMappers();
     return device;
 }
 
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 882407d..d06cc20 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -67,7 +67,7 @@
     void setEnabled(bool enabled, nsecs_t when);
 
     void dump(std::string& dump);
-    void addMapper(InputMapper* mapper);
+    void populateMappers();
     void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
     void reset(nsecs_t when);
     void process(const RawEvent* rawEvents, size_t count);
@@ -116,6 +116,14 @@
 
     std::optional<int32_t> getAssociatedDisplayId();
 
+    // construct and add a mapper to the input device
+    template <class T, typename... Args>
+    T& addMapper(Args... args) {
+        T* mapper = new T(this, args...);
+        mMappers.emplace_back(mapper);
+        return *mapper;
+    }
+
 private:
     InputReaderContext* mContext;
     int32_t mId;
@@ -125,7 +133,7 @@
     std::string mAlias;
     uint32_t mClasses;
 
-    std::vector<InputMapper*> mMappers;
+    std::vector<std::unique_ptr<InputMapper>> mMappers;
 
     uint32_t mSources;
     bool mIsExternal;
@@ -138,6 +146,26 @@
     int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
 
     PropertyMap mConfiguration;
+
+    // run a function against every mapper
+    inline void for_each_mapper(std::function<void(InputMapper&)> f) {
+        for (auto& mapperPtr : mMappers) {
+            f(*mapperPtr);
+        }
+    }
+
+    // return the first value returned by a function over every mapper.
+    // if all mappers return nullopt, return nullopt.
+    template <typename T>
+    inline std::optional<T> first_in_mappers(std::function<std::optional<T>(InputMapper&)> f) {
+        for (auto& mapperPtr : mMappers) {
+            std::optional<T> ret = f(*mapperPtr);
+            if (ret) {
+                return ret;
+            }
+        }
+        return std::nullopt;
+    }
 };
 
 } // namespace android
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 09ecb13..f913d82 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -10,6 +10,7 @@
         "InputClassifierConverter_test.cpp",
         "InputDispatcher_test.cpp",
         "InputReader_test.cpp",
+        "UinputDevice.cpp",
     ],
     cflags: [
         "-Wall",
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
index 6504738..be2e19e 100644
--- a/services/inputflinger/tests/EventHub_test.cpp
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -16,7 +16,8 @@
 
 #include "EventHub.h"
 
-#include <android-base/stringprintf.h>
+#include "UinputDevice.h"
+
 #include <gtest/gtest.h>
 #include <inttypes.h>
 #include <linux/uinput.h>
@@ -25,16 +26,16 @@
 
 #define TAG "EventHub_test"
 
+using android::createUinputDevice;
 using android::EventHub;
 using android::EventHubInterface;
 using android::InputDeviceIdentifier;
 using android::RawEvent;
 using android::sp;
-using android::base::StringPrintf;
+using android::UinputHomeKey;
 using std::chrono_literals::operator""ms;
 
 static constexpr bool DEBUG = false;
-static const char* DEVICE_NAME = "EventHub Test Device";
 
 static void dumpEvents(const std::vector<RawEvent>& events) {
     for (const RawEvent& event : events) {
@@ -62,27 +63,26 @@
 protected:
     std::unique_ptr<EventHubInterface> mEventHub;
     // We are only going to emulate a single input device currently.
-    android::base::unique_fd mDeviceFd;
+    std::unique_ptr<UinputHomeKey> mKeyboard;
     int32_t mDeviceId;
+
     virtual void SetUp() override {
         mEventHub = std::make_unique<EventHub>();
         consumeInitialDeviceAddedEvents();
-        createDevice();
+        mKeyboard = createUinputDevice<UinputHomeKey>();
         mDeviceId = waitForDeviceCreation();
     }
     virtual void TearDown() override {
-        mDeviceFd.reset();
+        mKeyboard.reset();
         waitForDeviceClose(mDeviceId);
     }
 
-    void createDevice();
     /**
      * Return the device id of the created device.
      */
     int32_t waitForDeviceCreation();
     void waitForDeviceClose(int32_t deviceId);
     void consumeInitialDeviceAddedEvents();
-    void sendEvent(uint16_t type, uint16_t code, int32_t value);
     std::vector<RawEvent> getEvents(std::chrono::milliseconds timeout = 5ms);
 };
 
@@ -105,48 +105,6 @@
     return events;
 }
 
-void EventHubTest::createDevice() {
-    mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
-    if (mDeviceFd < 0) {
-        FAIL() << "Can't open /dev/uinput :" << strerror(errno);
-    }
-
-    /**
-     * Signal which type of events this input device supports.
-     * We will emulate a keyboard here.
-     */
-    // enable key press/release event
-    if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_KEY)) {
-        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno);
-    }
-
-    // enable set of KEY events
-    if (ioctl(mDeviceFd, UI_SET_KEYBIT, KEY_HOME)) {
-        ADD_FAILURE() << "Error in ioctl : UI_SET_KEYBIT : KEY_HOME: " << strerror(errno);
-    }
-
-    // enable synchronization event
-    if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_SYN)) {
-        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno);
-    }
-
-    struct uinput_user_dev keyboard = {};
-    strlcpy(keyboard.name, DEVICE_NAME, UINPUT_MAX_NAME_SIZE);
-    keyboard.id.bustype = BUS_USB;
-    keyboard.id.vendor = 0x01;
-    keyboard.id.product = 0x01;
-    keyboard.id.version = 1;
-
-    if (write(mDeviceFd, &keyboard, sizeof(keyboard)) < 0) {
-        FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: "
-               << strerror(errno);
-    }
-
-    if (ioctl(mDeviceFd, UI_DEV_CREATE)) {
-        FAIL() << "Error in ioctl : UI_DEV_CREATE: " << strerror(errno);
-    }
-}
-
 /**
  * Since the test runs on a real platform, there will be existing devices
  * in addition to the test devices being added. Therefore, when EventHub is first created,
@@ -176,7 +134,7 @@
     EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_ADDED), deviceAddedEvent.type);
     InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId);
     const int32_t deviceId = deviceAddedEvent.deviceId;
-    EXPECT_EQ(identifier.name, DEVICE_NAME);
+    EXPECT_EQ(identifier.name, mKeyboard->getName());
     const RawEvent& finishedDeviceScanEvent = events[1];
     EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
               finishedDeviceScanEvent.type);
@@ -194,22 +152,6 @@
               finishedDeviceScanEvent.type);
 }
 
-void EventHubTest::sendEvent(uint16_t type, uint16_t code, int32_t value) {
-    struct input_event event = {};
-    event.type = type;
-    event.code = code;
-    event.value = value;
-    event.time = {}; // uinput ignores the timestamp
-
-    if (write(mDeviceFd, &event, sizeof(input_event)) < 0) {
-        std::string msg = StringPrintf("Could not write event %" PRIu16 " %" PRIu16
-                                       " with value %" PRId32 " : %s",
-                                       type, code, value, strerror(errno));
-        ALOGE("%s", msg.c_str());
-        ADD_FAILURE() << msg.c_str();
-    }
-}
-
 /**
  * Ensure that input_events are generated with monotonic clock.
  * That means input_event should receive a timestamp that is in the future of the time
@@ -218,13 +160,7 @@
  */
 TEST_F(EventHubTest, InputEvent_TimestampIsMonotonic) {
     nsecs_t lastEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
-    // key press
-    sendEvent(EV_KEY, KEY_HOME, 1);
-    sendEvent(EV_SYN, SYN_REPORT, 0);
-
-    // key release
-    sendEvent(EV_KEY, KEY_HOME, 0);
-    sendEvent(EV_SYN, SYN_REPORT, 0);
+    ASSERT_NO_FATAL_FAILURE(mKeyboard->pressAndReleaseHomeKey());
 
     std::vector<RawEvent> events = getEvents();
     ASSERT_EQ(4U, events.size()) << "Expected to receive 2 keys and 2 syncs, total of 4 events";
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 27db8f5..c4092cd 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -43,6 +43,18 @@
     float y;
 };
 
+/**
+ * Return a DOWN key event with KEYCODE_A.
+ */
+static KeyEvent getTestKeyEvent() {
+    KeyEvent event;
+
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+                     AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
+                     ARBITRARY_TIME);
+    return event;
+}
+
 // --- FakeInputDispatcherPolicy ---
 
 class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
@@ -197,6 +209,69 @@
     }
 };
 
+// --- HmacKeyManagerTest ---
+
+class HmacKeyManagerTest : public testing::Test {
+protected:
+    HmacKeyManager mHmacKeyManager;
+};
+
+/**
+ * Ensure that separate calls to sign the same data are generating the same key.
+ * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
+ * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
+ * tests.
+ */
+TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) {
+    KeyEvent event = getTestKeyEvent();
+    VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
+
+    std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(verifiedEvent);
+    std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(verifiedEvent);
+    ASSERT_EQ(hmac1, hmac2);
+}
+
+/**
+ * Ensure that changes in VerifiedKeyEvent produce a different hmac.
+ */
+TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) {
+    KeyEvent event = getTestKeyEvent();
+    VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
+    std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(verifiedEvent);
+
+    verifiedEvent.deviceId += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.source += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.eventTimeNanos += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.displayId += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.action += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.downTimeNanos += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.flags += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.keyCode += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.scanCode += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.metaState += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.repeatCount += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+}
 
 // --- InputDispatcherTest ---
 
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 8ca7e4a..7cd8793 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1354,22 +1354,22 @@
         ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
     }
 
-    void disableDevice(int32_t deviceId, InputDevice* device) {
+    void disableDevice(int32_t deviceId) {
         mFakePolicy->addDisabledDevice(deviceId);
         mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
     }
 
-    void enableDevice(int32_t deviceId, InputDevice* device) {
+    void enableDevice(int32_t deviceId) {
         mFakePolicy->removeDisabledDevice(deviceId);
         mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
     }
 
-    FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
-            const std::string& name, uint32_t classes, uint32_t sources,
-            const PropertyMap* configuration) {
+    FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
+                                                  const std::string& name, uint32_t classes,
+                                                  uint32_t sources,
+                                                  const PropertyMap* configuration) {
         InputDevice* device = mReader->newDevice(deviceId, controllerNumber, name, classes);
-        FakeInputMapper* mapper = new FakeInputMapper(device, sources);
-        device->addMapper(mapper);
+        FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(sources);
         mReader->setNextDevice(device);
         addDevice(deviceId, name, classes, configuration);
         return mapper;
@@ -1406,8 +1406,7 @@
     constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
     InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
     // Must add at least one mapper or the device will be ignored!
-    FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
-    device->addMapper(mapper);
+    device->addMapper<FakeInputMapper>(AINPUT_SOURCE_KEYBOARD);
     mReader->setNextDevice(device);
     ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
 
@@ -1418,20 +1417,20 @@
     ASSERT_EQ(deviceId, resetArgs.deviceId);
 
     ASSERT_EQ(device->isEnabled(), true);
-    disableDevice(deviceId, device);
+    disableDevice(deviceId);
     mReader->loopOnce();
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_EQ(deviceId, resetArgs.deviceId);
     ASSERT_EQ(device->isEnabled(), false);
 
-    disableDevice(deviceId, device);
+    disableDevice(deviceId);
     mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled());
     ASSERT_EQ(device->isEnabled(), false);
 
-    enableDevice(deviceId, device);
+    enableDevice(deviceId);
     mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_EQ(deviceId, resetArgs.deviceId);
@@ -1439,10 +1438,10 @@
 }
 
 TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
-    FakeInputMapper* mapper = nullptr;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
-    mapper->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
+    FakeInputMapper& mapper =
+            addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD,
+                                         AINPUT_SOURCE_KEYBOARD, nullptr);
+    mapper.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(0,
             AINPUT_SOURCE_ANY, AKEYCODE_A))
@@ -1466,10 +1465,10 @@
 }
 
 TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) {
-    FakeInputMapper* mapper = nullptr;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
-    mapper->setScanCodeState(KEY_A, AKEY_STATE_DOWN);
+    FakeInputMapper& mapper =
+            addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD,
+                                         AINPUT_SOURCE_KEYBOARD, nullptr);
+    mapper.setScanCodeState(KEY_A, AKEY_STATE_DOWN);
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(0,
             AINPUT_SOURCE_ANY, KEY_A))
@@ -1493,10 +1492,10 @@
 }
 
 TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) {
-    FakeInputMapper* mapper = nullptr;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
-    mapper->setSwitchState(SW_LID, AKEY_STATE_DOWN);
+    FakeInputMapper& mapper =
+            addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD,
+                                         AINPUT_SOURCE_KEYBOARD, nullptr);
+    mapper.setSwitchState(SW_LID, AKEY_STATE_DOWN);
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(0,
             AINPUT_SOURCE_ANY, SW_LID))
@@ -1520,12 +1519,12 @@
 }
 
 TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) {
-    FakeInputMapper* mapper = nullptr;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
+    FakeInputMapper& mapper =
+            addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD,
+                                         AINPUT_SOURCE_KEYBOARD, nullptr);
 
-    mapper->addSupportedKeyCode(AKEYCODE_A);
-    mapper->addSupportedKeyCode(AKEYCODE_B);
+    mapper.addSupportedKeyCode(AKEYCODE_A);
+    mapper.addSupportedKeyCode(AKEYCODE_B);
 
     const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 };
     uint8_t flags[4] = { 0, 0, 0, 1 };
@@ -1565,16 +1564,16 @@
 }
 
 TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
-    FakeInputMapper* mapper = nullptr;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
+    FakeInputMapper& mapper =
+            addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD,
+                                         AINPUT_SOURCE_KEYBOARD, nullptr);
 
     mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1);
     mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
 
     RawEvent event;
-    ASSERT_NO_FATAL_FAILURE(mapper->assertProcessWasCalled(&event));
+    ASSERT_NO_FATAL_FAILURE(mapper.assertProcessWasCalled(&event));
     ASSERT_EQ(0, event.when);
     ASSERT_EQ(1, event.deviceId);
     ASSERT_EQ(EV_KEY, event.type);
@@ -1587,8 +1586,7 @@
     constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD;
     InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass);
     // Must add at least one mapper or the device will be ignored!
-    FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
-    device->addMapper(mapper);
+    device->addMapper<FakeInputMapper>(AINPUT_SOURCE_KEYBOARD);
     mReader->setNextDevice(device);
     ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
 
@@ -1596,19 +1594,19 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     uint32_t prevSequenceNum = resetArgs.sequenceNum;
 
-    disableDevice(deviceId, device);
+    disableDevice(deviceId);
     mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
     prevSequenceNum = resetArgs.sequenceNum;
 
-    enableDevice(deviceId, device);
+    enableDevice(deviceId);
     mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
     prevSequenceNum = resetArgs.sequenceNum;
 
-    disableDevice(deviceId, device);
+    disableDevice(deviceId);
     mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
@@ -1621,8 +1619,7 @@
     const char* DEVICE_LOCATION = "USB1";
     InputDevice* device = mReader->newDevice(deviceId, 0 /*controllerNumber*/, "fake", deviceClass,
             DEVICE_LOCATION);
-    FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN);
-    device->addMapper(mapper);
+    FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(AINPUT_SOURCE_TOUCHSCREEN);
     mReader->setNextDevice(device);
 
     const uint8_t hdmi1 = 1;
@@ -1645,7 +1642,7 @@
     ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled());
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
-    ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
 
     // Device should only dispatch to the specified display.
     ASSERT_EQ(deviceId, device->getId());
@@ -1653,14 +1650,13 @@
     ASSERT_TRUE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
 
     // Can't dispatch event from a disabled device.
-    disableDevice(deviceId, device);
+    disableDevice(deviceId);
     mReader->loopOnce();
     ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
 }
 
 
 // --- InputDeviceTest ---
-
 class InputDeviceTest : public testing::Test {
 protected:
     static const char* DEVICE_NAME;
@@ -1764,21 +1760,19 @@
     // Configuration.
     mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8("key"), String8("value"));
 
-    FakeInputMapper* mapper1 = new FakeInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD);
-    mapper1->setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    mapper1->setMetaState(AMETA_ALT_ON);
-    mapper1->addSupportedKeyCode(AKEYCODE_A);
-    mapper1->addSupportedKeyCode(AKEYCODE_B);
-    mapper1->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
-    mapper1->setKeyCodeState(AKEYCODE_B, AKEY_STATE_UP);
-    mapper1->setScanCodeState(2, AKEY_STATE_DOWN);
-    mapper1->setScanCodeState(3, AKEY_STATE_UP);
-    mapper1->setSwitchState(4, AKEY_STATE_DOWN);
-    mDevice->addMapper(mapper1);
+    FakeInputMapper& mapper1 = mDevice->addMapper<FakeInputMapper>(AINPUT_SOURCE_KEYBOARD);
+    mapper1.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    mapper1.setMetaState(AMETA_ALT_ON);
+    mapper1.addSupportedKeyCode(AKEYCODE_A);
+    mapper1.addSupportedKeyCode(AKEYCODE_B);
+    mapper1.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
+    mapper1.setKeyCodeState(AKEYCODE_B, AKEY_STATE_UP);
+    mapper1.setScanCodeState(2, AKEY_STATE_DOWN);
+    mapper1.setScanCodeState(3, AKEY_STATE_UP);
+    mapper1.setSwitchState(4, AKEY_STATE_DOWN);
 
-    FakeInputMapper* mapper2 = new FakeInputMapper(mDevice, AINPUT_SOURCE_TOUCHSCREEN);
-    mapper2->setMetaState(AMETA_SHIFT_ON);
-    mDevice->addMapper(mapper2);
+    FakeInputMapper& mapper2 = mDevice->addMapper<FakeInputMapper>(AINPUT_SOURCE_TOUCHSCREEN);
+    mapper2.setMetaState(AMETA_SHIFT_ON);
 
     InputReaderConfiguration config;
     mDevice->configure(ARBITRARY_TIME, &config, 0);
@@ -1788,13 +1782,13 @@
             << "Device should have read configuration during configuration phase.";
     ASSERT_STREQ("value", propertyValue.string());
 
-    ASSERT_NO_FATAL_FAILURE(mapper1->assertConfigureWasCalled());
-    ASSERT_NO_FATAL_FAILURE(mapper2->assertConfigureWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper1.assertConfigureWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper2.assertConfigureWasCalled());
 
     // Reset
     mDevice->reset(ARBITRARY_TIME);
-    ASSERT_NO_FATAL_FAILURE(mapper1->assertResetWasCalled());
-    ASSERT_NO_FATAL_FAILURE(mapper2->assertResetWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper1.assertResetWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper2.assertResetWasCalled());
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -1850,16 +1844,15 @@
     RawEvent event;
     mDevice->process(&event, 1);
 
-    ASSERT_NO_FATAL_FAILURE(mapper1->assertProcessWasCalled());
-    ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper1.assertProcessWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled());
 }
 
 // A single input device is associated with a specific display. Check that:
 // 1. Device is disabled if the viewport corresponding to the associated display is not found
 // 2. Device is disabled when setEnabled API is called
 TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) {
-    FakeInputMapper* mapper = new FakeInputMapper(mDevice, AINPUT_SOURCE_TOUCHSCREEN);
-    mDevice->addMapper(mapper);
+    mDevice->addMapper<FakeInputMapper>(AINPUT_SOURCE_TOUCHSCREEN);
 
     // First Configuration.
     mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
@@ -1944,10 +1937,12 @@
         mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
     }
 
-    void addMapperAndConfigure(InputMapper* mapper) {
-        mDevice->addMapper(mapper);
+    template <class T, typename... Args>
+    T& addMapperAndConfigure(Args... args) {
+        T& mapper = mDevice->addMapper<T>(args...);
         configureDevice(0);
         mDevice->reset(ARBITRARY_TIME);
+        return mapper;
     }
 
     void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
@@ -1962,15 +1957,15 @@
         mFakePolicy->clearViewports();
     }
 
-    static void process(InputMapper* mapper, nsecs_t when, int32_t type,
-            int32_t code, int32_t value) {
+    static void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code,
+                        int32_t value) {
         RawEvent event;
         event.when = when;
-        event.deviceId = mapper->getDeviceId();
+        event.deviceId = mapper.getDeviceId();
         event.type = type;
         event.code = code;
         event.value = value;
-        mapper->process(&event);
+        mapper.process(&event);
     }
 
     static void assertMotionRange(const InputDeviceInfo& info,
@@ -2024,26 +2019,23 @@
 };
 
 TEST_F(SwitchInputMapperTest, GetSources) {
-    SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
-    addMapperAndConfigure(mapper);
+    SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
 
-    ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper->getSources());
+    ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper.getSources());
 }
 
 TEST_F(SwitchInputMapperTest, GetSwitchState) {
-    SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
-    addMapperAndConfigure(mapper);
+    SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
 
     mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 1);
-    ASSERT_EQ(1, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
+    ASSERT_EQ(1, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
 
     mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 0);
-    ASSERT_EQ(0, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
+    ASSERT_EQ(0, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
 }
 
 TEST_F(SwitchInputMapperTest, Process) {
-    SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
-    addMapperAndConfigure(mapper);
+    SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
 
     process(mapper, ARBITRARY_TIME, EV_SW, SW_LID, 1);
     process(mapper, ARBITRARY_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
@@ -2068,7 +2060,7 @@
 
     void prepareDisplay(int32_t orientation);
 
-    void testDPadKeyRotation(KeyboardInputMapper* mapper, int32_t originalScanCode,
+    void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
                              int32_t originalKeyCode, int32_t rotatedKeyCode,
                              int32_t displayId = ADISPLAY_ID_NONE);
 };
@@ -2081,7 +2073,7 @@
             orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
 }
 
-void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
+void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
                                                   int32_t originalScanCode, int32_t originalKeyCode,
                                                   int32_t rotatedKeyCode, int32_t displayId) {
     NotifyKeyArgs args;
@@ -2102,11 +2094,11 @@
 }
 
 TEST_F(KeyboardInputMapperTest, GetSources) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper.getSources());
 }
 
 TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) {
@@ -2115,9 +2107,9 @@
     mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
     mFakeEventHub->addKey(DEVICE_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     // Key down by scan code.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
@@ -2213,38 +2205,38 @@
     mFakeEventHub->addKey(DEVICE_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
     mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     // Initial metastate.
-    ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 
     // Metakey down.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
     NotifyKeyArgs args;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
-    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
     ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
 
     // Key down.
     process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
-    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
 
     // Key up.
     process(mapper, ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
-    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
 
     // Metakey up.
     process(mapper, ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_NONE, args.metaState);
-    ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
     ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
 }
 
@@ -2254,9 +2246,9 @@
     mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
     mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -2275,10 +2267,10 @@
     mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
     mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addConfigurationProperty("keyboard.orientationAware", "1");
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     prepareDisplay(DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(
@@ -2348,9 +2340,9 @@
     // key events should not be associated with a specific display id
     mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     NotifyKeyArgs args;
 
     // Display id should be ADISPLAY_ID_NONE without any display configuration.
@@ -2373,10 +2365,10 @@
     // key events should be associated with the internal viewport
     mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addConfigurationProperty("keyboard.orientationAware", "1");
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     NotifyKeyArgs args;
 
     // Display id should be ADISPLAY_ID_NONE without any display configuration.
@@ -2402,39 +2394,39 @@
 }
 
 TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 1);
-    ASSERT_EQ(1, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+    ASSERT_EQ(1, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
 
     mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 0);
-    ASSERT_EQ(0, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+    ASSERT_EQ(0, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
 }
 
 TEST_F(KeyboardInputMapperTest, GetScanCodeState) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 1);
-    ASSERT_EQ(1, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+    ASSERT_EQ(1, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
 
     mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 0);
-    ASSERT_EQ(0, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+    ASSERT_EQ(0, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
 }
 
 TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0);
 
     const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B };
     uint8_t flags[2] = { 0, 0 };
-    ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 1, keyCodes, flags));
+    ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, 1, keyCodes, flags));
     ASSERT_TRUE(flags[0]);
     ASSERT_FALSE(flags[1]);
 }
@@ -2447,9 +2439,9 @@
     mFakeEventHub->addKey(DEVICE_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
     mFakeEventHub->addKey(DEVICE_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     // Initialization should have turned all of the lights off.
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
@@ -2462,7 +2454,7 @@
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
-    ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper->getMetaState());
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
 
     // Toggle num lock on.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
@@ -2470,7 +2462,7 @@
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
-    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper->getMetaState());
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
 
     // Toggle caps lock off.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
@@ -2478,7 +2470,7 @@
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
-    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper->getMetaState());
+    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
 
     // Toggle scroll lock on.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
@@ -2486,7 +2478,7 @@
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
-    ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper->getMetaState());
+    ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
 
     // Toggle num lock off.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
@@ -2494,7 +2486,7 @@
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
     ASSERT_TRUE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
-    ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper->getMetaState());
+    ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
 
     // Toggle scroll lock off.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
@@ -2502,7 +2494,7 @@
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_NUML));
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_SCROLLL));
-    ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 }
 
 TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
@@ -2527,13 +2519,13 @@
     mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
     mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD,
-                                                          AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    KeyboardInputMapper* mapper2 = new KeyboardInputMapper(device2.get(), AINPUT_SOURCE_KEYBOARD,
-                                                           AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    device2->addMapper(mapper2);
+    KeyboardInputMapper& mapper2 =
+            device2->addMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                    AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
     device2->reset(ARBITRARY_TIME);
 
@@ -2593,9 +2585,9 @@
     mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
     mFakeEventHub->addKey(DEVICE_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD,
-                                                          AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
     NotifyKeyArgs args;
@@ -2631,10 +2623,10 @@
     mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
     mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD,
-                                                          AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addConfigurationProperty("keyboard.doNotWakeByDefault", "1");
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
     NotifyKeyArgs args;
@@ -2677,8 +2669,8 @@
         mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController);
     }
 
-    void testMotionRotation(CursorInputMapper* mapper,
-            int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY);
+    void testMotionRotation(CursorInputMapper& mapper, int32_t originalX, int32_t originalY,
+                            int32_t rotatedX, int32_t rotatedY);
 
     void prepareDisplay(int32_t orientation) {
         const std::string uniqueId = "local:0";
@@ -2690,8 +2682,9 @@
 
 const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
 
-void CursorInputMapperTest::testMotionRotation(CursorInputMapper* mapper,
-        int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY) {
+void CursorInputMapperTest::testMotionRotation(CursorInputMapper& mapper, int32_t originalX,
+                                               int32_t originalY, int32_t rotatedX,
+                                               int32_t rotatedY) {
     NotifyMotionArgs args;
 
     process(mapper, ARBITRARY_TIME, EV_REL, REL_X, originalX);
@@ -2706,28 +2699,25 @@
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsNavigation_GetSources_ReturnsTrackball) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper.getSources());
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeFromPointerController) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     InputDeviceInfo info;
-    mapper->populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(&info);
 
     // Initially there may not be a valid motion range.
     ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
@@ -2739,7 +2729,7 @@
     mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
 
     InputDeviceInfo info2;
-    mapper->populateDeviceInfo(&info2);
+    mapper.populateDeviceInfo(&info2);
 
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
             AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE,
@@ -2753,12 +2743,11 @@
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsScaledRange) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     InputDeviceInfo info;
-    mapper->populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(&info);
 
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
             AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL,
@@ -2772,9 +2761,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -2865,9 +2853,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -2889,9 +2876,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -2923,9 +2909,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -2971,9 +2956,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
@@ -2987,10 +2971,9 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
     addConfigurationProperty("cursor.orientationAware", "1");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     prepareDisplay(DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
@@ -3034,9 +3017,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
     mFakePointerController->setPosition(100, 200);
@@ -3322,9 +3304,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerAround) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
     mFakePointerController->setPosition(100, 200);
@@ -3344,10 +3325,9 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_PointerCapture) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
     mFakePolicy->setPointerCapture(true);
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -3433,8 +3413,7 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     // Setup for second display.
     constexpr int32_t SECOND_DISPLAY_ID = 1;
@@ -3462,7 +3441,6 @@
     ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId);
 }
 
-
 // --- TouchInputMapperTest ---
 
 class TouchInputMapperTest : public InputMapperTest {
@@ -3637,15 +3615,15 @@
     void prepareButtons();
     void prepareAxes(int axes);
 
-    void processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y);
-    void processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y);
-    void processUp(SingleTouchInputMapper* mappery);
-    void processPressure(SingleTouchInputMapper* mapper, int32_t pressure);
-    void processToolMajor(SingleTouchInputMapper* mapper, int32_t toolMajor);
-    void processDistance(SingleTouchInputMapper* mapper, int32_t distance);
-    void processTilt(SingleTouchInputMapper* mapper, int32_t tiltX, int32_t tiltY);
-    void processKey(SingleTouchInputMapper* mapper, int32_t code, int32_t value);
-    void processSync(SingleTouchInputMapper* mapper);
+    void processDown(SingleTouchInputMapper& mapper, int32_t x, int32_t y);
+    void processMove(SingleTouchInputMapper& mapper, int32_t x, int32_t y);
+    void processUp(SingleTouchInputMapper& mappery);
+    void processPressure(SingleTouchInputMapper& mapper, int32_t pressure);
+    void processToolMajor(SingleTouchInputMapper& mapper, int32_t toolMajor);
+    void processDistance(SingleTouchInputMapper& mapper, int32_t distance);
+    void processTilt(SingleTouchInputMapper& mapper, int32_t tiltX, int32_t tiltY);
+    void processKey(SingleTouchInputMapper& mapper, int32_t code, int32_t value);
+    void processSync(SingleTouchInputMapper& mapper);
 };
 
 void SingleTouchInputMapperTest::prepareButtons() {
@@ -3679,103 +3657,95 @@
     }
 }
 
-void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
+void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper& mapper, int32_t x, int32_t y) {
     process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 1);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y);
 }
 
-void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
+void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper& mapper, int32_t x, int32_t y) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y);
 }
 
-void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper* mapper) {
+void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 0);
 }
 
-void SingleTouchInputMapperTest::processPressure(
-        SingleTouchInputMapper* mapper, int32_t pressure) {
+void SingleTouchInputMapperTest::processPressure(SingleTouchInputMapper& mapper, int32_t pressure) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_PRESSURE, pressure);
 }
 
-void SingleTouchInputMapperTest::processToolMajor(
-        SingleTouchInputMapper* mapper, int32_t toolMajor) {
+void SingleTouchInputMapperTest::processToolMajor(SingleTouchInputMapper& mapper,
+                                                  int32_t toolMajor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
 }
 
-void SingleTouchInputMapperTest::processDistance(
-        SingleTouchInputMapper* mapper, int32_t distance) {
+void SingleTouchInputMapperTest::processDistance(SingleTouchInputMapper& mapper, int32_t distance) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_DISTANCE, distance);
 }
 
-void SingleTouchInputMapperTest::processTilt(
-        SingleTouchInputMapper* mapper, int32_t tiltX, int32_t tiltY) {
+void SingleTouchInputMapperTest::processTilt(SingleTouchInputMapper& mapper, int32_t tiltX,
+                                             int32_t tiltY) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_X, tiltX);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TILT_Y, tiltY);
 }
 
-void SingleTouchInputMapperTest::processKey(
-        SingleTouchInputMapper* mapper, int32_t code, int32_t value) {
+void SingleTouchInputMapperTest::processKey(SingleTouchInputMapper& mapper, int32_t code,
+                                            int32_t value) {
     process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
 }
 
-void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) {
+void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 }
 
-
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsACursor_ReturnsTouchPad) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_X);
     mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_Y);
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchPad_ReturnsTouchPad) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareButtons();
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchPad");
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_ReturnsTouchScreen) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareButtons();
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     // Unknown key.
-    ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+    ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
 
     // Virtual key is down.
     int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
@@ -3784,27 +3754,26 @@
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
-    ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
+    ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
 
     // Virtual key is up.
     processUp(mapper);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
-    ASSERT_EQ(AKEY_STATE_UP, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
+    ASSERT_EQ(AKEY_STATE_UP, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
 }
 
 TEST_F(SingleTouchInputMapperTest, GetScanCodeState) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     // Unknown key.
-    ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+    ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
 
     // Virtual key is down.
     int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
@@ -3813,40 +3782,38 @@
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
-    ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
+    ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
 
     // Virtual key is up.
     processUp(mapper);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
-    ASSERT_EQ(AKEY_STATE_UP, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
+    ASSERT_EQ(AKEY_STATE_UP, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
 }
 
 TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     const int32_t keys[2] = { AKEYCODE_HOME, AKEYCODE_A };
     uint8_t flags[2] = { 0, 0 };
-    ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 2, keys, flags));
+    ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, 2, keys, flags));
     ASSERT_TRUE(flags[0]);
     ASSERT_FALSE(flags[1]);
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -3891,13 +3858,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -4013,13 +3979,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -4087,7 +4052,6 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture_VirtualDisplay) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     addConfigurationProperty("touch.displayId", VIRTUAL_DISPLAY_UNIQUE_ID);
 
@@ -4095,7 +4059,7 @@
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -4186,13 +4150,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -4277,12 +4240,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotateMotions) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareButtons();
     prepareAxes(POSITION);
     addConfigurationProperty("touch.orientationAware", "0");
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -4301,11 +4263,10 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -4367,12 +4328,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE | TOOL | DISTANCE | TILT);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -4412,13 +4372,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareLocationCalibration();
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     int32_t rawX = 100;
     int32_t rawY = 200;
@@ -4436,12 +4395,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     NotifyKeyArgs keyArgs;
@@ -4680,12 +4638,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -4816,13 +4773,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     mFakeEventHub->addKey(DEVICE_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -4889,12 +4845,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsValueIsZero) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -4960,27 +4915,26 @@
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
 }
 
-
 // --- MultiTouchInputMapperTest ---
 
 class MultiTouchInputMapperTest : public TouchInputMapperTest {
 protected:
     void prepareAxes(int axes);
 
-    void processPosition(MultiTouchInputMapper* mapper, int32_t x, int32_t y);
-    void processTouchMajor(MultiTouchInputMapper* mapper, int32_t touchMajor);
-    void processTouchMinor(MultiTouchInputMapper* mapper, int32_t touchMinor);
-    void processToolMajor(MultiTouchInputMapper* mapper, int32_t toolMajor);
-    void processToolMinor(MultiTouchInputMapper* mapper, int32_t toolMinor);
-    void processOrientation(MultiTouchInputMapper* mapper, int32_t orientation);
-    void processPressure(MultiTouchInputMapper* mapper, int32_t pressure);
-    void processDistance(MultiTouchInputMapper* mapper, int32_t distance);
-    void processId(MultiTouchInputMapper* mapper, int32_t id);
-    void processSlot(MultiTouchInputMapper* mapper, int32_t slot);
-    void processToolType(MultiTouchInputMapper* mapper, int32_t toolType);
-    void processKey(MultiTouchInputMapper* mapper, int32_t code, int32_t value);
-    void processMTSync(MultiTouchInputMapper* mapper);
-    void processSync(MultiTouchInputMapper* mapper);
+    void processPosition(MultiTouchInputMapper& mapper, int32_t x, int32_t y);
+    void processTouchMajor(MultiTouchInputMapper& mapper, int32_t touchMajor);
+    void processTouchMinor(MultiTouchInputMapper& mapper, int32_t touchMinor);
+    void processToolMajor(MultiTouchInputMapper& mapper, int32_t toolMajor);
+    void processToolMinor(MultiTouchInputMapper& mapper, int32_t toolMinor);
+    void processOrientation(MultiTouchInputMapper& mapper, int32_t orientation);
+    void processPressure(MultiTouchInputMapper& mapper, int32_t pressure);
+    void processDistance(MultiTouchInputMapper& mapper, int32_t distance);
+    void processId(MultiTouchInputMapper& mapper, int32_t id);
+    void processSlot(MultiTouchInputMapper& mapper, int32_t slot);
+    void processToolType(MultiTouchInputMapper& mapper, int32_t toolType);
+    void processKey(MultiTouchInputMapper& mapper, int32_t code, int32_t value);
+    void processMTSync(MultiTouchInputMapper& mapper);
+    void processSync(MultiTouchInputMapper& mapper);
 };
 
 void MultiTouchInputMapperTest::prepareAxes(int axes) {
@@ -5033,83 +4987,74 @@
     }
 }
 
-void MultiTouchInputMapperTest::processPosition(
-        MultiTouchInputMapper* mapper, int32_t x, int32_t y) {
+void MultiTouchInputMapperTest::processPosition(MultiTouchInputMapper& mapper, int32_t x,
+                                                int32_t y) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_X, x);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_POSITION_Y, y);
 }
 
-void MultiTouchInputMapperTest::processTouchMajor(
-        MultiTouchInputMapper* mapper, int32_t touchMajor) {
+void MultiTouchInputMapperTest::processTouchMajor(MultiTouchInputMapper& mapper,
+                                                  int32_t touchMajor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
 }
 
-void MultiTouchInputMapperTest::processTouchMinor(
-        MultiTouchInputMapper* mapper, int32_t touchMinor) {
+void MultiTouchInputMapperTest::processTouchMinor(MultiTouchInputMapper& mapper,
+                                                  int32_t touchMinor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
 }
 
-void MultiTouchInputMapperTest::processToolMajor(
-        MultiTouchInputMapper* mapper, int32_t toolMajor) {
+void MultiTouchInputMapperTest::processToolMajor(MultiTouchInputMapper& mapper, int32_t toolMajor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
 }
 
-void MultiTouchInputMapperTest::processToolMinor(
-        MultiTouchInputMapper* mapper, int32_t toolMinor) {
+void MultiTouchInputMapperTest::processToolMinor(MultiTouchInputMapper& mapper, int32_t toolMinor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
 }
 
-void MultiTouchInputMapperTest::processOrientation(
-        MultiTouchInputMapper* mapper, int32_t orientation) {
+void MultiTouchInputMapperTest::processOrientation(MultiTouchInputMapper& mapper,
+                                                   int32_t orientation) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_ORIENTATION, orientation);
 }
 
-void MultiTouchInputMapperTest::processPressure(
-        MultiTouchInputMapper* mapper, int32_t pressure) {
+void MultiTouchInputMapperTest::processPressure(MultiTouchInputMapper& mapper, int32_t pressure) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_PRESSURE, pressure);
 }
 
-void MultiTouchInputMapperTest::processDistance(
-        MultiTouchInputMapper* mapper, int32_t distance) {
+void MultiTouchInputMapperTest::processDistance(MultiTouchInputMapper& mapper, int32_t distance) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_DISTANCE, distance);
 }
 
-void MultiTouchInputMapperTest::processId(
-        MultiTouchInputMapper* mapper, int32_t id) {
+void MultiTouchInputMapperTest::processId(MultiTouchInputMapper& mapper, int32_t id) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, id);
 }
 
-void MultiTouchInputMapperTest::processSlot(
-        MultiTouchInputMapper* mapper, int32_t slot) {
+void MultiTouchInputMapperTest::processSlot(MultiTouchInputMapper& mapper, int32_t slot) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, slot);
 }
 
-void MultiTouchInputMapperTest::processToolType(
-        MultiTouchInputMapper* mapper, int32_t toolType) {
+void MultiTouchInputMapperTest::processToolType(MultiTouchInputMapper& mapper, int32_t toolType) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
 }
 
-void MultiTouchInputMapperTest::processKey(
-        MultiTouchInputMapper* mapper, int32_t code, int32_t value) {
+void MultiTouchInputMapperTest::processKey(MultiTouchInputMapper& mapper, int32_t code,
+                                           int32_t value) {
     process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
 }
 
-void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) {
+void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, EV_SYN, SYN_MT_REPORT, 0);
 }
 
-void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper* mapper) {
+void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 }
 
-
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5381,12 +5326,11 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5557,12 +5501,11 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5728,11 +5671,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR | DISTANCE);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -5778,12 +5720,11 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | MINOR);
     addConfigurationProperty("touch.size.calibration", "geometric");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -5816,7 +5757,6 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_SummedLinearCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL);
@@ -5824,7 +5764,7 @@
     addConfigurationProperty("touch.size.scale", "10");
     addConfigurationProperty("touch.size.bias", "160");
     addConfigurationProperty("touch.size.isSummed", "1");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     // Note: We only provide a single common touch/tool value because the device is assumed
@@ -5869,14 +5809,13 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_AreaCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL);
     addConfigurationProperty("touch.size.calibration", "area");
     addConfigurationProperty("touch.size.scale", "43");
     addConfigurationProperty("touch.size.bias", "3");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -5903,16 +5842,15 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_PressureAxis_AmplitudeCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | PRESSURE);
     addConfigurationProperty("touch.pressure.calibration", "amplitude");
     addConfigurationProperty("touch.pressure.scale", "0.01");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     InputDeviceInfo info;
-    mapper->populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(&info);
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
             AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_TOUCHSCREEN,
             0.0f, RAW_PRESSURE_MAX * 0.01, 0.0f, 0.0f));
@@ -5938,11 +5876,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     NotifyKeyArgs keyArgs;
@@ -6182,11 +6119,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6333,12 +6269,11 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6405,11 +6340,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfItsValueIsZero) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6482,7 +6416,6 @@
  * This can be checked by looking at the displayId of the resulting NotifyMotionArgs.
  */
 TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     const std::string usb2 = "USB2";
     const uint8_t hdmi1 = 0;
     const uint8_t hdmi2 = 1;
@@ -6491,7 +6424,7 @@
 
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
     mFakePolicy->addInputPortAssociation(usb2, hdmi2);
@@ -6522,14 +6455,13 @@
  * Expect fallback to internal viewport if device is external and external viewport is not present.
  */
 TEST_F(MultiTouchInputMapperTest, Viewports_Fallback) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     mDevice->setExternal(true);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
 
     NotifyMotionArgs motionArgs;
 
@@ -6559,13 +6491,12 @@
     mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
     prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
 
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // Check source is mouse that would obtain the PointerController.
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 
     NotifyMotionArgs motionArgs;
     processPosition(mapper, 100, 100);
@@ -6578,10 +6509,9 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
     // Setup the first touch screen device.
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareAxes(POSITION | ID | SLOT);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // Create the second touch screen device, and enable multi fingers.
     const std::string USB2 = "USB2";
@@ -6606,8 +6536,7 @@
             String8("touchScreen"));
 
     // Setup the second touch screen device.
-    MultiTouchInputMapper* mapper2 = new MultiTouchInputMapper(device2.get());
-    device2->addMapper(mapper2);
+    MultiTouchInputMapper& mapper2 = device2->addMapper<MultiTouchInputMapper>();
     device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
     device2->reset(ARBITRARY_TIME);
 
@@ -6659,11 +6588,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     // Unrotated video frame
@@ -6685,10 +6613,9 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
     // Unrotated video frame
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
     NotifyMotionArgs motionArgs;
@@ -6710,10 +6637,9 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
     // Unrotated video frames. There's no rule that they must all have the same dimensions,
     // so mix these.
     TouchVideoFrame frame1(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
@@ -6737,7 +6663,6 @@
  * expected to be disabled, and it should be enabled after the viewport has found.
  */
 TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     constexpr uint8_t hdmi2 = 1;
     const std::string secondaryUniqueId = "uniqueId2";
     constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
@@ -6746,7 +6671,7 @@
 
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     ASSERT_EQ(mDevice->isEnabled(), false);
 
@@ -6767,11 +6692,10 @@
  * Test touch should not work if outside of surface.
  */
 TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // Touch on left-top area should work.
     int32_t rawX = DISPLAY_WIDTH / 2 - 1;
@@ -6783,7 +6707,7 @@
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
 
     // Reset.
-    mapper->reset(ARBITRARY_TIME);
+    mapper.reset(ARBITRARY_TIME);
 
     // Let logical display be different to physical display and rotate 90-degrees.
     std::optional<DisplayViewport> internalViewport =
@@ -6811,11 +6735,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6857,11 +6780,10 @@
  * UP events should be ignored.
  */
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
new file mode 100644
index 0000000..2775d21
--- /dev/null
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "UinputDevice.h"
+
+#include <android-base/stringprintf.h>
+
+namespace android {
+
+// --- UinputDevice ---
+
+UinputDevice::UinputDevice(const char* name) : mName(name) {}
+
+UinputDevice::~UinputDevice() {
+    if (ioctl(mDeviceFd, UI_DEV_DESTROY)) {
+        ALOGE("Error while destroying uinput device: %s", strerror(errno));
+    }
+    mDeviceFd.reset();
+}
+
+void UinputDevice::init() {
+    mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
+    if (mDeviceFd < 0) {
+        FAIL() << "Can't open /dev/uinput :" << strerror(errno);
+    }
+
+    struct uinput_user_dev device = {};
+    strlcpy(device.name, mName, UINPUT_MAX_NAME_SIZE);
+    device.id.bustype = BUS_USB;
+    device.id.vendor = 0x01;
+    device.id.product = 0x01;
+    device.id.version = 1;
+
+    // Using EXPECT instead of ASSERT to allow the device creation to continue even when
+    // some failures are reported when configuring the device.
+    EXPECT_NO_FATAL_FAILURE(configureDevice(mDeviceFd, &device));
+
+    if (write(mDeviceFd, &device, sizeof(device)) < 0) {
+        FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: "
+               << strerror(errno);
+    }
+
+    if (ioctl(mDeviceFd, UI_DEV_CREATE)) {
+        FAIL() << "Error in ioctl : UI_DEV_CREATE: " << strerror(errno);
+    }
+}
+
+void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) {
+    struct input_event event = {};
+    event.type = type;
+    event.code = code;
+    event.value = value;
+    event.time = {}; // uinput ignores the timestamp
+
+    if (write(mDeviceFd, &event, sizeof(input_event)) < 0) {
+        std::string msg = base::StringPrintf("Could not write event %" PRIu16 " %" PRIu16
+                                             " with value %" PRId32 " : %s",
+                                             type, code, value, strerror(errno));
+        ALOGE("%s", msg.c_str());
+        ADD_FAILURE() << msg.c_str();
+    }
+}
+
+// --- UinputKeyboard ---
+
+UinputKeyboard::UinputKeyboard(std::initializer_list<int> keys)
+      : UinputDevice(UinputKeyboard::KEYBOARD_NAME), mKeys(keys.begin(), keys.end()) {}
+
+void UinputKeyboard::configureDevice(int fd, uinput_user_dev* device) {
+    // enable key press/release event
+    if (ioctl(fd, UI_SET_EVBIT, EV_KEY)) {
+        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno);
+    }
+
+    // enable set of KEY events
+    std::for_each(mKeys.begin(), mKeys.end(), [fd](int key) {
+        if (ioctl(fd, UI_SET_KEYBIT, key)) {
+            ADD_FAILURE() << "Error in ioctl : UI_SET_KEYBIT : " << key << " : " << strerror(errno);
+        }
+    });
+
+    // enable synchronization event
+    if (ioctl(fd, UI_SET_EVBIT, EV_SYN)) {
+        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno);
+    }
+}
+
+void UinputKeyboard::pressKey(int key) {
+    if (mKeys.find(key) == mKeys.end()) {
+        ADD_FAILURE() << mName << ": Cannot inject key press: Key not found: " << key;
+    }
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, key, 1));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+void UinputKeyboard::releaseKey(int key) {
+    if (mKeys.find(key) == mKeys.end()) {
+        ADD_FAILURE() << mName << ": Cannot inject key release: Key not found: " << key;
+    }
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, key, 0));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+void UinputKeyboard::pressAndReleaseKey(int key) {
+    EXPECT_NO_FATAL_FAILURE(pressKey(key));
+    EXPECT_NO_FATAL_FAILURE(releaseKey(key));
+}
+
+// --- UinputHomeKey---
+
+UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {}
+
+void UinputHomeKey::pressAndReleaseHomeKey() {
+    EXPECT_NO_FATAL_FAILURE(pressAndReleaseKey(KEY_HOME));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
new file mode 100644
index 0000000..57d9011
--- /dev/null
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UI_TEST_INPUT_UINPUT_INJECTOR_H
+#define _UI_TEST_INPUT_UINPUT_INJECTOR_H
+
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+
+#include <memory>
+
+namespace android {
+
+// This is the factory method that must be used to create a UinputDevice.
+template <class D, class... Ts>
+std::unique_ptr<D> createUinputDevice(Ts... args) {
+    // Using `new` to access non-public constructors.
+    std::unique_ptr<D> dev(new D(&args...));
+    EXPECT_NO_FATAL_FAILURE(dev->init());
+    return dev;
+}
+
+// --- UinputDevice ---
+
+class UinputDevice {
+public:
+    virtual ~UinputDevice();
+
+    inline const char* getName() const { return mName; }
+
+    // Subclasses must either provide a public constructor or must be-friend the factory method.
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+protected:
+    const char* mName;
+
+    UinputDevice(const char* name);
+
+    // Signals which types of events this device supports before it is created.
+    // This must be overridden by subclasses.
+    virtual void configureDevice(int fd, uinput_user_dev* device) = 0;
+
+    void injectEvent(uint16_t type, uint16_t code, int32_t value);
+
+private:
+    base::unique_fd mDeviceFd;
+
+    // This is called once by the factory method createUinputDevice().
+    void init();
+};
+
+// --- UinputKeyboard ---
+
+class UinputKeyboard : public UinputDevice {
+public:
+    static constexpr const char* KEYBOARD_NAME = "Test Keyboard Device";
+
+    // Injects key press and sync.
+    void pressKey(int key);
+    // Injects key release and sync.
+    void releaseKey(int key);
+    // Injects 4 events: key press, sync, key release, and sync.
+    void pressAndReleaseKey(int key);
+
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+protected:
+    UinputKeyboard(std::initializer_list<int> keys = {});
+
+private:
+    void configureDevice(int fd, uinput_user_dev* device) override;
+
+    std::set<int> mKeys;
+};
+
+// --- UinputHomeKey---
+
+// A keyboard device that has a single HOME key.
+class UinputHomeKey : public UinputKeyboard {
+public:
+    // Injects 4 events: key press, sync, key release, and sync.
+    void pressAndReleaseHomeKey();
+
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+private:
+    UinputHomeKey();
+};
+
+} // namespace android
+
+#endif // _UI_TEST_INPUT_UINPUT_INJECTOR_H
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 914a4cb..22a15c6 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -210,12 +210,6 @@
                 registerSensor(new RotationVectorSensor(), !needRotationVector, true);
                 registerSensor(new OrientationSensor(), !needRotationVector, true);
 
-                bool needLinearAcceleration =
-                        (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) != 0;
-
-                registerSensor(new LinearAccelerationSensor(list, count),
-                               !needLinearAcceleration, true);
-
                 // virtual debugging sensors are not for user
                 registerSensor( new CorrectedGyroSensor(list, count), true, true);
                 registerSensor( new GyroDriftSensor(), true, true);
@@ -225,6 +219,11 @@
                 bool needGravitySensor = (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) != 0;
                 registerSensor(new GravitySensor(list, count), !needGravitySensor, true);
 
+                bool needLinearAcceleration =
+                        (virtualSensorsNeeds & (1<<SENSOR_TYPE_LINEAR_ACCELERATION)) != 0;
+                registerSensor(new LinearAccelerationSensor(list, count),
+                               !needLinearAcceleration, true);
+
                 bool needGameRotationVector =
                         (virtualSensorsNeeds & (1<<SENSOR_TYPE_GAME_ROTATION_VECTOR)) != 0;
                 registerSensor(new GameRotationVectorSensor(), !needGameRotationVector, true);
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 3cc803e..e65064b 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -441,6 +441,7 @@
             status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
             if (result != NO_ERROR) {
                 ALOGE("[%s] Timed out waiting on callback", getDebugName());
+                break;
             }
         }
 
@@ -469,6 +470,7 @@
             status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500));
             if (result != NO_ERROR) {
                 ALOGE("[%s] Timed out waiting on callback", getDebugName());
+                break;
             }
         }
 
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 6e0d13a..1188dfe 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -742,6 +742,33 @@
     layer->setInitialValuesForClone(this);
     return layer;
 }
+
+Layer::RoundedCornerState BufferStateLayer::getRoundedCornerState() const {
+    const auto& p = mDrawingParent.promote();
+    if (p != nullptr) {
+        RoundedCornerState parentState = p->getRoundedCornerState();
+        if (parentState.radius > 0) {
+            ui::Transform t = getActiveTransform(getDrawingState());
+            t = t.inverse();
+            parentState.cropRect = t.transform(parentState.cropRect);
+            // The rounded corners shader only accepts 1 corner radius for performance reasons,
+            // but a transform matrix can define horizontal and vertical scales.
+            // Let's take the average between both of them and pass into the shader, practically we
+            // never do this type of transformation on windows anyway.
+            parentState.radius *= (t[0][0] + t[1][1]) / 2.0f;
+            return parentState;
+        }
+    }
+    const float radius = getDrawingState().cornerRadius;
+    const State& s(getDrawingState());
+    if (radius <= 0 || (getActiveWidth(s) == UINT32_MAX && getActiveHeight(s) == UINT32_MAX))
+        return RoundedCornerState();
+    return RoundedCornerState(FloatRect(static_cast<float>(s.active.transform.tx()),
+                                        static_cast<float>(s.active.transform.ty()),
+                                        static_cast<float>(s.active.transform.tx() + s.active.w),
+                                        static_cast<float>(s.active.transform.ty() + s.active.h)),
+                              radius);
+}
 } // namespace android
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 6ee5802..539442a 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -103,6 +103,7 @@
 
     Rect getBufferSize(const State& s) const override;
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
+    Layer::RoundedCornerState getRoundedCornerState() const override;
 
     // -----------------------------------------------------------------------
 
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 40cd3e0..d8ce629 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -40,6 +40,29 @@
 
 namespace android::compositionengine {
 
+// More complex metadata for this layer
+struct GenericLayerMetadataEntry {
+    // True if the metadata may affect the composed result.
+    // See setLayerGenericMetadata in IComposerClient.hal
+    bool mandatory;
+
+    // Byte blob or parcel
+    std::vector<uint8_t> value;
+
+    std::string dumpAsString() const;
+};
+
+inline bool operator==(const GenericLayerMetadataEntry& lhs, const GenericLayerMetadataEntry& rhs) {
+    return lhs.mandatory == rhs.mandatory && lhs.value == rhs.value;
+}
+
+// Defining PrintTo helps with Google Tests.
+inline void PrintTo(const GenericLayerMetadataEntry& v, ::std::ostream* os) {
+    *os << v.dumpAsString();
+}
+
+using GenericLayerMetadataMap = std::unordered_map<std::string, GenericLayerMetadataEntry>;
+
 /*
  * Used by LayerFE::getCompositionState
  */
@@ -115,6 +138,8 @@
     // The appId for this layer
     int appId{0};
 
+    GenericLayerMetadataMap metadata;
+
     /*
      * Per-frame content
      */
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 3e0f803..02e3a45 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -32,6 +32,20 @@
 
 } // namespace
 
+std::string GenericLayerMetadataEntry::dumpAsString() const {
+    using android::base::StringAppendF;
+    std::string out;
+
+    out.append("GenericLayerMetadataEntry{mandatory: ");
+    StringAppendF(&out, "%d", mandatory);
+    out.append(" value: ");
+    for (uint8_t byte : value) {
+        StringAppendF(&out, "0x08%" PRIx8 " ", byte);
+    }
+    out.append("]}");
+    return out;
+}
+
 LayerFECompositionState::~LayerFECompositionState() = default;
 
 void LayerFECompositionState::dump(std::string& out) const {
@@ -65,6 +79,17 @@
     dumpVal(out, "type", type);
     dumpVal(out, "appId", appId);
 
+    if (!metadata.empty()) {
+        out.append("\n      metadata {");
+        for (const auto& [key, entry] : metadata) {
+            out.append("\n           ");
+            out.append(key);
+            out.append("=");
+            out.append(entry.dumpAsString());
+        }
+        out.append("\n      }\n      ");
+    }
+
     dumpVal(out, "composition type", toString(compositionType), compositionType);
 
     out.append("\n      buffer: ");
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index b538d75..3aa7956 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -406,6 +406,14 @@
         ALOGE("[%s] Failed to set info %s (%d)", getLayerFE().getDebugName(),
               to_string(error).c_str(), static_cast<int32_t>(error));
     }
+
+    for (const auto& [name, entry] : outputIndependentState.metadata) {
+        if (auto error = hwcLayer->setLayerGenericMetadata(name, entry.mandatory, entry.value);
+            error != HWC2::Error::None) {
+            ALOGE("[%s] Failed to set generic metadata %s %s (%d)", getLayerFE().getDebugName(),
+                  name.c_str(), to_string(error).c_str(), static_cast<int32_t>(error));
+        }
+    }
 }
 
 void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) {
@@ -661,4 +669,3 @@
 
 } // namespace impl
 } // namespace android::compositionengine
-
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index a51cc67..be89c1a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -65,6 +65,8 @@
     MOCK_METHOD2(setInfo, Error(uint32_t, uint32_t));
 
     MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
+    MOCK_METHOD3(setLayerGenericMetadata,
+                 Error(const std::string&, bool, const std::vector<uint8_t>&));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 502a33f..9995ce1 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -36,7 +36,7 @@
     HWComposer();
     ~HWComposer() override;
 
-    MOCK_METHOD2(registerCallback, void(HWC2::ComposerCallback*, int32_t));
+    MOCK_METHOD2(setConfiguration, void(HWC2::ComposerCallback*, int32_t));
     MOCK_CONST_METHOD3(getDisplayIdentificationData,
                        bool(hwc2_display_t, uint8_t*, DisplayIdentificationData*));
     MOCK_CONST_METHOD1(hasCapability, bool(HWC2::Capability));
@@ -96,6 +96,8 @@
     MOCK_METHOD2(setAutoLowLatencyMode, status_t(DisplayId, bool));
     MOCK_METHOD2(getSupportedContentTypes, status_t(DisplayId, std::vector<HWC2::ContentType>*));
     MOCK_METHOD2(setContentType, status_t(DisplayId, HWC2::ContentType));
+    MOCK_CONST_METHOD0(getSupportedLayerGenericMetadata,
+                       const std::unordered_map<std::string, bool>&());
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 963062f..1b5617c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -625,6 +625,8 @@
     static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71);
     static constexpr int kSupportedPerFrameMetadata = 101;
     static constexpr int kExpectedHwcSlot = 0;
+    static constexpr bool kLayerGenericMetadata1Mandatory = true;
+    static constexpr bool kLayerGenericMetadata2Mandatory = true;
 
     static const half4 kColor;
     static const Rect kDisplayFrame;
@@ -635,6 +637,10 @@
     static native_handle_t* kSidebandStreamHandle;
     static const sp<GraphicBuffer> kBuffer;
     static const sp<Fence> kFence;
+    static const std::string kLayerGenericMetadata1Key;
+    static const std::vector<uint8_t> kLayerGenericMetadata1Value;
+    static const std::string kLayerGenericMetadata2Key;
+    static const std::vector<uint8_t> kLayerGenericMetadata2Value;
 
     OutputLayerWriteStateToHWCTest() {
         auto& outputLayerState = mOutputLayer.editState();
@@ -669,6 +675,13 @@
     // Some tests may need to simulate unsupported HWC calls
     enum class SimulateUnsupported { None, ColorTransform };
 
+    void includeGenericLayerMetadataInState() {
+        mLayerFEState.metadata[kLayerGenericMetadata1Key] = {kLayerGenericMetadata1Mandatory,
+                                                             kLayerGenericMetadata1Value};
+        mLayerFEState.metadata[kLayerGenericMetadata2Key] = {kLayerGenericMetadata2Mandatory,
+                                                             kLayerGenericMetadata2Value};
+    }
+
     void expectGeometryCommonCalls() {
         EXPECT_CALL(*mHwcLayer, setDisplayFrame(kDisplayFrame)).WillOnce(Return(kError));
         EXPECT_CALL(*mHwcLayer, setSourceCrop(kSourceCrop)).WillOnce(Return(kError));
@@ -720,6 +733,18 @@
         EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, kBuffer, kFence));
     }
 
+    void expectGenericLayerMetadataCalls() {
+        // Note: Can be in any order.
+        EXPECT_CALL(*mHwcLayer,
+                    setLayerGenericMetadata(kLayerGenericMetadata1Key,
+                                            kLayerGenericMetadata1Mandatory,
+                                            kLayerGenericMetadata1Value));
+        EXPECT_CALL(*mHwcLayer,
+                    setLayerGenericMetadata(kLayerGenericMetadata2Key,
+                                            kLayerGenericMetadata2Mandatory,
+                                            kLayerGenericMetadata2Value));
+    }
+
     std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()};
     StrictMock<mock::DisplayColorProfile> mDisplayColorProfile;
 };
@@ -739,6 +764,13 @@
         reinterpret_cast<native_handle_t*>(1031);
 const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
 const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
+const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key =
+        "com.example.metadata.1";
+const std::vector<uint8_t> OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Value{{1, 2, 3}};
+const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata2Key =
+        "com.example.metadata.2";
+const std::vector<uint8_t> OutputLayerWriteStateToHWCTest::kLayerGenericMetadata2Value{
+        {4, 5, 6, 7}};
 
 TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) {
     EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
@@ -862,6 +894,30 @@
     mOutputLayer.writeStateToHWC(false);
 }
 
+TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    includeGenericLayerMetadataInState();
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectGenericLayerMetadataCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+    mOutputLayer.writeStateToHWC(true);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    includeGenericLayerMetadataInState();
+
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
 /*
  * OutputLayer::writeCursorPositionToHWC()
  */
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 41e7879..e8bf2d8 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -734,12 +734,11 @@
 
 Layer::Layer(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities,
              hwc2_display_t displayId, hwc2_layer_t layerId)
-  : mComposer(composer),
-    mCapabilities(capabilities),
-    mDisplayId(displayId),
-    mId(layerId),
-    mColorMatrix(android::mat4())
-{
+      : mComposer(composer),
+        mCapabilities(capabilities),
+        mDisplayId(displayId),
+        mId(layerId),
+        mColorMatrix(android::mat4()) {
     ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, displayId);
 }
 
@@ -979,6 +978,13 @@
     return error;
 }
 
+// Composer HAL 2.4
+Error Layer::setLayerGenericMetadata(const std::string& name, bool mandatory,
+                                     const std::vector<uint8_t>& value) {
+    auto intError = mComposer.setLayerGenericMetadata(mDisplayId, mId, name, mandatory, value);
+    return static_cast<Error>(intError);
+}
+
 } // namespace impl
 } // namespace HWC2
 
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index e7cf5ff..5804903 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -374,6 +374,10 @@
 
     // Composer HAL 2.3
     [[clang::warn_unused_result]] virtual Error setColorTransform(const android::mat4& matrix) = 0;
+
+    // Composer HAL 2.4
+    [[clang::warn_unused_result]] virtual Error setLayerGenericMetadata(
+            const std::string& name, bool mandatory, const std::vector<uint8_t>& value) = 0;
 };
 
 namespace impl {
@@ -382,8 +386,7 @@
 
 class Layer : public HWC2::Layer {
 public:
-    Layer(android::Hwc2::Composer& composer,
-          const std::unordered_set<Capability>& capabilities,
+    Layer(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities,
           hwc2_display_t displayId, hwc2_layer_t layerId);
     ~Layer() override;
 
@@ -412,6 +415,10 @@
     // Composer HAL 2.3
     Error setColorTransform(const android::mat4& matrix) override;
 
+    // Composer HAL 2.4
+    Error setLayerGenericMetadata(const std::string& name, bool mandatory,
+                                  const std::vector<uint8_t>& value) override;
+
 private:
     // These are references to data owned by HWC2::Device, which will outlive
     // this HWC2::Layer, so these references are guaranteed to be valid for
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 153cfe7..bb6f696 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -109,7 +109,6 @@
             android::Hwc2::Display display, int64_t timestamp,
             android::Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override {
         if (mVsyncSwitchingSupported) {
-            // TODO(b/140201379): use vsyncPeriodNanos in the new DispSync
             mCallback->onVsyncReceived(mSequenceId, display, timestamp,
                                        std::make_optional(vsyncPeriodNanos));
         } else {
@@ -149,20 +148,20 @@
 namespace impl {
 
 HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer) : mComposer(std::move(composer)) {
-    loadCapabilities();
 }
 
 HWComposer::HWComposer(const std::string& composerServiceName)
       : mComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {
-    loadCapabilities();
 }
 
 HWComposer::~HWComposer() {
     mDisplayData.clear();
 }
 
-void HWComposer::registerCallback(HWC2::ComposerCallback* callback,
-                                  int32_t sequenceId) {
+void HWComposer::setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) {
+    loadCapabilities();
+    loadLayerMetadataSupport();
+
     if (mRegisteredCallback) {
         ALOGW("Callback already registered. Ignored extra registration attempt.");
         return;
@@ -579,12 +578,13 @@
 
 sp<Fence> HWComposer::getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
-    auto displayFences = mDisplayData.at(displayId).releaseFences;
-    if (displayFences.count(layer) == 0) {
+    const auto& displayFences = mDisplayData.at(displayId).releaseFences;
+    auto fence = displayFences.find(layer);
+    if (fence == displayFences.end()) {
         ALOGV("getLayerReleaseFence: Release fence not found");
         return Fence::NO_FENCE;
     }
-    return displayFences[layer];
+    return fence->second;
 }
 
 status_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) {
@@ -871,6 +871,10 @@
     return NO_ERROR;
 }
 
+const std::unordered_map<std::string, bool>& HWComposer::getSupportedLayerGenericMetadata() const {
+    return mSupportedLayerGenericMetadata;
+}
+
 void HWComposer::dump(std::string& result) const {
     result.append(mComposer->dumpDebugInfo());
 }
@@ -946,6 +950,22 @@
     }
 }
 
+void HWComposer::loadLayerMetadataSupport() {
+    mSupportedLayerGenericMetadata.clear();
+
+    std::vector<Hwc2::IComposerClient::LayerGenericMetadataKey> supportedMetadataKeyInfo;
+    const auto error = mComposer->getLayerGenericMetadataKeys(&supportedMetadataKeyInfo);
+    if (error != hardware::graphics::composer::V2_4::Error::NONE) {
+        ALOGE("%s: %s failed: %s (%d)", __FUNCTION__, "getLayerGenericMetadataKeys",
+              toString(error).c_str(), static_cast<int32_t>(error));
+        return;
+    }
+
+    for (const auto& [name, mandatory] : supportedMetadataKeyInfo) {
+        mSupportedLayerGenericMetadata.emplace(name, mandatory);
+    }
+}
+
 uint32_t HWComposer::getMaxVirtualDisplayCount() const {
     return mComposer->getMaxVirtualDisplayCount();
 }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index effe43b..76e831b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -55,11 +55,16 @@
 class Output;
 } // namespace compositionengine
 
+struct KnownHWCGenericLayerMetadata {
+    const char* name;
+    const uint32_t id;
+};
+
 class HWComposer {
 public:
     virtual ~HWComposer();
 
-    virtual void registerCallback(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
+    virtual void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
 
     virtual bool getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
                                               DisplayIdentificationData* outData) const = 0;
@@ -194,6 +199,8 @@
     virtual status_t getSupportedContentTypes(
             DisplayId displayId, std::vector<HWC2::ContentType>* outSupportedContentTypes) = 0;
     virtual status_t setContentType(DisplayId displayId, HWC2::ContentType contentType) = 0;
+    virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
+            const = 0;
 
     // for debugging ----------------------------------------------------------
     virtual void dump(std::string& out) const = 0;
@@ -217,7 +224,7 @@
 
     ~HWComposer() override;
 
-    void registerCallback(HWC2::ComposerCallback* callback, int32_t sequenceId) override;
+    void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) override;
 
     bool getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
                                       DisplayIdentificationData* outData) const override;
@@ -329,6 +336,8 @@
                                       std::vector<HWC2::ContentType>*) override;
     status_t setContentType(DisplayId displayId, HWC2::ContentType) override;
 
+    const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const override;
+
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
 
@@ -351,6 +360,7 @@
 
     std::optional<DisplayIdentificationInfo> onHotplugConnect(hwc2_display_t hwcDisplayId);
     void loadCapabilities();
+    void loadLayerMetadataSupport();
     uint32_t getMaxVirtualDisplayCount() const;
 
     struct DisplayData {
@@ -379,6 +389,7 @@
 
     std::unique_ptr<android::Hwc2::Composer> mComposer;
     std::unordered_set<HWC2::Capability> mCapabilities;
+    std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata;
     bool mRegisteredCallback = false;
 
     std::unordered_map<hwc2_display_t, DisplayId> mPhysicalDisplayIdMap;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 5073a92..da26a37 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -80,7 +80,6 @@
         mName(args.name),
         mClientRef(args.client),
         mWindowType(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0)) {
-
     uint32_t layerFlags = 0;
     if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
     if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
@@ -476,6 +475,26 @@
 
     compositionState->type = type;
     compositionState->appId = appId;
+
+    compositionState->metadata.clear();
+    const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata();
+    for (const auto& [key, mandatory] : supportedMetadata) {
+        const auto& genericLayerMetadataCompatibilityMap =
+                mFlinger->getGenericLayerMetadataKeyMap();
+        auto compatIter = genericLayerMetadataCompatibilityMap.find(key);
+        if (compatIter == std::end(genericLayerMetadataCompatibilityMap)) {
+            continue;
+        }
+        const uint32_t id = compatIter->second;
+
+        auto it = drawingState.metadata.mMap.find(id);
+        if (it == std::end(drawingState.metadata.mMap)) {
+            continue;
+        }
+
+        compositionState->metadata
+                .emplace(key, compositionengine::GenericLayerMetadataEntry{mandatory, it->second});
+    }
 }
 
 void Layer::preparePerFrameCompositionState() {
@@ -1306,6 +1325,9 @@
 }
 
 bool Layer::setFrameRate(FrameRate frameRate) {
+    if (!mFlinger->useFrameRateApi) {
+        return false;
+    }
     if (mCurrentState.frameRate == frameRate) {
         return false;
     }
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 1689881..37ae340 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -716,7 +716,7 @@
     // corner definition and converting it into current layer's coordinates.
     // As of now, only 1 corner radius per display list is supported. Subsequent ones will be
     // ignored.
-    RoundedCornerState getRoundedCornerState() const;
+    virtual RoundedCornerState getRoundedCornerState() const;
 
     renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const;
 
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index ca41608..809a0e5 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -542,7 +542,8 @@
     resetLocked();
 }
 
-bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
+bool DispSync::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> /*hwcVsyncPeriod*/,
+                               bool* periodFlushed) {
     Mutex::Autolock lock(mMutex);
 
     ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp));
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
index c6aadbb..2d9afc9 100644
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -49,7 +49,8 @@
     virtual void reset() = 0;
     virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
     virtual void beginResync() = 0;
-    virtual bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) = 0;
+    virtual bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                                 bool* periodFlushed) = 0;
     virtual void endResync() = 0;
     virtual void setPeriod(nsecs_t period) = 0;
     virtual nsecs_t getPeriod() = 0;
@@ -125,7 +126,8 @@
     // down the DispSync model, and false otherwise.
     // periodFlushed will be set to true if mPendingPeriod is flushed to
     // mIntendedPeriod, and false otherwise.
-    bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) override;
+    bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                         bool* periodFlushed) override;
     void endResync() override;
 
     // The setPeriod method sets the vsync event model's period to a specific
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 14e3ec6..acab5a6 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -335,6 +335,11 @@
     mCondition.notify_all();
 }
 
+size_t EventThread::getEventThreadConnectionCount() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mDisplayEventConnections.size();
+}
+
 void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
     DisplayEventConsumers consumers;
 
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 641b2a5..466234d 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -16,7 +16,12 @@
 
 #pragma once
 
+#include <android-base/thread_annotations.h>
+#include <gui/DisplayEventReceiver.h>
+#include <gui/IDisplayEventConnection.h>
+#include <private/gui/BitTube.h>
 #include <sys/types.h>
+#include <utils/Errors.h>
 
 #include <condition_variable>
 #include <cstdint>
@@ -26,13 +31,6 @@
 #include <thread>
 #include <vector>
 
-#include <android-base/thread_annotations.h>
-
-#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
-#include <private/gui/BitTube.h>
-
-#include <utils/Errors.h>
 #include "HwcStrongTypes.h"
 
 // ---------------------------------------------------------------------------
@@ -134,6 +132,9 @@
     // Usage of this method assumes that only the primary internal display
     // supports multiple display configurations.
     virtual void requestLatestConfig() = 0;
+
+    // Retrieves the number of event connections tracked by this EventThread.
+    virtual size_t getEventThreadConnectionCount() = 0;
 };
 
 namespace impl {
@@ -168,6 +169,8 @@
 
     void setPhaseOffset(nsecs_t phaseOffset) override;
 
+    size_t getEventThreadConnectionCount() override;
+
 private:
     friend EventThreadTest;
 
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 9aada11..b313777 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -66,7 +66,6 @@
     ATRACE_INT(tag.c_str(), fps);
     ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
 }
-
 } // namespace
 
 LayerHistory::LayerHistory()
@@ -102,23 +101,6 @@
 
     partitionLayers(now);
 
-    // Find the maximum refresh rate among recently active layers.
-    for (const auto& [activeLayer, info] : activeLayers()) {
-        const bool recent = info->isRecentlyActive(now);
-
-        if (recent || CC_UNLIKELY(mTraceEnabled)) {
-            const float refreshRate = info->getRefreshRate(now);
-            if (recent && refreshRate > 0.0f) {
-                if (const auto layer = activeLayer.promote(); layer) {
-                    const int32_t priority = layer->getFrameRateSelectionPriority();
-                    // TODO(b/142507166): This is where the scoring algorithm should live.
-                    // Layers should be organized by priority
-                    ALOGD("Layer has priority: %d", priority);
-                }
-            }
-        }
-    }
-
     LayerHistory::Summary summary;
     for (const auto& [weakLayer, info] : activeLayers()) {
         const bool recent = info->isRecentlyActive(now);
@@ -196,6 +178,5 @@
 
     mActiveLayersEnd = 0;
 }
-
 } // namespace android::scheduler::impl
 
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index ce085f4..6ef6ce4 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -79,7 +79,6 @@
 
     ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
 }
-
 } // namespace
 
 LayerHistoryV2::LayerHistoryV2()
@@ -124,6 +123,10 @@
             continue;
         }
 
+        // TODO(b/144307188): This needs to be plugged into layer summary as
+        //  an additional parameter.
+        ALOGV("Layer has priority: %d", strong->getFrameRateSelectionPriority());
+
         const bool recent = info->isRecentlyActive(now);
         if (recent) {
             const auto [type, refreshRate] = info->getRefreshRate(now);
@@ -212,5 +215,4 @@
 
     mActiveLayersEnd = 0;
 }
-
 } // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 8202515..d1de737 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -194,9 +194,22 @@
         }
     }
 
-    float max = 0;
+    // Now that we scored all the refresh rates we need to pick the one that got the highest score.
+    // In case of a tie we will pick the higher refresh rate if any of the layers wanted Max,
+    // or the lower otherwise.
+    const RefreshRate* bestRefreshRate = maxVoteLayers > 0
+            ? getBestRefreshRate(scores.rbegin(), scores.rend())
+            : getBestRefreshRate(scores.begin(), scores.end());
+
+    return bestRefreshRate == nullptr ? *mCurrentRefreshRate : *bestRefreshRate;
+}
+
+template <typename Iter>
+const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
     const RefreshRate* bestRefreshRate = nullptr;
-    for (const auto [refreshRate, score] : scores) {
+    float max = 0;
+    for (auto i = begin; i != end; ++i) {
+        const auto [refreshRate, score] = *i;
         ALOGV("%s scores %.2f", refreshRate->name.c_str(), score);
 
         ATRACE_INT(refreshRate->name.c_str(), round<int>(score * 100));
@@ -207,7 +220,7 @@
         }
     }
 
-    return bestRefreshRate == nullptr ? *mCurrentRefreshRate : *bestRefreshRate;
+    return bestRefreshRate;
 }
 
 const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const {
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index e5bb557..1132a8c 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -182,6 +182,12 @@
             const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
             std::vector<const RefreshRate*>* outRefreshRates);
 
+    // Returns the refresh rate with the highest score in the collection specified from begin
+    // to end. If there are more than one with the same highest refresh rate, the first one is
+    // returned.
+    template <typename Iter>
+    const RefreshRate* getBestRefreshRate(Iter begin, Iter end) const;
+
     // The list of refresh rates, indexed by display config ID. This must not change after this
     // object is initialized.
     AllRefreshRatesMapType mRefreshRates;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 94791eb..71ac90e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -108,13 +108,10 @@
         mUseContentDetectionV2(useContentDetectionV2) {
     using namespace sysprop;
 
-    if (property_get_bool("debug.sf.use_content_detection_for_refresh_rate", 0) ||
-        use_content_detection_for_refresh_rate(false)) {
-        if (mUseContentDetectionV2) {
-            mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
-        } else {
-            mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
-        }
+    if (mUseContentDetectionV2) {
+        mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+    } else {
+        mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
     }
 
     const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
@@ -233,6 +230,11 @@
     mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod);
 }
 
+size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
+    RETURN_IF_INVALID_HANDLE(handle, 0);
+    return mConnections[handle].thread->getEventThreadConnectionCount();
+}
+
 void Scheduler::dump(ConnectionHandle handle, std::string& result) const {
     RETURN_IF_INVALID_HANDLE(handle);
     mConnections.at(handle).thread->dump(result);
@@ -341,13 +343,15 @@
     }
 }
 
-void Scheduler::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
+void Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                                bool* periodFlushed) {
     bool needsHwVsync = false;
     *periodFlushed = false;
     { // Scope for the lock
         std::lock_guard<std::mutex> lock(mHWVsyncLock);
         if (mPrimaryHWVsyncEnabled) {
-            needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodFlushed);
+            needsHwVsync =
+                    mPrimaryDispSync->addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
         }
     }
 
@@ -438,7 +442,7 @@
         mFeatures.contentDetection =
                 !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
 
-        newConfigId = calculateRefreshRateType();
+        newConfigId = calculateRefreshRateConfigIndexType();
         if (mFeatures.configId == newConfigId) {
             return;
         }
@@ -531,8 +535,6 @@
     using base::StringAppendF;
     const char* const states[] = {"off", "on"};
 
-    StringAppendF(&result, "+  Content detection: %s\n", states[mLayerHistory != nullptr]);
-
     StringAppendF(&result, "+  Idle timer: %s\n",
                   mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
     StringAppendF(&result, "+  Touch timer: %s\n\n",
@@ -549,7 +551,7 @@
             return;
         }
         *currentState = newState;
-        newConfigId = calculateRefreshRateType();
+        newConfigId = calculateRefreshRateConfigIndexType();
         if (mFeatures.configId == newConfigId) {
             return;
         }
@@ -563,6 +565,7 @@
 }
 
 bool Scheduler::layerHistoryHasClientSpecifiedFrameRate() {
+    // Traverse all the layers to see if any of them requested frame rate.
     for (const auto& layer : mFeatures.contentRequirements) {
         if (layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::ExplicitDefault ||
             layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::ExplicitExactOrMultiple) {
@@ -573,10 +576,9 @@
     return false;
 }
 
-HwcConfigIndexType Scheduler::calculateRefreshRateType() {
+HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() {
     // This block of the code checks whether any layers used the SetFrameRate API. If they have,
-    // their request should be honored regardless of whether the device has refresh rate switching
-    // turned off.
+    // their request should be honored depending on other active layers.
     if (layerHistoryHasClientSpecifiedFrameRate()) {
         if (!mUseContentDetectionV2) {
             return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements)
@@ -587,23 +589,23 @@
         }
     }
 
-    // If the layer history doesn't have the frame rate specified, use the old path. NOTE:
-    // if we remove the kernel idle timer, and use our internal idle timer, this code will have to
-    // be refactored.
-    // If Display Power is not in normal operation we want to be in performance mode.
-    // When coming back to normal mode, a grace period is given with DisplayPowerTimer
+    // If the layer history doesn't have the frame rate specified, check for other features and
+    // honor them. NOTE: If we remove the kernel idle timer, and use our internal idle timer, this
+    // code will have to be refactored. If Display Power is not in normal operation we want to be in
+    // performance mode. When coming back to normal mode, a grace period is given with
+    // DisplayPowerTimer.
     if (mDisplayPowerTimer &&
         (!mFeatures.isDisplayPowerStateNormal ||
          mFeatures.displayPowerTimer == TimerState::Reset)) {
         return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
     }
 
-    // As long as touch is active we want to be in performance mode
+    // As long as touch is active we want to be in performance mode.
     if (mTouchTimer && mFeatures.touch == TouchState::Active) {
         return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
     }
 
-    // If timer has expired as it means there is no new content on the screen
+    // If timer has expired as it means there is no new content on the screen.
     if (mIdleTimer && mFeatures.idleTimer == TimerState::Expired) {
         return mRefreshRateConfigs.getMinRefreshRateByPolicy().configId;
     }
@@ -611,7 +613,7 @@
     if (!mUseContentDetectionV2) {
         // If content detection is off we choose performance as we don't know the content fps.
         if (mFeatures.contentDetection == ContentDetectionState::Off) {
-            // TODO(b/148428554): Be careful to not always call this.
+            // NOTE: V1 always calls this, but this is not a default behavior for V2.
             return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
         }
 
@@ -633,7 +635,7 @@
     std::lock_guard<std::mutex> lock(mFeatureStateLock);
     // Make sure that the default config ID is first updated, before returned.
     if (mFeatures.configId.has_value()) {
-        mFeatures.configId = calculateRefreshRateType();
+        mFeatures.configId = calculateRefreshRateConfigIndexType();
     }
     return mFeatures.configId;
 }
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 01062f8..81051be 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -109,7 +109,8 @@
 
     // Passes a vsync sample to DispSync. periodFlushed will be true if
     // DispSync detected that the vsync period changed, and false otherwise.
-    void addResyncSample(nsecs_t timestamp, bool* periodFlushed);
+    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();
@@ -144,6 +145,8 @@
     // Notifies the scheduler when the display size has changed. Called from SF's main thread
     void onPrimaryDisplayAreaChanged(uint32_t displayArea);
 
+    size_t getEventThreadConnectionCount(ConnectionHandle handle);
+
 private:
     friend class TestableScheduler;
 
@@ -177,7 +180,10 @@
 
     void setVsyncPeriod(nsecs_t period);
 
-    HwcConfigIndexType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
+    // This function checks whether individual features that are affecting the refresh rate
+    // selection were initialized, prioritizes them, and calculates the HwcConfigIndexType
+    // for the suggested refresh rate.
+    HwcConfigIndexType calculateRefreshRateConfigIndexType() REQUIRES(mFeatureStateLock);
 
     bool layerHistoryHasClientSpecifiedFrameRate() REQUIRES(mFeatureStateLock);
 
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index b467f24..399da19 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -53,15 +53,15 @@
 }
 
 inline size_t VSyncPredictor::next(int i) const {
-    return (i + 1) % timestamps.size();
+    return (i + 1) % mTimestamps.size();
 }
 
 bool VSyncPredictor::validate(nsecs_t timestamp) const {
-    if (lastTimestampIndex < 0 || timestamps.empty()) {
+    if (mLastTimestampIndex < 0 || mTimestamps.empty()) {
         return true;
     }
 
-    auto const aValidTimestamp = timestamps[lastTimestampIndex];
+    auto const aValidTimestamp = mTimestamps[mLastTimestampIndex];
     auto const percent = (timestamp - aValidTimestamp) % mIdealPeriod * kMaxPercent / mIdealPeriod;
     return percent < kOutlierTolerancePercent || percent > (kMaxPercent - kOutlierTolerancePercent);
 }
@@ -79,15 +79,15 @@
         return false;
     }
 
-    if (timestamps.size() != kHistorySize) {
-        timestamps.push_back(timestamp);
-        lastTimestampIndex = next(lastTimestampIndex);
+    if (mTimestamps.size() != kHistorySize) {
+        mTimestamps.push_back(timestamp);
+        mLastTimestampIndex = next(mLastTimestampIndex);
     } else {
-        lastTimestampIndex = next(lastTimestampIndex);
-        timestamps[lastTimestampIndex] = timestamp;
+        mLastTimestampIndex = next(mLastTimestampIndex);
+        mTimestamps[mLastTimestampIndex] = timestamp;
     }
 
-    if (timestamps.size() < kMinimumSamplesForPrediction) {
+    if (mTimestamps.size() < kMinimumSamplesForPrediction) {
         mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
         return true;
     }
@@ -107,11 +107,11 @@
     //
     // intercept = mean(Y) - slope * mean(X)
     //
-    std::vector<nsecs_t> vsyncTS(timestamps.size());
-    std::vector<nsecs_t> ordinals(timestamps.size());
+    std::vector<nsecs_t> vsyncTS(mTimestamps.size());
+    std::vector<nsecs_t> ordinals(mTimestamps.size());
 
     // normalizing to the oldest timestamp cuts down on error in calculating the intercept.
-    auto const oldest_ts = *std::min_element(timestamps.begin(), timestamps.end());
+    auto const oldest_ts = *std::min_element(mTimestamps.begin(), mTimestamps.end());
     auto it = mRateMap.find(mIdealPeriod);
     auto const currentPeriod = std::get<0>(it->second);
     // TODO (b/144707443): its important that there's some precision in the mean of the ordinals
@@ -120,10 +120,10 @@
     //                     scheduler::utils::calculate_mean to have a fixed point fractional part.
     static constexpr int kScalingFactor = 10;
 
-    for (auto i = 0u; i < timestamps.size(); i++) {
-        traceInt64If("VSP-ts", timestamps[i]);
+    for (auto i = 0u; i < mTimestamps.size(); i++) {
+        traceInt64If("VSP-ts", mTimestamps[i]);
 
-        vsyncTS[i] = timestamps[i] - oldest_ts;
+        vsyncTS[i] = mTimestamps[i] - oldest_ts;
         ordinals[i] = ((vsyncTS[i] + (currentPeriod / 2)) / currentPeriod) * kScalingFactor;
     }
 
@@ -143,12 +143,20 @@
 
     if (CC_UNLIKELY(bottom == 0)) {
         it->second = {mIdealPeriod, 0};
+        clearTimestamps();
         return false;
     }
 
     nsecs_t const anticipatedPeriod = top / bottom * kScalingFactor;
     nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor);
 
+    auto const percent = std::abs(anticipatedPeriod - mIdealPeriod) * kMaxPercent / mIdealPeriod;
+    if (percent >= kOutlierTolerancePercent) {
+        it->second = {mIdealPeriod, 0};
+        clearTimestamps();
+        return false;
+    }
+
     traceInt64If("VSP-period", anticipatedPeriod);
     traceInt64If("VSP-intercept", intercept);
 
@@ -164,14 +172,14 @@
 
     auto const [slope, intercept] = getVSyncPredictionModel(lk);
 
-    if (timestamps.empty()) {
+    if (mTimestamps.empty()) {
         traceInt64If("VSP-mode", 1);
         auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint;
         auto const numPeriodsOut = ((timePoint - knownTimestamp) / mIdealPeriod) + 1;
         return knownTimestamp + numPeriodsOut * mIdealPeriod;
     }
 
-    auto const oldest = *std::min_element(timestamps.begin(), timestamps.end());
+    auto const oldest = *std::min_element(mTimestamps.begin(), mTimestamps.end());
 
     // See b/145667109, the ordinal calculation must take into account the intercept.
     auto const zeroPoint = oldest + intercept;
@@ -225,10 +233,10 @@
 }
 
 void VSyncPredictor::clearTimestamps() {
-    if (!timestamps.empty()) {
-        mKnownTimestamp = *std::max_element(timestamps.begin(), timestamps.end());
-        timestamps.clear();
-        lastTimestampIndex = 0;
+    if (!mTimestamps.empty()) {
+        mKnownTimestamp = *std::max_element(mTimestamps.begin(), mTimestamps.end());
+        mTimestamps.clear();
+        mLastTimestampIndex = 0;
     }
 }
 
@@ -236,11 +244,11 @@
     using namespace std::literals::chrono_literals;
     std::lock_guard<std::mutex> lk(mMutex);
     bool needsMoreSamples = true;
-    if (timestamps.size() >= kMinimumSamplesForPrediction) {
+    if (mTimestamps.size() >= kMinimumSamplesForPrediction) {
         nsecs_t constexpr aLongTime =
                 std::chrono::duration_cast<std::chrono::nanoseconds>(500ms).count();
-        if (!(lastTimestampIndex < 0 || timestamps.empty())) {
-            auto const lastTimestamp = timestamps[lastTimestampIndex];
+        if (!(mLastTimestampIndex < 0 || mTimestamps.empty())) {
+            auto const lastTimestamp = mTimestamps[mLastTimestampIndex];
             needsMoreSamples = !((lastTimestamp + aLongTime) > now);
         }
     }
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 532fe9e..ef1d88a 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -83,8 +83,8 @@
 
     std::unordered_map<nsecs_t, std::tuple<nsecs_t, nsecs_t>> mutable mRateMap GUARDED_BY(mMutex);
 
-    int lastTimestampIndex GUARDED_BY(mMutex) = 0;
-    std::vector<nsecs_t> timestamps GUARDED_BY(mMutex);
+    int mLastTimestampIndex GUARDED_BY(mMutex) = 0;
+    std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
 };
 
 } // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 70e4760..949ba4c 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -98,6 +98,9 @@
 
         {
             std::lock_guard<std::mutex> lk(mMutex);
+            if (mStopped) {
+                return;
+            }
             auto const schedule_result = mRegistration.schedule(calculateWorkload(), vsynctime);
             LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
                                 "Error rescheduling callback: rc %X", schedule_result);
@@ -229,24 +232,33 @@
 
 void VSyncReactor::endResync() {}
 
-bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp) {
-    if (!mLastHwVsync || !mPeriodConfirmationInProgress) {
+bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> HwcVsyncPeriod) {
+    if (!mPeriodConfirmationInProgress) {
         return false;
     }
-    auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
 
+    if (!mLastHwVsync && !HwcVsyncPeriod) {
+        return false;
+    }
+
+    auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
     static constexpr int allowancePercent = 10;
     static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
     auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
+    if (HwcVsyncPeriod) {
+        return std::abs(*HwcVsyncPeriod - period) < allowance;
+    }
+
     auto const distance = vsync_timestamp - *mLastHwVsync;
     return std::abs(distance - period) < allowance;
 }
 
-bool VSyncReactor::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
+bool VSyncReactor::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                                   bool* periodFlushed) {
     assert(periodFlushed);
 
     std::lock_guard<std::mutex> lk(mMutex);
-    if (periodConfirmed(timestamp)) {
+    if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
         if (mPeriodTransitioningTo) {
             mTracker->setPeriod(*mPeriodTransitioningTo);
             for (auto& entry : mCallbacks) {
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 5b79f35..aa8a38d 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -49,7 +49,8 @@
 
     // TODO: (b/145626181) remove begin,endResync functions from DispSync i/f when possible.
     void beginResync() final;
-    bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) final;
+    bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                         bool* periodFlushed) final;
     void endResync() final;
 
     status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
@@ -65,7 +66,8 @@
     void updateIgnorePresentFencesInternal() REQUIRES(mMutex);
     void startPeriodTransition(nsecs_t newPeriod) REQUIRES(mMutex);
     void endPeriodTransition() REQUIRES(mMutex);
-    bool periodConfirmed(nsecs_t vsync_timestamp) REQUIRES(mMutex);
+    bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod)
+            REQUIRES(mMutex);
 
     std::unique_ptr<Clock> const mClock;
     std::unique_ptr<VSyncTracker> const mTracker;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ff4a652..745736f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -57,6 +57,7 @@
 #include <gui/IDisplayEventConnection.h>
 #include <gui/IProducerListener.h>
 #include <gui/LayerDebugInfo.h>
+#include <gui/LayerMetadata.h>
 #include <gui/Surface.h>
 #include <input/IInputFlinger.h>
 #include <renderengine/RenderEngine.h>
@@ -228,6 +229,7 @@
 ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
 Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
 ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
+bool SurfaceFlinger::useFrameRateApi;
 
 std::string getHwcServiceName() {
     char value[PROPERTY_VALUE_MAX] = {};
@@ -387,6 +389,8 @@
         // for production purposes later on.
         setenv("TREBLE_TESTING_OVERRIDE", "true", true);
     }
+
+    useFrameRateApi = use_frame_rate_api(true);
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -619,7 +623,7 @@
     LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
             "Starting with vr flinger active is not currently supported.");
     mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
-    mCompositionEngine->getHwComposer().registerCallback(this, getBE().mComposerSequenceId);
+    mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId);
     // Process any initial hotplug and resulting display changes.
     processDisplayHotplugEventsLocked();
     const auto display = getDefaultDisplayDeviceLocked();
@@ -1499,11 +1503,9 @@
 
 void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
                                      int64_t timestamp,
-                                     std::optional<hwc2_vsync_period_t> /*vsyncPeriod*/) {
+                                     std::optional<hwc2_vsync_period_t> vsyncPeriod) {
     ATRACE_NAME("SF onVsync");
 
-    // TODO(b/140201379): use vsyncPeriod in the new DispSync
-
     Mutex::Autolock lock(mStateLock);
     // Ignore any vsyncs from a previous hardware composer.
     if (sequenceId != getBE().mComposerSequenceId) {
@@ -1520,7 +1522,7 @@
     }
 
     bool periodFlushed = false;
-    mScheduler->addResyncSample(timestamp, &periodFlushed);
+    mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
     if (periodFlushed) {
         mVSyncModulator->onRefreshRateChangeCompleted();
     }
@@ -1681,7 +1683,7 @@
     mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
     mCompositionEngine->setHwComposer(getFactory().createHWComposer(
             vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName));
-    getHwComposer().registerCallback(this, ++getBE().mComposerSequenceId);
+    mCompositionEngine->getHwComposer().setConfiguration(this, ++getBE().mComposerSequenceId);
 
     LOG_ALWAYS_FATAL_IF(!getHwComposer().getComposer()->isRemote(),
                         "Switched to non-remote hardware composer");
@@ -2080,7 +2082,8 @@
         }
     });
 
-    if (presentFenceTime->isValid()) {
+    if (displayDevice && displayDevice->isPrimary() &&
+        displayDevice->getPowerMode() == HWC_POWER_MODE_NORMAL && presentFenceTime->isValid()) {
         mScheduler->addPresentFence(presentFenceTime);
     }
 
@@ -2118,6 +2121,10 @@
 
     mTimeStats->setPresentFenceGlobal(presentFenceTime);
 
+    const size_t sfConnections = mScheduler->getEventThreadConnectionCount(mSfConnectionHandle);
+    const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
+    mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
+
     if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
         !displayDevice->isPoweredOn()) {
         return;
@@ -5850,6 +5857,21 @@
     return NO_ERROR;
 }
 
+const std::unordered_map<std::string, uint32_t>& SurfaceFlinger::getGenericLayerMetadataKeyMap()
+        const {
+    // TODO(b/149500060): Remove this fixed/static mapping. Please prefer taking
+    // on the work to remove the table in that bug rather than adding more to
+    // it.
+    static const std::unordered_map<std::string, uint32_t> genericLayerMetadataKeyMap{
+            // Note: METADATA_OWNER_UID and METADATA_WINDOW_TYPE are officially
+            // supported, and exposed via the
+            // IVrComposerClient::VrCommand::SET_LAYER_INFO command.
+            {"org.chromium.arc.V1_0.TaskId", METADATA_TASK_ID},
+            {"org.chromium.arc.V1_0.CursorInfo", METADATA_MOUSE_CURSOR},
+    };
+    return genericLayerMetadataKeyMap;
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3a48bda..0144b4e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -248,6 +248,11 @@
     static ui::Dataspace wideColorGamutCompositionDataspace;
     static ui::PixelFormat wideColorGamutCompositionPixelFormat;
 
+    // Whether to use frame rate API when deciding about the refresh rate of the display. This
+    // variable is caches in SF, so that we can check it with each layer creation, and a void the
+    // overhead that is caused by reading from sysprop.
+    static bool useFrameRateApi;
+
     static char const* getServiceName() ANDROID_API {
         return "SurfaceFlinger";
     }
@@ -1148,6 +1153,15 @@
 
     std::atomic<nsecs_t> mExpectedPresentTime = 0;
 
+    /* ------------------------------------------------------------------------
+     * Generic Layer Metadata
+     */
+    const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const;
+
+    /* ------------------------------------------------------------------------
+     * Misc
+     */
+
     std::mutex mActiveConfigLock;
     // This bit is set once we start setting the config. We read from this bit during the
     // process. If at the end, this bit is different than mDesiredActiveConfig, we restart
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 1eab3b1..209bd0c 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -16,14 +16,14 @@
 
 #pragma once
 
+#include <cutils/compiler.h>
+#include <utils/StrongPointer.h>
+
 #include <cinttypes>
 #include <functional>
 #include <memory>
 #include <string>
 
-#include <cutils/compiler.h>
-#include <utils/StrongPointer.h>
-
 namespace android {
 
 typedef int32_t PixelFormat;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 269cf94..1a611f5 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -297,6 +297,14 @@
     return defaultValue;
 }
 
+bool use_frame_rate_api(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::use_frame_rate_api();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return defaultValue;
+}
+
 #define DISPLAY_PRIMARY_SIZE 3
 
 constexpr float kSrgbRedX = 0.4123f;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 440df49..4c6e191 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -87,6 +87,8 @@
 
 bool support_kernel_idle_timer(bool defaultValue);
 
+bool use_frame_rate_api(bool defaultValue);
+
 android::ui::DisplayPrimaries getDisplayNativePrimaries();
 } // namespace sysprop
 } // namespace android
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 493a709..7c8c28e 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -70,6 +70,7 @@
     mStatsDelegate->statsEventWriteInt64(event, mTimeStats.clientCompositionFrames);
     mStatsDelegate->statsEventWriteInt64(event, mTimeStats.displayOnTime);
     mStatsDelegate->statsEventWriteInt64(event, mTimeStats.presentToPresent.totalTime());
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.displayEventConnectionsCount);
     mStatsDelegate->statsEventBuild(event);
     clearGlobalLocked();
 
@@ -148,6 +149,9 @@
             }
         }
 
+        mStatsDelegate->statsEventWriteInt64(event, layer->lateAcquireFrames);
+        mStatsDelegate->statsEventWriteInt64(event, layer->badDesiredPresentFrames);
+
         mStatsDelegate->statsEventBuild(event);
     }
     clearLayersLocked();
@@ -269,6 +273,16 @@
     mTimeStats.clientCompositionReusedFrames++;
 }
 
+void TimeStats::recordDisplayEventConnectionCount(int32_t count) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    mTimeStats.displayEventConnectionsCount =
+            std::max(mTimeStats.displayEventConnectionsCount, count);
+}
+
 static int32_t msBetween(nsecs_t start, nsecs_t end) {
     int64_t delta = std::chrono::duration_cast<std::chrono::milliseconds>(
                             std::chrono::nanoseconds(end - start))
@@ -812,6 +826,7 @@
     mTimeStats.missedFrames = 0;
     mTimeStats.clientCompositionFrames = 0;
     mTimeStats.clientCompositionReusedFrames = 0;
+    mTimeStats.displayEventConnectionsCount = 0;
     mTimeStats.displayOnTime = 0;
     mTimeStats.presentToPresent.hist.clear();
     mTimeStats.frameDuration.hist.clear();
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index a428ef4..ddebeb1 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -52,6 +52,9 @@
     virtual void incrementMissedFrames() = 0;
     virtual void incrementClientCompositionFrames() = 0;
     virtual void incrementClientCompositionReusedFrames() = 0;
+    // Records the most up-to-date count of display event connections.
+    // The stored count will be the maximum ever recoded.
+    virtual void recordDisplayEventConnectionCount(int32_t count) = 0;
 
     // Records the start and end times for a frame.
     // The start time is the same as the beginning of a SurfaceFlinger
@@ -177,6 +180,10 @@
             return AStatsEvent_setAtomId(event, atom_id);
         }
 
+        virtual void statsEventWriteInt32(AStatsEvent* event, int32_t field) {
+            return AStatsEvent_writeInt32(event, field);
+        }
+
         virtual void statsEventWriteInt64(AStatsEvent* event, int64_t field) {
             return AStatsEvent_writeInt64(event, field);
         }
@@ -208,6 +215,7 @@
     void incrementMissedFrames() override;
     void incrementClientCompositionFrames() override;
     void incrementClientCompositionReusedFrames() override;
+    void recordDisplayEventConnectionCount(int32_t count) override;
 
     void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) override;
     void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) override;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index e374b73..5e7c449 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -62,6 +62,7 @@
         int32_t missedFrames = 0;
         int32_t clientCompositionFrames = 0;
         int32_t clientCompositionReusedFrames = 0;
+        int32_t displayEventConnectionsCount = 0;
         int64_t displayOnTime = 0;
         Histogram presentToPresent;
         Histogram frameDuration;
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 71f8d6a..b19eae6 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -393,3 +393,13 @@
     access: Readonly
     prop_name: "ro.surface_flinger.supports_background_blur"
 }
+
+# Indicates whether Scheduler should use frame rate API when adjusting the
+# display refresh rate.
+prop {
+    api_name: "use_frame_rate_api"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.use_frame_rate_api"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index cdfd0f5..c66523a 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -121,6 +121,10 @@
     prop_name: "ro.surface_flinger.use_context_priority"
   }
   prop {
+    api_name: "use_frame_rate_api"
+    prop_name: "ro.surface_flinger.use_frame_rate_api"
+  }
+  prop {
     api_name: "use_smart_90_for_video"
     prop_name: "ro.surface_flinger.use_smart_90_for_video"
     deprecated: true
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
index 3bbd12a..dbace11 100644
--- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -199,10 +199,17 @@
     ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size));
     ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size));
 
-    Transaction()
-            .setCornerRadius(layer, cornerRadius)
-            .setCrop_legacy(layer, Rect(0, 0, size, size))
-            .apply();
+    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+        Transaction()
+                .setCornerRadius(layer, cornerRadius)
+                .setCrop_legacy(layer, Rect(0, 0, size, size))
+                .apply();
+    } else {
+        Transaction()
+                .setCornerRadius(layer, cornerRadius)
+                .setFrame(layer, Rect(0, 0, size, size))
+                .apply();
+    }
     {
         const uint8_t bottom = size - 1;
         const uint8_t right = size - 1;
@@ -226,12 +233,21 @@
     ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size / 2));
     ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size / 2));
 
-    Transaction()
-            .setCornerRadius(parent, cornerRadius)
-            .setCrop_legacy(parent, Rect(0, 0, size, size))
-            .reparent(child, parent->getHandle())
-            .setPosition(child, 0, size / 2)
-            .apply();
+    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+        Transaction()
+                .setCornerRadius(parent, cornerRadius)
+                .setCrop_legacy(parent, Rect(0, 0, size, size))
+                .reparent(child, parent->getHandle())
+                .setPosition(child, 0, size / 2)
+                .apply();
+    } else {
+        Transaction()
+                .setCornerRadius(parent, cornerRadius)
+                .setFrame(parent, Rect(0, 0, size, size))
+                .reparent(child, parent->getHandle())
+                .setFrame(child, Rect(0, size / 2, size, size))
+                .apply();
+    }
     {
         const uint8_t bottom = size - 1;
         const uint8_t right = size - 1;
@@ -279,6 +295,48 @@
                       50 /* tolerance */);
 }
 
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusOnMultipleLayers) {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.surface_flinger.supports_background_blur", value, "0");
+    if (!atoi(value)) {
+        // This device doesn't support blurs, no-op.
+        return;
+    }
+
+    auto size = 256;
+    auto center = size / 2;
+    auto blurRadius = 50;
+
+    sp<SurfaceControl> backgroundLayer;
+    ASSERT_NO_FATAL_FAILURE(backgroundLayer = createLayer("background", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(backgroundLayer, Color::GREEN, size, size));
+
+    sp<SurfaceControl> leftLayer;
+    ASSERT_NO_FATAL_FAILURE(leftLayer = createLayer("left", size / 2, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(leftLayer, Color::RED, size / 2, size));
+
+    sp<SurfaceControl> blurLayer1;
+    auto centralSquareSize = size / 2;
+    ASSERT_NO_FATAL_FAILURE(blurLayer1 =
+                                    createLayer("blur1", centralSquareSize, centralSquareSize));
+    ASSERT_NO_FATAL_FAILURE(
+            fillLayerColor(blurLayer1, Color::BLUE, centralSquareSize, centralSquareSize));
+
+    sp<SurfaceControl> blurLayer2;
+    ASSERT_NO_FATAL_FAILURE(blurLayer2 = createLayer("blur2", size, size));
+    ASSERT_NO_FATAL_FAILURE(
+            fillLayerColor(blurLayer2, Color::TRANSPARENT, centralSquareSize, centralSquareSize));
+
+    Transaction()
+            .setBackgroundBlurRadius(blurLayer1, blurRadius)
+            .setBackgroundBlurRadius(blurLayer2, blurRadius)
+            .apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(center - 5, center - 5, center, center), Color{100, 100, 100, 255},
+                      40 /* tolerance */);
+}
+
 TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
     sp<SurfaceControl> bufferLayer;
     ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index 2bedd7d..cf7d570 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
 {
         "presubmit": {
-            "filter": "*:-LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadius/3:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/2:LayerTypeAndRenderTypeTransactionTests/LayerTypeAndRenderTypeTransactionTest.SetCornerRadiusChildCrop/3"
+            "filter": ""
         }
 }
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index d046f76..1cd8731 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -42,6 +42,7 @@
         "DisplayTransactionTest.cpp",
         "EventControlThreadTest.cpp",
         "EventThreadTest.cpp",
+        "HWComposerTest.cpp",
         "OneShotTimerTest.cpp",
         "LayerHistoryTest.cpp",
         "LayerHistoryTestV2.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 6e83166..1dfb6ba 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -116,8 +116,6 @@
 
     void setupComposer(int virtualDisplayCount) {
         mComposer = new Hwc2::mock::Composer();
-        EXPECT_CALL(*mComposer, getCapabilities())
-                .WillOnce(Return(std::vector<IComposer::Capability>()));
         EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
         mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
 
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index dddad92..232255f 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -201,8 +201,6 @@
 
 void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) {
     mComposer = new Hwc2::mock::Composer();
-    EXPECT_CALL(*mComposer, getCapabilities())
-            .WillOnce(Return(std::vector<IComposer::Capability>()));
     EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
     mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
 
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 61d4f47..65b3e35 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -19,9 +19,7 @@
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
 #include <log/log.h>
-
 #include <utils/Errors.h>
 
 #include "AsyncCallRecorder.h"
@@ -401,6 +399,34 @@
     expectVSyncSetEnabledCallReceived(false);
 }
 
+TEST_F(EventThreadTest, tracksEventConnections) {
+    EXPECT_EQ(1, mThread->getEventThreadConnectionCount());
+    ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
+    sp<MockEventThreadConnection> errorConnection =
+            createConnection(errorConnectionEventRecorder,
+                             ISurfaceComposer::eConfigChangedSuppress);
+    mThread->setVsyncRate(1, errorConnection);
+    EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+    ConnectionEventRecorder secondConnectionEventRecorder{0};
+    sp<MockEventThreadConnection> secondConnection =
+            createConnection(secondConnectionEventRecorder,
+                             ISurfaceComposer::eConfigChangedSuppress);
+    mThread->setVsyncRate(1, secondConnection);
+    EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
+
+    // EventThread should enable vsync callbacks.
+    expectVSyncSetEnabledCallReceived(true);
+
+    // The first event will be seen by the interceptor, and by the connection,
+    // which then returns an error.
+    mCallback->onVSyncEvent(123);
+    expectInterceptCallReceived(123);
+    expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
+    expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
+                                         1u);
+    EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+}
+
 TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
     ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
     sp<MockEventThreadConnection> errorConnection =
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
new file mode 100644
index 0000000..c6fe205
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gui/LayerMetadata.h>
+#include <log/log.h>
+
+#include "DisplayHardware/HWComposer.h"
+#include "mock/DisplayHardware/MockComposer.h"
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
+namespace android {
+namespace {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::ElementsAreArray;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrictMock;
+
+struct MockHWC2ComposerCallback : public HWC2::ComposerCallback {
+    ~MockHWC2ComposerCallback() = default;
+
+    MOCK_METHOD3(onHotplugReceived,
+                 void(int32_t sequenceId, hwc2_display_t display, HWC2::Connection connection));
+    MOCK_METHOD2(onRefreshReceived, void(int32_t sequenceId, hwc2_display_t display));
+    MOCK_METHOD4(onVsyncReceived,
+                 void(int32_t sequenceId, hwc2_display_t display, int64_t timestamp,
+                      std::optional<hwc2_vsync_period_t> vsyncPeriod));
+    MOCK_METHOD3(onVsyncPeriodTimingChangedReceived,
+                 void(int32_t sequenceId, hwc2_display_t display,
+                      const hwc_vsync_period_change_timeline_t& updatedTimeline));
+    MOCK_METHOD2(onSeamlessPossible, void(int32_t sequenceId, hwc2_display_t display));
+};
+
+struct HWComposerTest : public testing::Test {
+    Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
+};
+
+struct HWComposerSetConfigurationTest : public HWComposerTest {
+    StrictMock<MockHWC2ComposerCallback> mCallback;
+};
+
+TEST_F(HWComposerSetConfigurationTest, loadsLayerMetadataSupport) {
+    const std::string kMetadata1Name = "com.example.metadata.1";
+    constexpr bool kMetadata1Mandatory = false;
+    const std::string kMetadata2Name = "com.example.metadata.2";
+    constexpr bool kMetadata2Mandatory = true;
+
+    EXPECT_CALL(*mHal, getMaxVirtualDisplayCount()).WillOnce(Return(0));
+    EXPECT_CALL(*mHal, getCapabilities())
+            .WillOnce(Return(std::vector<Hwc2::IComposer::Capability>{}));
+    EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
+            .WillOnce(DoAll(SetArgPointee<0>(
+                                    std::vector<Hwc2::IComposerClient::LayerGenericMetadataKey>{
+                                            {kMetadata1Name, kMetadata1Mandatory},
+                                            {kMetadata2Name, kMetadata2Mandatory},
+                                    }),
+                            Return(hardware::graphics::composer::V2_4::Error::NONE)));
+    EXPECT_CALL(*mHal, registerCallback(_));
+    EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported()).WillOnce(Return(false));
+
+    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
+    hwc.setConfiguration(&mCallback, 123);
+
+    const auto& supported = hwc.getSupportedLayerGenericMetadata();
+    EXPECT_EQ(2u, supported.size());
+    EXPECT_EQ(1u, supported.count(kMetadata1Name));
+    EXPECT_EQ(kMetadata1Mandatory, supported.find(kMetadata1Name)->second);
+    EXPECT_EQ(1u, supported.count(kMetadata2Name));
+    EXPECT_EQ(kMetadata2Mandatory, supported.find(kMetadata2Name)->second);
+}
+
+TEST_F(HWComposerSetConfigurationTest, handlesUnsupportedCallToGetLayerGenericMetadataKeys) {
+    EXPECT_CALL(*mHal, getMaxVirtualDisplayCount()).WillOnce(Return(0));
+    EXPECT_CALL(*mHal, getCapabilities())
+            .WillOnce(Return(std::vector<Hwc2::IComposer::Capability>{}));
+    EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
+            .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
+    EXPECT_CALL(*mHal, registerCallback(_));
+    EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported()).WillOnce(Return(false));
+
+    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
+    hwc.setConfiguration(&mCallback, 123);
+
+    const auto& supported = hwc.getSupportedLayerGenericMetadata();
+    EXPECT_EQ(0u, supported.size());
+}
+
+struct HWComposerLayerTest : public testing::Test {
+    static constexpr hwc2_display_t kDisplayId = static_cast<hwc2_display_t>(1001);
+    static constexpr hwc2_layer_t kLayerId = static_cast<hwc2_layer_t>(1002);
+
+    HWComposerLayerTest(const std::unordered_set<HWC2::Capability>& capabilities)
+          : mCapabilies(capabilities) {}
+
+    ~HWComposerLayerTest() override { EXPECT_CALL(*mHal, destroyLayer(kDisplayId, kLayerId)); }
+
+    std::unique_ptr<Hwc2::mock::Composer> mHal{new StrictMock<Hwc2::mock::Composer>()};
+    const std::unordered_set<HWC2::Capability> mCapabilies;
+    HWC2::impl::Layer mLayer{*mHal, mCapabilies, kDisplayId, kLayerId};
+};
+
+struct HWComposerLayerGenericMetadataTest : public HWComposerLayerTest {
+    static const std::string kLayerGenericMetadata1Name;
+    static constexpr bool kLayerGenericMetadata1Mandatory = false;
+    static const std::vector<uint8_t> kLayerGenericMetadata1Value;
+    static const std::string kLayerGenericMetadata2Name;
+    static constexpr bool kLayerGenericMetadata2Mandatory = true;
+    static const std::vector<uint8_t> kLayerGenericMetadata2Value;
+
+    HWComposerLayerGenericMetadataTest() : HWComposerLayerTest({}) {}
+};
+
+const std::string HWComposerLayerGenericMetadataTest::kLayerGenericMetadata1Name =
+        "com.example.metadata.1";
+
+const std::vector<uint8_t> HWComposerLayerGenericMetadataTest::kLayerGenericMetadata1Value = {1u,
+                                                                                              2u,
+                                                                                              3u};
+
+const std::string HWComposerLayerGenericMetadataTest::kLayerGenericMetadata2Name =
+        "com.example.metadata.2";
+
+const std::vector<uint8_t> HWComposerLayerGenericMetadataTest::kLayerGenericMetadata2Value = {45u,
+                                                                                              67u};
+
+TEST_F(HWComposerLayerGenericMetadataTest, forwardsSupportedMetadata) {
+    EXPECT_CALL(*mHal,
+                setLayerGenericMetadata(kDisplayId, kLayerId, kLayerGenericMetadata1Name,
+                                        kLayerGenericMetadata1Mandatory,
+                                        kLayerGenericMetadata1Value))
+            .WillOnce(Return(hardware::graphics::composer::V2_4::Error::NONE));
+    auto result = mLayer.setLayerGenericMetadata(kLayerGenericMetadata1Name,
+                                                 kLayerGenericMetadata1Mandatory,
+                                                 kLayerGenericMetadata1Value);
+    EXPECT_EQ(HWC2::Error::None, result);
+
+    EXPECT_CALL(*mHal,
+                setLayerGenericMetadata(kDisplayId, kLayerId, kLayerGenericMetadata2Name,
+                                        kLayerGenericMetadata2Mandatory,
+                                        kLayerGenericMetadata2Value))
+            .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
+    result = mLayer.setLayerGenericMetadata(kLayerGenericMetadata2Name,
+                                            kLayerGenericMetadata2Mandatory,
+                                            kLayerGenericMetadata2Value);
+    EXPECT_EQ(HWC2::Error::Unsupported, result);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 841c624..99c5f3d 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -745,6 +745,44 @@
     }
 }
 
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_Multiples) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 90.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::Max;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 30.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 90.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 30.0f;
+    lr2.vote = LayerVoteType::Max;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
 } // namespace
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index edd9de4..18b1063 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -153,8 +153,6 @@
 
 void RefreshRateSelectionTest::setupComposer(int virtualDisplayCount) {
     mComposer = new Hwc2::mock::Composer();
-    EXPECT_CALL(*mComposer, getCapabilities())
-            .WillOnce(Return(std::vector<IComposer::Capability>()));
     EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
     mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
 
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 89002a8..5db11ec 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -159,6 +159,11 @@
 
     EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
     ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
+
+    static constexpr size_t kEventConnections = 5;
+    ON_CALL(*mEventThread, getEventThreadConnectionCount())
+            .WillByDefault(Return(kEventConnections));
+    EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
 }
 
 } // namespace
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 91a40d0..685dfba 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -166,6 +166,7 @@
 
         MOCK_METHOD1(unregisterStatsPullAtomCallback, void(int32_t));
         MOCK_METHOD2(statsEventSetAtomId, void(AStatsEvent*, uint32_t));
+        MOCK_METHOD2(statsEventWriteInt32, void(AStatsEvent*, int32_t));
         MOCK_METHOD2(statsEventWriteInt64, void(AStatsEvent*, int64_t));
         MOCK_METHOD2(statsEventWriteString8, void(AStatsEvent*, const char*));
         MOCK_METHOD3(statsEventWriteByteArray, void(AStatsEvent*, const uint8_t*, size_t));
@@ -800,6 +801,7 @@
     constexpr size_t TOTAL_FRAMES = 5;
     constexpr size_t MISSED_FRAMES = 4;
     constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
+    constexpr size_t DISPLAY_EVENT_CONNECTIONS = 14;
 
     mTimeStats->onBootFinished();
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
@@ -814,6 +816,8 @@
         mTimeStats->incrementClientCompositionFrames();
     }
 
+    mTimeStats->recordDisplayEventConnectionCount(DISPLAY_EVENT_CONNECTIONS);
+
     mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
     mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
@@ -834,6 +838,7 @@
         EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, CLIENT_COMPOSITION_FRAMES));
         EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, _));
         EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 2));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, DISPLAY_EVENT_CONNECTIONS));
         EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
     }
     EXPECT_EQ(AStatsManager_PULL_SUCCESS,
@@ -892,12 +897,20 @@
     return expected == actual;
 }
 
-TEST_F(TimeStatsTest, layerStatsCallback_pullsAllHistogramsAndClears) {
+TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) {
+    constexpr size_t LATE_ACQUIRE_FRAMES = 2;
+    constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3;
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
     mTimeStats->onBootFinished();
 
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
+        mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
+    }
+    for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
+        mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
+    }
     insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
 
     EXPECT_THAT(mDelegate->mAtomTags,
@@ -955,6 +968,9 @@
                                              BytesEq((const uint8_t*)expectedPostToAcquire.c_str(),
                                                      expectedPostToAcquire.size()),
                                              expectedPostToAcquire.size()));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, LATE_ACQUIRE_FRAMES));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteInt64(mDelegate->mEvent, BAD_DESIRED_PRESENT_FRAMES));
         EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
     }
     EXPECT_EQ(AStatsManager_PULL_SUCCESS,
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 6ec3844..f834af8 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -36,7 +36,7 @@
 namespace android::scheduler {
 
 MATCHER_P2(IsCloseTo, value, tolerance, "is within tolerance") {
-    return arg <= value + tolerance && value >= value - tolerance;
+    return arg <= value + tolerance && arg >= value - tolerance;
 }
 
 std::vector<nsecs_t> generateVsyncTimestamps(size_t count, nsecs_t period, nsecs_t bias) {
@@ -370,6 +370,26 @@
                 IsCloseTo(idealPeriod, mMaxRoundingError));
 }
 
+TEST_F(VSyncPredictorTest, slopeAlwaysValid) {
+    constexpr auto kNumVsyncs = 100;
+    auto invalidPeriod = mPeriod;
+    auto now = 0;
+    for (int i = 0; i < kNumVsyncs; i++) {
+        tracker.addVsyncTimestamp(now);
+        now += invalidPeriod;
+        invalidPeriod *= 0.9f;
+
+        auto [slope, intercept] = tracker.getVSyncPredictionModel();
+        EXPECT_THAT(slope, IsCloseTo(mPeriod, mPeriod * kOutlierTolerancePercent / 100.f));
+
+        // When VsyncPredictor returns the period it means that it doesn't know how to predict and
+        // it needs to get more samples
+        if (slope == mPeriod && intercept == 0) {
+            EXPECT_TRUE(tracker.needsMoreSamples(now));
+        }
+    }
+}
+
 } // namespace android::scheduler
 
 // TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 1de72b9..ac95938 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -250,9 +250,9 @@
     nsecs_t const newPeriod = 5000;
     mReactor.setPeriod(newPeriod);
 
-    EXPECT_TRUE(mReactor.addResyncSample(0, &periodFlushed));
+    EXPECT_TRUE(mReactor.addResyncSample(0, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_FALSE(mReactor.addResyncSample(newPeriod, &periodFlushed));
+    EXPECT_FALSE(mReactor.addResyncSample(newPeriod, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 
     EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
@@ -302,16 +302,16 @@
     mReactor.setPeriod(newPeriod);
 
     bool periodFlushed = true;
-    EXPECT_TRUE(mReactor.addResyncSample(10000, &periodFlushed));
+    EXPECT_TRUE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 
-    EXPECT_TRUE(mReactor.addResyncSample(20000, &periodFlushed));
+    EXPECT_TRUE(mReactor.addResyncSample(20000, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 
     Mock::VerifyAndClearExpectations(mMockTracker.get());
     EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
 
-    EXPECT_FALSE(mReactor.addResyncSample(25000, &periodFlushed));
+    EXPECT_FALSE(mReactor.addResyncSample(25000, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 }
 
@@ -320,14 +320,14 @@
     nsecs_t const newPeriod = 5000;
     mReactor.setPeriod(newPeriod);
     bool periodFlushed = true;
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 
     mReactor.setPeriod(period);
-    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 }
 
@@ -338,14 +338,14 @@
 
     mReactor.setPeriod(secondPeriod);
     bool periodFlushed = true;
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
     mReactor.setPeriod(thirdPeriod);
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += secondPeriod, &periodFlushed));
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += thirdPeriod, &periodFlushed));
+    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += thirdPeriod, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 }
 
@@ -360,9 +360,9 @@
     nsecs_t skewyPeriod = period >> 1;
     bool periodFlushed = false;
     nsecs_t sampleTime = 0;
-    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, &periodFlushed));
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
-    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, &periodFlushed));
+    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 }
 
@@ -390,12 +390,12 @@
     mReactor.setPeriod(newPeriod);
 
     bool periodFlushed = true;
-    EXPECT_TRUE(mReactor.addResyncSample(5000, &periodFlushed));
+    EXPECT_TRUE(mReactor.addResyncSample(5000, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
     Mock::VerifyAndClearExpectations(mMockTracker.get());
 
     EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
-    EXPECT_FALSE(mReactor.addResyncSample(10000, &periodFlushed));
+    EXPECT_FALSE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 }
 
@@ -404,7 +404,7 @@
     bool periodFlushed = false;
 
     EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
-    EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, &periodFlushed));
+    EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, std::nullopt, &periodFlushed));
     EXPECT_FALSE(periodFlushed);
 }
 
@@ -418,17 +418,17 @@
     auto constexpr numTimestampSubmissions = 10;
     for (auto i = 0; i < numTimestampSubmissions; i++) {
         time += period;
-        EXPECT_TRUE(mReactor.addResyncSample(time, &periodFlushed));
+        EXPECT_TRUE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
         EXPECT_FALSE(periodFlushed);
     }
 
     time += newPeriod;
-    EXPECT_FALSE(mReactor.addResyncSample(time, &periodFlushed));
+    EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
     EXPECT_TRUE(periodFlushed);
 
     for (auto i = 0; i < numTimestampSubmissions; i++) {
         time += newPeriod;
-        EXPECT_FALSE(mReactor.addResyncSample(time, &periodFlushed));
+        EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
         EXPECT_FALSE(periodFlushed);
     }
 }
@@ -440,11 +440,11 @@
     mReactor.setPeriod(newPeriod);
 
     time += period;
-    mReactor.addResyncSample(time, &periodFlushed);
+    mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
     EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 
     time += newPeriod;
-    mReactor.addResyncSample(time, &periodFlushed);
+    mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
 
     EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
 }
@@ -534,6 +534,30 @@
     mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
 }
 
+// b/149221293
+TEST_F(VSyncReactorTest, selfRemovingEventListenerStopsCallbacks) {
+    class SelfRemovingCallback : public DispSync::Callback {
+    public:
+        SelfRemovingCallback(VSyncReactor& vsr) : mVsr(vsr) {}
+        void onDispSyncEvent(nsecs_t when) final { mVsr.removeEventListener(this, &when); }
+
+    private:
+        VSyncReactor& mVsr;
+    } selfRemover(mReactor);
+
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &selfRemover, lastCallbackTime);
+    innerCb(0, 0);
+}
+
 TEST_F(VSyncReactorTest, addEventListenerChangePeriod) {
     Sequence seq;
     EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
@@ -568,8 +592,8 @@
 
     bool periodFlushed = false;
     mReactor.setPeriod(anotherPeriod);
-    EXPECT_TRUE(mReactor.addResyncSample(anotherPeriod, &periodFlushed));
-    EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, &periodFlushed));
+    EXPECT_TRUE(mReactor.addResyncSample(anotherPeriod, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, std::nullopt, &periodFlushed));
 
     mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
 }
@@ -614,6 +638,24 @@
     mReactor.beginResync();
 }
 
+TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) {
+    bool periodFlushed = true;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(3);
+    mReactor.setIgnorePresentFences(true);
+
+    nsecs_t const newPeriod = 5000;
+    mReactor.setPeriod(newPeriod);
+
+    EXPECT_TRUE(mReactor.addResyncSample(0, 0, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_TRUE(mReactor.addResyncSample(newPeriod, 0, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_FALSE(mReactor.addResyncSample(newPeriod, newPeriod, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
 using VSyncReactorDeathTest = VSyncReactorTest;
 TEST_F(VSyncReactorDeathTest, invalidRemoval) {
     mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
index 9ca116d..a2ae6c9 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
@@ -31,7 +31,7 @@
     MOCK_METHOD0(reset, void());
     MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
     MOCK_METHOD0(beginResync, void());
-    MOCK_METHOD2(addResyncSample, bool(nsecs_t, bool*));
+    MOCK_METHOD3(addResyncSample, bool(nsecs_t, std::optional<nsecs_t>, bool*));
     MOCK_METHOD0(endResync, void());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(getPeriod, nsecs_t());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 9bda095..50eb390 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -42,6 +42,7 @@
     MOCK_METHOD1(requestNextVsync, void(const sp<android::EventThreadConnection> &));
     MOCK_METHOD0(requestLatestConfig, void());
     MOCK_METHOD1(pauseVsyncCallback, void(bool));
+    MOCK_METHOD0(getEventThreadConnectionCount, size_t());
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 2720537..c45d584 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -36,6 +36,7 @@
     MOCK_METHOD0(incrementMissedFrames, void());
     MOCK_METHOD0(incrementClientCompositionFrames, void());
     MOCK_METHOD0(incrementClientCompositionReusedFrames, void());
+    MOCK_METHOD1(recordDisplayEventConnectionCount, void(int32_t));
     MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
     MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
     MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));