Merge "Add InputDispatcher unit tests for untrusted touches [5/n]" into sc-dev
diff --git a/aidl/gui/android/hardware/display/IDeviceProductInfoConstants.aidl b/aidl/gui/android/hardware/display/IDeviceProductInfoConstants.aidl
deleted file mode 100644
index 7cc272a..0000000
--- a/aidl/gui/android/hardware/display/IDeviceProductInfoConstants.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.display;
-
-/** @hide */
-interface IDeviceProductInfoConstants {
-    /** The device connection to the display sink is unknown. */
-    const int CONNECTION_TO_SINK_UNKNOWN = 0;
-
-    /** The device is built-in in the display sink. */
-    const int CONNECTION_TO_SINK_BUILT_IN = 1;
-
-    /** The device is directly connected to the display sink. */
-    const int CONNECTION_TO_SINK_DIRECT = 2;
-
-    /** The device is transitively connected to the display sink. */
-    const int CONNECTION_TO_SINK_TRANSITIVE = 3;
-}
\ No newline at end of file
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index c7c3a34..006e532 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -37,12 +37,18 @@
     chmod 0666 /sys/kernel/tracing/events/sched/sched_process_exit/enable
     chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_waking/enable
     chmod 0666 /sys/kernel/tracing/events/sched/sched_waking/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_wakeup_new/enable
+    chmod 0666 /sys/kernel/tracing/events/sched/sched_wakeup_new/enable
     chmod 0666 /sys/kernel/debug/tracing/events/cgroup/enable
     chmod 0666 /sys/kernel/tracing/events/cgroup/enable
     chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
     chmod 0666 /sys/kernel/tracing/events/power/cpu_frequency/enable
     chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_idle/enable
     chmod 0666 /sys/kernel/tracing/events/power/cpu_idle/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/power/clock_enable/enable
+    chmod 0666 /sys/kernel/tracing/events/power/clock_enable/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/power/clock_disable/enable
+    chmod 0666 /sys/kernel/tracing/events/power/clock_disable/enable
     chmod 0666 /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
     chmod 0666 /sys/kernel/tracing/events/power/clock_set_rate/enable
     chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable
@@ -79,6 +85,8 @@
     chmod 0666 /sys/kernel/tracing/events/binder/binder_locked/enable
     chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_unlock/enable
     chmod 0666 /sys/kernel/tracing/events/binder/binder_unlock/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/binder/binder_set_priority/enable
+    chmod 0666 /sys/kernel/tracing/events/binder/binder_set_priority/enable
     chmod 0666 /sys/kernel/debug/tracing/events/i2c/enable
     chmod 0666 /sys/kernel/tracing/events/i2c/enable
     chmod 0666 /sys/kernel/debug/tracing/events/i2c/i2c_read/enable
@@ -125,6 +133,8 @@
     chmod 0666 /sys/kernel/tracing/events/lowmemorykiller/lowmemory_kill/enable
     chmod 0666 /sys/kernel/debug/tracing/events/oom/oom_score_adj_update/enable
     chmod 0666 /sys/kernel/tracing/events/oom/oom_score_adj_update/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/oom/mark_victim/enable
+    chmod 0666 /sys/kernel/tracing/events/oom/mark_victim/enable
     chmod 0666 /sys/kernel/debug/tracing/events/task/task_rename/enable
     chmod 0666 /sys/kernel/tracing/events/task/task_rename/enable
     chmod 0666 /sys/kernel/debug/tracing/events/task/task_newtask/enable
@@ -159,6 +169,12 @@
     chmod 0666 /sys/kernel/tracing/events/ipi/ipi_exit/enable
     chmod 0666 /sys/kernel/debug/tracing/events/ipi/ipi_raise/enable
     chmod 0666 /sys/kernel/tracing/events/ipi/ipi_raise/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/clk/clk_enable/enable
+    chmod 0666 /sys/kernel/tracing/events/clk/clk_disable/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/clk/clk_disable/enable
+    chmod 0666 /sys/kernel/tracing/events/clk/clk_enable/enable
+    chmod 0666 /sys/kernel/debug/tracing/events/clk/clk_set_rate/enable
+    chmod 0666 /sys/kernel/tracing/events/clk/clk_set_rate/enable
 
     # disk
     chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable
diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc
index 6c86c21..9186514 100644
--- a/cmds/atrace/atrace_userdebug.rc
+++ b/cmds/atrace/atrace_userdebug.rc
@@ -18,8 +18,3 @@
     chmod 0666 /sys/kernel/tracing/events/filemap/enable
     chmod 0666 /sys/kernel/debug/tracing/events/filemap/enable
 
-    # irq
-    chmod 0666 /sys/kernel/tracing/events/irq/enable
-    chmod 0666 /sys/kernel/debug/tracing/events/irq/enable
-    chmod 0666 /sys/kernel/tracing/events/ipi/enable
-    chmod 0666 /sys/kernel/debug/tracing/events/ipi/enable
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 990aa53..45959e4 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1657,8 +1657,6 @@
     for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
     for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
 
-    /* Dump Bluetooth HCI logs */
-    ds.AddDir("/data/misc/bluetooth/logs", true);
     /* Dump Nfc NCI logs */
     ds.AddDir("/data/misc/nfc/logs", true);
 
@@ -1744,6 +1742,9 @@
 
     RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal);
 
+    /* Dump Bluetooth HCI logs after getting bluetooth_manager dumpsys */
+    ds.AddDir("/data/misc/bluetooth/logs", true);
+
     if (ds.dump_pool_) {
         WAIT_TASK_WITH_CONSENT_CHECK(DUMP_CHECKINS_TASK, ds.dump_pool_);
     } else {
diff --git a/cmds/idlcli/CommandVibrator.cpp b/cmds/idlcli/CommandVibrator.cpp
index a7a70c3..81bdbe2 100644
--- a/cmds/idlcli/CommandVibrator.cpp
+++ b/cmds/idlcli/CommandVibrator.cpp
@@ -22,7 +22,7 @@
 class IdlCli;
 
 class CommandVibrator : public CommandWithSubcommands<CommandVibrator> {
-    std::string getDescription() const override { return "Invoke Vibrator HIDL APIs."; }
+    std::string getDescription() const override { return "Invoke Vibrator IDL APIs."; }
 
     std::string getUsageSummary() const override { return "<api> [arguments]"; }
 
diff --git a/cmds/idlcli/IdlCli.h b/cmds/idlcli/IdlCli.h
index dd84304..24a40d9 100644
--- a/cmds/idlcli/IdlCli.h
+++ b/cmds/idlcli/IdlCli.h
@@ -25,14 +25,47 @@
 class IdlCli : public CommandWithSubcommands<IdlCli> {
     std::string getDescription() const override { return "Invoke IDL APIs."; }
 
-    std::string getUsageSummary() const override { return "<idl> [arguments]"; }
+    std::string getUsageSummary() const override { return "<idl> [options] [arguments]"; }
 
     UsageDetails getUsageDetails() const override {
         UsageDetails details{
+                {"-n <name>", {"Get named service, rather than default."}},
                 {"<idl>", CommandRegistry<IdlCli>::List()},
         };
         return details;
     }
+
+    Status doArgs(Args &args) override {
+        while (args.get<std::string>().value_or("").find("-") == 0) {
+            auto opt = *args.pop<std::string>();
+            if (opt == "--") {
+                break;
+            } else if (opt == "-n") {
+                if (auto name = args.pop<decltype(mName)>()) {
+                    mName = *name;
+                } else {
+                    std::cerr << "Missing Value for Name!" << std::endl;
+                    return USAGE;
+                }
+            } else {
+                std::cerr << "Invalid Option '" << opt << "'!" << std::endl;
+                return USAGE;
+            }
+        }
+        return CommandWithSubcommands::doArgs(args);
+    }
+
+    IdlCli() {}
+
+    std::string mName;
+
+public:
+    static IdlCli &Get() {
+        static IdlCli instance;
+        return instance;
+    }
+
+    auto getName() { return mName; }
 };
 
 } // namespace idlcli
diff --git a/cmds/idlcli/main.cpp b/cmds/idlcli/main.cpp
index 9ed9d82..308f294 100644
--- a/cmds/idlcli/main.cpp
+++ b/cmds/idlcli/main.cpp
@@ -19,5 +19,5 @@
 
 int main(const int argc, const char* const argv[]) {
     using namespace ::android::idlcli;
-    return IdlCli{}.main(Args{argc, argv});
+    return IdlCli::Get().main(Args{argc, argv});
 }
diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h
index b874455..262f2e5 100644
--- a/cmds/idlcli/utils.h
+++ b/cmds/idlcli/utils.h
@@ -249,7 +249,7 @@
 
 template <typename T>
 class CommandWithSubcommands : public Command {
-private:
+protected:
     Status doArgs(Args &args) override {
         mCommand = CommandRegistry<T>::Create(*args.get());
         if (!mCommand) {
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index 6c30a9e..dfbb886 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -13,24 +13,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
-#define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+#pragma once
 
 #include <future>
 
 #include <aidl/android/hardware/vibrator/BnVibratorCallback.h>
 #include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <aidl/android/hardware/vibrator/IVibratorManager.h>
 #include <android/binder_manager.h>
 #include <android/binder_process.h>
 #include <android/hardware/vibrator/1.3/IVibrator.h>
 
+#include "IdlCli.h"
 #include "utils.h"
 
-#include "log/log.h"
-
 namespace android {
 
 using hardware::Return;
+using idlcli::IdlCli;
 
 static constexpr int NUM_TRIES = 2;
 
@@ -47,20 +47,34 @@
 }
 
 template <typename I>
-inline auto getService() {
-    return I::getService();
+inline auto getService(std::string name) {
+    const auto instance = std::string() + I::descriptor + "/" + name;
+    auto vibBinder = ndk::SpAIBinder(AServiceManager_getService(instance.c_str()));
+    return I::fromBinder(vibBinder);
 }
 
 template <>
-inline auto getService<aidl::android::hardware::vibrator::IVibrator>() {
-    const auto instance =
-            std::string() + aidl::android::hardware::vibrator::IVibrator::descriptor + "/default";
-    auto vibBinder = ndk::SpAIBinder(AServiceManager_getService(instance.c_str()));
-    return aidl::android::hardware::vibrator::IVibrator::fromBinder(vibBinder);
+inline auto getService<android::hardware::vibrator::V1_0::IVibrator>(std::string name) {
+    return android::hardware::vibrator::V1_0::IVibrator::getService(name);
+}
+
+template <>
+inline auto getService<android::hardware::vibrator::V1_1::IVibrator>(std::string name) {
+    return android::hardware::vibrator::V1_1::IVibrator::getService(name);
+}
+
+template <>
+inline auto getService<android::hardware::vibrator::V1_2::IVibrator>(std::string name) {
+    return android::hardware::vibrator::V1_2::IVibrator::getService(name);
+}
+
+template <>
+inline auto getService<android::hardware::vibrator::V1_3::IVibrator>(std::string name) {
+    return android::hardware::vibrator::V1_3::IVibrator::getService(name);
 }
 
 template <typename I>
-using shared_ptr = std::result_of_t<decltype(getService<I>)&()>;
+using shared_ptr = std::result_of_t<decltype(getService<I>)&(std::string)>;
 
 template <typename I>
 class HalWrapper {
@@ -68,7 +82,8 @@
     static std::unique_ptr<HalWrapper> Create() {
         // Assume that if getService returns a nullptr, HAL is not available on the
         // device.
-        auto hal = getService<I>();
+        const auto name = IdlCli::Get().getName();
+        auto hal = getService<I>(name.empty() ? "default" : name);
         return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr;
     }
 
@@ -121,5 +136,3 @@
 } // namespace idlcli
 
 } // namespace android
-
-#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 83b6aa0..9d88ca6 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -1,7 +1,16 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
 prebuilt_etc {
     name: "android.hardware.biometrics.face.xml",
     product_specific: true,
     sub_dir: "permissions",
     src: "android.hardware.biometrics.face.xml",
     filename_from_src: true,
-}
\ No newline at end of file
+}
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
index cac67d4..ee1eee2 100644
--- a/include/android/imagedecoder.h
+++ b/include/android/imagedecoder.h
@@ -367,6 +367,10 @@
  * the first frame (e.g. before calling {@link AImageDecoder_advanceFrame} or
  * after calling {@link AImageDecoder_rewind}).
  *
+ * It is strongly recommended to use setTargetSize only for downscaling, as it
+ * is often more efficient to scale-up when rendering than up-front due to
+ * reduced overall memory.
+ *
  * Available since API level 30.
  *
  * @param width Width of the output (prior to cropping).
diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp
index b85aecd..ea3c341 100644
--- a/libs/attestation/Android.bp
+++ b/libs/attestation/Android.bp
@@ -11,6 +11,15 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
 cc_library_static {
     name: "libattestation",
     cflags: [
@@ -28,4 +37,4 @@
         "liblog",
         "libcrypto",
     ],
-}
\ No newline at end of file
+}
diff --git a/libs/attestation/tests/Android.bp b/libs/attestation/tests/Android.bp
index 6ce5ea1..8dac0ce 100644
--- a/libs/attestation/tests/Android.bp
+++ b/libs/attestation/tests/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
 cc_test {
     name: "libattestation_tests",
     test_suites: ["device-tests"],
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 2e90142..1fbaa13 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -22,10 +22,6 @@
       "name": "binderParcelTest"
     },
     {
-      "name": "binderParcelTest",
-      "host": true
-    },
-    {
       "name": "binderLibTest"
     },
     {
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 4580751..5df0012 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -96,6 +96,22 @@
 bool AServiceManager_isDeclared(const char* instance) __INTRODUCED_IN(31);
 
 /**
+ * Returns all declared instances for a particular interface.
+ *
+ * For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo' is
+ * passed here, then ["foo"] would be returned.
+ *
+ * See also AServiceManager_isDeclared.
+ *
+ * \param interface interface, e.g. 'android.foo.IFoo'
+ * \param context to pass to callback
+ * \param callback taking instance (e.g. 'foo') and context
+ */
+void AServiceManager_forEachDeclaredInstance(const char* interface, void* context,
+                                             void (*callback)(const char*, void*))
+        __INTRODUCED_IN(31);
+
+/**
  * Prevent lazy services without client from shutting down their process
  *
  * \param persist 'true' if the process should not exit.
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index cef0bf3..8d08275 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -118,6 +118,7 @@
     AIBinder_getCallingSid; # apex
     AIBinder_setRequestingSid; # apex
     AServiceManager_isDeclared; # apex llndk
+    AServiceManager_forEachDeclaredInstance; # apex llndk
     AServiceManager_registerLazyService; # llndk
     AServiceManager_waitForService; # apex llndk
     AServiceManager_forceLazyServicesPersist; # llndk
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index cb0987e..1ccd0d2 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -19,6 +19,7 @@
 #include "ibinder_internal.h"
 #include "status_internal.h"
 
+#include <android-base/logging.h>
 #include <binder/IServiceManager.h>
 #include <binder/LazyServiceRegistrar.h>
 
@@ -28,6 +29,7 @@
 using ::android::sp;
 using ::android::status_t;
 using ::android::String16;
+using ::android::String8;
 
 binder_exception_t AServiceManager_addService(AIBinder* binder, const char* instance) {
     if (binder == nullptr || instance == nullptr) {
@@ -92,6 +94,17 @@
     sp<IServiceManager> sm = defaultServiceManager();
     return sm->isDeclared(String16(instance));
 }
+void AServiceManager_forEachDeclaredInstance(const char* interface, void* context,
+                                             void (*callback)(const char*, void*)) {
+    CHECK(interface != nullptr);
+    // context may be nullptr
+    CHECK(callback != nullptr);
+
+    sp<IServiceManager> sm = defaultServiceManager();
+    for (const String16& instance : sm->getDeclaredInstances(String16(interface))) {
+        callback(String8(instance).c_str(), context);
+    }
+}
 void AServiceManager_forceLazyServicesPersist(bool persist) {
     auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance();
     serviceRegistrar.forcePersist(persist);
@@ -110,4 +123,4 @@
 void AServiceManager_reRegister() {
     auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance();
     serviceRegistrar.reRegister();
-}
\ No newline at end of file
+}
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index de1a48d..6a88401 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -270,6 +270,25 @@
     EXPECT_EQ(2, out);
 }
 
+void defaultInstanceCounter(const char* instance, void* context) {
+    if (strcmp(instance, "default") == 0) {
+        ++*(size_t*)(context);
+    }
+}
+
+TEST(NdkBinder, GetDeclaredInstances) {
+    bool hasLight = AServiceManager_isDeclared("android.hardware.light.ILights/default");
+
+    size_t count;
+    AServiceManager_forEachDeclaredInstance("android.hardware.light.ILights", &count,
+                                            defaultInstanceCounter);
+
+    // At the time of writing this test, there is no good interface guaranteed
+    // to be on all devices. Cuttlefish has light, so this will generally test
+    // things.
+    EXPECT_EQ(count, hasLight ? 1 : 0);
+}
+
 TEST(NdkBinder, GetLazyService) {
     // Not declared in the vintf manifest
     ASSERT_FALSE(AServiceManager_isDeclared(kLazyBinderNdkUnitTestService));
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 2bf3d03..f1b068e 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -40,6 +40,41 @@
     include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
 }
 
+macro_rules! assert_eq {
+    ($left:expr, $right:expr $(,)?) => {
+        match (&$left, &$right) {
+            (left, right) => {
+                if *left != *right {
+                    eprintln!(
+                        "assertion failed: `{:?}` == `{:?}`, {}:{}:{}",
+                        &*left,
+                        &*right,
+                        file!(),
+                        line!(),
+                        column!()
+                    );
+                    return Err(StatusCode::FAILED_TRANSACTION);
+                }
+            }
+        }
+    };
+}
+
+macro_rules! assert {
+    ($expr:expr) => {
+        if !$expr {
+            eprintln!(
+                "assertion failed: `{:?}`, {}:{}:{}",
+                $expr,
+                file!(),
+                line!(),
+                column!()
+            );
+            return Err(StatusCode::FAILED_TRANSACTION);
+        }
+    };
+}
+
 static SERVICE_ONCE: Once = Once::new();
 static mut SERVICE: Option<SpIBinder> = None;
 
@@ -282,7 +317,7 @@
             ))?;
         }
         bindings::Transaction_TEST_FAIL => {
-            return Err(StatusCode::FAILED_TRANSACTION)
+            assert!(false);
         }
         _ => return Err(StatusCode::UNKNOWN_TRANSACTION),
     }
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 5bccaca..97626be 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
 cc_test {
     name: "ftl_test",
     test_suites: ["device-tests"],
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index debd664..dc91fc6 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -143,7 +143,12 @@
 
     aidl: {
         export_aidl_headers: true,
-    }
+    },
+
+    pgo: {
+        sampling: true,
+        profile_file: "libgui/libgui.profdata",
+    },
 }
 
 // Used by media codec services exclusively as a static lib for
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 2e4f858..05e1935 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -17,15 +17,10 @@
 // tag as surfaceflinger
 #define LOG_TAG "SurfaceFlinger"
 
-#include <stdint.h>
-#include <sys/types.h>
-
 #include <android/gui/ITransactionTraceListener.h>
-
-#include <binder/Parcel.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
-
+#include <binder/Parcel.h>
 #include <gui/IDisplayEventConnection.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/IRegionSamplingListener.h>
@@ -33,16 +28,15 @@
 #include <gui/ISurfaceComposerClient.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/LayerState.h>
-
+#include <stdint.h>
+#include <sys/types.h>
 #include <system/graphics.h>
-
 #include <ui/DisplayMode.h>
 #include <ui/DisplayStatInfo.h>
 #include <ui/DisplayState.h>
 #include <ui/DynamicDisplayInfo.h>
 #include <ui/HdrCapabilities.h>
 #include <ui/StaticDisplayInfo.h>
-
 #include <utils/Log.h>
 
 // ---------------------------------------------------------------------------
@@ -411,24 +405,6 @@
         return static_cast<status_t>(reply.readInt32());
     }
 
-    status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
-                                          bool* outSupport) const override {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        status_t result = data.writeStrongBinder(display);
-        if (result != NO_ERROR) {
-            ALOGE("getAutoLowLatencyModeSupport failed to writeStrongBinder: %d", result);
-            return result;
-        }
-        result = remote()->transact(BnSurfaceComposer::GET_AUTO_LOW_LATENCY_MODE_SUPPORT, data,
-                                    &reply);
-        if (result != NO_ERROR) {
-            ALOGE("getAutoLowLatencyModeSupport failed to transact: %d", result);
-            return result;
-        }
-        return reply.readBool(outSupport);
-    }
-
     void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -454,23 +430,6 @@
         }
     }
 
-    status_t getGameContentTypeSupport(const sp<IBinder>& display,
-                                       bool* outSupport) const override {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        status_t result = data.writeStrongBinder(display);
-        if (result != NO_ERROR) {
-            ALOGE("getGameContentTypeSupport failed to writeStrongBinder: %d", result);
-            return result;
-        }
-        result = remote()->transact(BnSurfaceComposer::GET_GAME_CONTENT_TYPE_SUPPORT, data, &reply);
-        if (result != NO_ERROR) {
-            ALOGE("getGameContentTypeSupport failed to transact: %d", result);
-            return result;
-        }
-        return reply.readBool(outSupport);
-    }
-
     void setGameContentType(const sp<IBinder>& display, bool on) override {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -796,6 +755,33 @@
         return error;
     }
 
+    virtual status_t addFpsListener(const sp<IBinder>& layerHandle,
+                                    const sp<gui::IFpsListener>& listener) {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, layerHandle);
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+        const status_t error =
+                remote()->transact(BnSurfaceComposer::ADD_FPS_LISTENER, data, &reply);
+        if (error != OK) {
+            ALOGE("addFpsListener: Failed to transact");
+        }
+        return error;
+    }
+
+    virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+
+        const status_t error =
+                remote()->transact(BnSurfaceComposer::REMOVE_FPS_LISTENER, data, &reply);
+        if (error != OK) {
+            ALOGE("removeFpsListener: Failed to transact");
+        }
+        return error;
+    }
+
     status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
                                         ui::DisplayModeId defaultMode, bool allowGroupSwitching,
                                         float primaryRefreshRateMin, float primaryRefreshRateMax,
@@ -1423,23 +1409,6 @@
             result = reply->writeInt32(result);
             return result;
         }
-
-        case GET_AUTO_LOW_LATENCY_MODE_SUPPORT: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IBinder> display = nullptr;
-            status_t result = data.readStrongBinder(&display);
-            if (result != NO_ERROR) {
-                ALOGE("getAutoLowLatencyModeSupport failed to readStrongBinder: %d", result);
-                return result;
-            }
-            bool supported = false;
-            result = getAutoLowLatencyModeSupport(display, &supported);
-            if (result == NO_ERROR) {
-                result = reply->writeBool(supported);
-            }
-            return result;
-        }
-
         case SET_AUTO_LOW_LATENCY_MODE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> display = nullptr;
@@ -1457,23 +1426,6 @@
             setAutoLowLatencyMode(display, setAllm);
             return result;
         }
-
-        case GET_GAME_CONTENT_TYPE_SUPPORT: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IBinder> display = nullptr;
-            status_t result = data.readStrongBinder(&display);
-            if (result != NO_ERROR) {
-                ALOGE("getGameContentTypeSupport failed to readStrongBinder: %d", result);
-                return result;
-            }
-            bool supported = false;
-            result = getGameContentTypeSupport(display, &supported);
-            if (result == NO_ERROR) {
-                result = reply->writeBool(supported);
-            }
-            return result;
-        }
-
         case SET_GAME_CONTENT_TYPE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> display = nullptr;
@@ -1491,7 +1443,6 @@
             setGameContentType(display, setGameContentTypeOn);
             return result;
         }
-
         case CLEAR_ANIMATION_FRAME_STATS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             status_t result = clearAnimationFrameStats();
@@ -1716,6 +1667,32 @@
             }
             return removeRegionSamplingListener(listener);
         }
+        case ADD_FPS_LISTENER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> layerHandle;
+            status_t result = data.readNullableStrongBinder(&layerHandle);
+            if (result != NO_ERROR) {
+                ALOGE("addFpsListener: Failed to read layer handle");
+                return result;
+            }
+            sp<gui::IFpsListener> listener;
+            result = data.readNullableStrongBinder(&listener);
+            if (result != NO_ERROR) {
+                ALOGE("addFpsListener: Failed to read listener");
+                return result;
+            }
+            return addFpsListener(layerHandle, listener);
+        }
+        case REMOVE_FPS_LISTENER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<gui::IFpsListener> listener;
+            status_t result = data.readNullableStrongBinder(&listener);
+            if (result != NO_ERROR) {
+                ALOGE("removeFpsListener: Failed to read listener");
+                return result;
+            }
+            return removeFpsListener(listener);
+        }
         case SET_DESIRED_DISPLAY_MODE_SPECS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> displayToken = data.readStrongBinder();
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1a643c2..f5cde59 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1904,22 +1904,10 @@
     return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
 }
 
-bool SurfaceComposerClient::getAutoLowLatencyModeSupport(const sp<IBinder>& display) {
-    bool supported = false;
-    ComposerService::getComposerService()->getAutoLowLatencyModeSupport(display, &supported);
-    return supported;
-}
-
 void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
     ComposerService::getComposerService()->setAutoLowLatencyMode(display, on);
 }
 
-bool SurfaceComposerClient::getGameContentTypeSupport(const sp<IBinder>& display) {
-    bool supported = false;
-    ComposerService::getComposerService()->getGameContentTypeSupport(display, &supported);
-    return supported;
-}
-
 void SurfaceComposerClient::setGameContentType(const sp<IBinder>& display, bool on) {
     ComposerService::getComposerService()->setGameContentType(display, on);
 }
@@ -1994,6 +1982,15 @@
     return ComposerService::getComposerService()->removeRegionSamplingListener(listener);
 }
 
+status_t SurfaceComposerClient::addFpsListener(const sp<IBinder>& layerHandle,
+                                               const sp<gui::IFpsListener>& listener) {
+    return ComposerService::getComposerService()->addFpsListener(layerHandle, listener);
+}
+
+status_t SurfaceComposerClient::removeFpsListener(const sp<gui::IFpsListener>& listener) {
+    return ComposerService::getComposerService()->removeFpsListener(listener);
+}
+
 bool SurfaceComposerClient::getDisplayBrightnessSupport(const sp<IBinder>& displayToken) {
     bool support = false;
     ComposerService::getComposerService()->getDisplayBrightnessSupport(displayToken, &support);
diff --git a/libs/gui/aidl/android/gui/IFpsListener.aidl b/libs/gui/aidl/android/gui/IFpsListener.aidl
new file mode 100644
index 0000000..63d333c
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IFpsListener.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+/** @hide */
+oneway interface IFpsListener {
+
+    // Reports the most recent recorded fps for the tree rooted at this layer
+    void onFpsReported(float fps);
+}
\ No newline at end of file
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index d0ab480..8b64bcf 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -16,21 +16,17 @@
 
 #pragma once
 
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <binder/IBinder.h>
-#include <binder/IInterface.h>
-
+#include <android/gui/IFpsListener.h>
 #include <android/gui/IScreenCaptureListener.h>
 #include <android/gui/ITransactionTraceListener.h>
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
 #include <gui/FrameTimelineInfo.h>
 #include <gui/ITransactionCompletedListener.h>
-
 #include <input/Flags.h>
-
 #include <math/vec4.h>
-
+#include <stdint.h>
+#include <sys/types.h>
 #include <ui/ConfigStoreTypes.h>
 #include <ui/DisplayId.h>
 #include <ui/DisplayMode.h>
@@ -40,7 +36,6 @@
 #include <ui/GraphicTypes.h>
 #include <ui/PixelFormat.h>
 #include <ui/Rotation.h>
-
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
@@ -219,32 +214,17 @@
             ui::ColorMode colorMode) = 0;
 
     /**
-     * Returns true if the connected display reports support for HDMI 2.1 Auto
-     * Low Latency Mode.
-     * For more information, see the HDMI 2.1 specification.
-     */
-    virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
-                                                  bool* outSupport) const = 0;
-
-    /**
      * Switches Auto Low Latency Mode on/off on the connected display, if it is
-     * available. This should only be called if #getAutoLowLatencyMode returns
-     * true.
+     * available. This should only be called if the display supports Auto Low
+     * Latency Mode as reported in #getDynamicDisplayInfo.
      * For more information, see the HDMI 2.1 specification.
      */
     virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) = 0;
 
     /**
-     * Returns true if the connected display reports support for Game Content Type.
-     * For more information, see the HDMI 1.4 specification.
-     */
-    virtual status_t getGameContentTypeSupport(const sp<IBinder>& display,
-                                               bool* outSupport) const = 0;
-
-    /**
      * This will start sending infoframes to the connected display with
-     * ContentType=Game (if on=true). This will switch the disply to Game mode.
-     * This should only be called if #getGameContentTypeSupport returns true.
+     * ContentType=Game (if on=true). This should only be called if the display
+     * Game Content Type as reported in #getDynamicDisplayInfo.
      * For more information, see the HDMI 1.4 specification.
      */
     virtual void setGameContentType(const sp<IBinder>& display, bool on) = 0;
@@ -369,6 +349,23 @@
      */
     virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
 
+    /* Registers a listener that streams fps updates from SurfaceFlinger.
+     *
+     * The listener will stream fps updates for the layer tree rooted at layerHandle. Usually, this
+     * should be tied to a task. Layers that are not descendants of that task are out of scope for
+     * FPS computations.
+     *
+     * Multiple listeners may be supported.
+     *
+     * Requires the ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t addFpsListener(const sp<IBinder>& layerHandle,
+                                    const sp<gui::IFpsListener>& listener) = 0;
+    /*
+     * Removes a listener that was streaming fps updates from SurfaceFlinger.
+     */
+    virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) = 0;
+
     /* Sets the refresh rate boundaries for the display.
      *
      * The primary refresh rate range represents display manager's general guidance on the display
@@ -563,9 +560,9 @@
         CAPTURE_DISPLAY_BY_ID,
         NOTIFY_POWER_BOOST,
         SET_GLOBAL_SHADOW_SETTINGS,
-        GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
+        GET_AUTO_LOW_LATENCY_MODE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
         SET_AUTO_LOW_LATENCY_MODE,
-        GET_GAME_CONTENT_TYPE_SUPPORT,
+        GET_GAME_CONTENT_TYPE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
         SET_GAME_CONTENT_TYPE,
         SET_FRAME_RATE,
         ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN,
@@ -574,6 +571,8 @@
         GET_GPU_CONTEXT_PRIORITY,
         GET_EXTRA_BUFFER_COUNT,
         GET_DYNAMIC_DISPLAY_INFO,
+        ADD_FPS_LISTENER,
+        REMOVE_FPS_LISTENER,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 6f0f18c..ab0347c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -143,17 +143,11 @@
     static status_t setActiveColorMode(const sp<IBinder>& display,
             ui::ColorMode colorMode);
 
-    // Reports whether the connected display supports Auto Low Latency Mode
-    static bool getAutoLowLatencyModeSupport(const sp<IBinder>& display);
-
     // Switches on/off Auto Low Latency Mode on the connected display. This should only be
     // called if the connected display supports Auto Low Latency Mode as reported by
     // #getAutoLowLatencyModeSupport
     static void setAutoLowLatencyMode(const sp<IBinder>& display, bool on);
 
-    // Reports whether the connected display supports Game content type
-    static bool getGameContentTypeSupport(const sp<IBinder>& display);
-
     // Turns Game mode on/off on the connected display. This should only be called
     // if the display supports Game content type, as reported by #getGameContentTypeSupport
     static void setGameContentType(const sp<IBinder>& display, bool on);
@@ -595,6 +589,9 @@
                                               const sp<IBinder>& stopLayerHandle,
                                               const sp<IRegionSamplingListener>& listener);
     static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener);
+    static status_t addFpsListener(const sp<IBinder>& layerHandle,
+                                   const sp<gui::IFpsListener>& listener);
+    static status_t removeFpsListener(const sp<gui::IFpsListener>& listener);
 
 private:
     virtual void onFirstRef();
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 3397198..91b2aff 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -757,15 +757,7 @@
                             const sp<IScreenCaptureListener>& /* captureListener */) override {
         return NO_ERROR;
     }
-    status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/,
-                                          bool* /*outSupport*/) const override {
-        return NO_ERROR;
-    }
     void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
-    status_t getGameContentTypeSupport(const sp<IBinder>& /*display*/,
-                                       bool* /*outSupport*/) const override {
-        return NO_ERROR;
-    }
     void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
     status_t captureDisplay(uint64_t /*displayOrLayerStack*/,
                             const sp<IScreenCaptureListener>& /* captureListener */) override {
@@ -832,6 +824,11 @@
             const sp<IRegionSamplingListener>& /*listener*/) override {
         return NO_ERROR;
     }
+    status_t addFpsListener(const sp<IBinder>& /*layerHandle*/,
+                            const sp<gui::IFpsListener>& /*listener*/) {
+        return NO_ERROR;
+    }
+    status_t removeFpsListener(const sp<gui::IFpsListener>& /*listener*/) { return NO_ERROR; }
     status_t setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/,
                                         ui::DisplayModeId /*defaultMode*/,
                                         bool /*allowGroupSwitching*/,
diff --git a/libs/math/Android.bp b/libs/math/Android.bp
index 3cf9f3f..907eb67 100644
--- a/libs/math/Android.bp
+++ b/libs/math/Android.bp
@@ -39,6 +39,7 @@
         "com.android.media.swcodec",
         "com.android.neuralnetworks",
     ],
+
     min_sdk_version: "29",
 
     export_include_dirs: ["include"],
@@ -49,4 +50,23 @@
     }
 }
 
+cc_library_headers {
+    name: "libmath_headers",
+    export_include_dirs: ["include"],
+    host_supported: true,
+    vendor_available: true,
+
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
+    min_sdk_version: "apex_inherit",
+
+    target:  {
+        windows: {
+            enabled: true,
+        }
+    }
+}
+
 subdirs = ["tests"]
diff --git a/libs/math/OWNERS b/libs/math/OWNERS
index 6fb149a..72d33bc 100644
--- a/libs/math/OWNERS
+++ b/libs/math/OWNERS
@@ -1,6 +1,3 @@
-jaesoo@google.com
-jiyong@google.com
 mathias@google.com
-pawin@google.com
 randolphs@google.com
 romainguy@google.com
diff --git a/libs/math/include/math/HashCombine.h b/libs/math/include/math/HashCombine.h
new file mode 100644
index 0000000..e91b52b
--- /dev/null
+++ b/libs/math/include/math/HashCombine.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021 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 <functional>
+
+namespace android {
+static inline void hashCombineSingleHashed(size_t& combinedHash, size_t hash) {
+    combinedHash = 31 * combinedHash + hash;
+}
+
+template<typename T>
+static inline void hashCombineSingle(size_t& combinedHash, const T& val) {
+    hashCombineSingleHashed(combinedHash, std::hash<T>{}(val));
+}
+
+template<typename... Types>
+static inline size_t hashCombine(const Types& ... args) {
+    size_t hash = 0;
+    ( hashCombineSingle(hash, args), ... );
+    return hash;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h
index 20f852f..0dac662 100644
--- a/libs/math/include/math/TVecHelpers.h
+++ b/libs/math/include/math/TVecHelpers.h
@@ -19,9 +19,11 @@
 
 #include <math.h>
 #include <stdint.h>
+#include <math/HashCombine.h>
 #include <sys/types.h>
 
 #include <cmath>
+#include <functional>
 #include <limits>
 #include <iostream>
 
@@ -250,6 +252,17 @@
         }
         return r;
     }
+
+    // This isn't strictly a unary operator, but it is a common place shared between both
+    // matrix and vector classes
+    size_t hash() const {
+        VECTOR<T> const& rv(static_cast<VECTOR<T> const&>(*this));
+        size_t hashed = 0;
+        for (size_t i = 0; i < rv.size(); i++) {
+            android::hashCombineSingle(hashed, rv[i]);
+        }
+        return hashed;
+    }
 };
 
 /*
@@ -606,3 +619,16 @@
 // -------------------------------------------------------------------------------------
 }  // namespace details
 }  // namespace android
+
+namespace std {
+    template<template<typename T> class VECTOR, typename T>
+    struct hash<VECTOR<T>> {
+        static constexpr bool IS_VECTOR =
+            std::is_base_of<android::details::TVecUnaryOperators<VECTOR, T>, VECTOR<T>>::value;
+
+        typename std::enable_if<IS_VECTOR, size_t>::type
+        operator()(const VECTOR<T>& v) const {
+            return v.hash();
+        }
+    };
+}
diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h
index 617a0ab..5ec9bf7 100644
--- a/libs/math/include/math/half.h
+++ b/libs/math/include/math/half.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <stdint.h>
+#include <functional>
 #include <iosfwd>
 #include <limits>
 #include <type_traits>
@@ -202,6 +203,12 @@
     inline static constexpr type signaling_NaN() noexcept { return android::half(android::half::binary, 0x7dff); }
 };
 
+template<> struct hash<android::half> {
+    size_t operator()(const android::half& half) {
+        return std::hash<float>{}(half);
+    }
+};
+
 } // namespace std
 
 #ifdef LIKELY_DEFINED_LOCAL
diff --git a/libs/math/tests/Android.bp b/libs/math/tests/Android.bp
index 4a7c4dd..14fb72a 100644
--- a/libs/math/tests/Android.bp
+++ b/libs/math/tests/Android.bp
@@ -50,3 +50,10 @@
     static_libs: ["libmath"],
     cflags: ["-Wall", "-Werror"],
 }
+
+cc_test {
+    name: "hashcombine_test",
+    srcs: ["hashcombine_test.cpp"],
+    static_libs: ["libmath"],
+    cflags: ["-Wall", "-Werror"],
+}
diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp
index 604072e..a514d98 100644
--- a/libs/math/tests/half_test.cpp
+++ b/libs/math/tests/half_test.cpp
@@ -94,4 +94,13 @@
     EXPECT_EQ(f4.xy, h2);
 }
 
+
+TEST_F(HalfTest, Hash) {
+    float4 f4a(1,2,3,4);
+    float4 f4b(2,2,3,4);
+    half4 h4a(f4a), h4b(f4b);
+
+    EXPECT_NE(std::hash<half4>{}(h4a), std::hash<half4>{}(h4b));
+}
+
 }; // namespace android
diff --git a/libs/math/tests/hashcombine_test.cpp b/libs/math/tests/hashcombine_test.cpp
new file mode 100644
index 0000000..96c6e81
--- /dev/null
+++ b/libs/math/tests/hashcombine_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HashCombineTest"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include <math/half.h>
+#include <math/vec4.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class HashCombineTest : public testing::Test {
+protected:
+};
+
+TEST_F(HashCombineTest, Basics) {
+    char a = 40;
+    int b = 32;
+    int c = 55;
+    float d = 42.f;
+    float d_ = 42.1f;
+
+    EXPECT_NE(hashCombine(a, b, c, d), hashCombine(a, b, c, d_));
+}
+
+}; // namespace android
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 327b04c..6c6db46 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -404,10 +404,6 @@
     return true;
 }
 
-static bool hasUsage(const AHardwareBuffer_Desc& desc, uint64_t usage) {
-    return !!(desc.usage & usage);
-}
-
 static float toDegrees(uint32_t transform) {
     switch (transform) {
         case ui::Transform::ROT_90:
@@ -454,11 +450,6 @@
             sourceTransfer != destTransfer;
 }
 
-static bool needsLinearEffect(const mat4& colorTransform, ui::Dataspace sourceDataspace,
-                              ui::Dataspace destinationDataspace) {
-    return colorTransform != mat4() || needsToneMapping(sourceDataspace, destinationDataspace);
-}
-
 void SkiaGLRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
     // Only run this if RE is running on its own thread. This way the access to GL
     // operations is guaranteed to be happening on the same thread.
@@ -491,14 +482,19 @@
 sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader,
                                                               const LayerSettings* layer,
                                                               const DisplaySettings& display,
-                                                              bool undoPremultipliedAlpha) {
+                                                              bool undoPremultipliedAlpha,
+                                                              bool requiresLinearEffect) {
     if (layer->stretchEffect.hasEffect()) {
         // TODO: Implement
     }
-    if (mUseColorManagement &&
-        needsLinearEffect(layer->colorTransform, layer->sourceDataspace, display.outputDataspace)) {
-        LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace,
-                                           .outputDataspace = display.outputDataspace,
+    if (requiresLinearEffect) {
+        const ui::Dataspace inputDataspace =
+                mUseColorManagement ? layer->sourceDataspace : ui::Dataspace::UNKNOWN;
+        const ui::Dataspace outputDataspace =
+                mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
+
+        LinearEffect effect = LinearEffect{.inputDataspace = inputDataspace,
+                                           .outputDataspace = outputDataspace,
                                            .undoPremultipliedAlpha = undoPremultipliedAlpha};
 
         auto effectIter = mRuntimeEffects.find(effect);
@@ -556,6 +552,26 @@
     canvas->translate(-display.clip.left, -display.clip.top);
 }
 
+class AutoSaveRestore {
+public:
+    AutoSaveRestore(SkCanvas* canvas) : mCanvas(canvas) { mSaveCount = canvas->save(); }
+    ~AutoSaveRestore() { restore(); }
+    void replace(SkCanvas* canvas) {
+        mCanvas = canvas;
+        mSaveCount = canvas->save();
+    }
+    void restore() {
+        if (mCanvas) {
+            mCanvas->restoreToCount(mSaveCount);
+            mCanvas = nullptr;
+        }
+    }
+
+private:
+    SkCanvas* mCanvas;
+    int mSaveCount;
+};
+
 status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display,
                                         const std::vector<const LayerSettings*>& layers,
                                         const sp<GraphicBuffer>& buffer,
@@ -586,8 +602,6 @@
     auto& cache = mInProtectedContext ? mProtectedTextureCache : mTextureCache;
     AHardwareBuffer_Desc bufferDesc;
     AHardwareBuffer_describe(buffer->toAHardwareBuffer(), &bufferDesc);
-    LOG_ALWAYS_FATAL_IF(!hasUsage(bufferDesc, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE),
-                        "missing usage");
 
     std::shared_ptr<AutoBackendTexture::LocalRef> surfaceTextureRef = nullptr;
     if (useFramebufferCache) {
@@ -610,11 +624,10 @@
         }
     }
 
+    const ui::Dataspace dstDataspace =
+            mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN;
     sk_sp<SkSurface> dstSurface =
-            surfaceTextureRef->getTexture()->getOrCreateSurface(mUseColorManagement
-                                                                        ? display.outputDataspace
-                                                                        : ui::Dataspace::UNKNOWN,
-                                                                grContext.get());
+            surfaceTextureRef->getTexture()->getOrCreateSurface(dstDataspace, grContext.get());
 
     SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
     if (dstCanvas == nullptr) {
@@ -650,7 +663,7 @@
         }
     }
 
-    canvas->save();
+    AutoSaveRestore surfaceAutoSaveRestore(canvas);
     // Clear the entire canvas with a transparent black to prevent ghost images.
     canvas->clear(SK_ColorTRANSPARENT);
     initCanvas(canvas, display);
@@ -671,13 +684,18 @@
         SkPaint paint;
         sk_sp<SkShader> shader =
                 SkShaders::Color(SkColor4f{.fR = 0., .fG = 0., .fB = 0., .fA = 1.0},
-                                 toSkColorSpace(mUseColorManagement ? display.outputDataspace
-                                                                    : ui::Dataspace::UNKNOWN));
+                                 toSkColorSpace(dstDataspace));
         paint.setShader(shader);
         clearRegion.setRects(skRects, numRects);
         canvas->drawRegion(clearRegion, paint);
     }
 
+    // setup color filter if necessary
+    sk_sp<SkColorFilter> displayColorTransform;
+    if (display.colorTransform != mat4()) {
+        displayColorTransform = SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform));
+    }
+
     for (const auto& layer : layers) {
         ATRACE_NAME("DrawLayer");
 
@@ -705,7 +723,7 @@
 
             // assign dstCanvas to canvas and ensure that the canvas state is up to date
             canvas = dstCanvas;
-            canvas->save();
+            surfaceAutoSaveRestore.replace(canvas);
             initCanvas(canvas, display);
 
             LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() !=
@@ -717,7 +735,7 @@
             activeSurface = dstSurface;
         }
 
-        canvas->save();
+        SkAutoCanvasRestore layerAutoSaveRestore(canvas, true);
         if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
             // Record the name of the layer if the capture is running.
             std::stringstream layerSettings;
@@ -765,15 +783,34 @@
             }
         }
 
-        const ui::Dataspace targetDataspace = mUseColorManagement
-                ? (needsLinearEffect(layer->colorTransform, layer->sourceDataspace,
-                                     display.outputDataspace)
-                           // If we need to map to linear space, then mark the source image with the
-                           // same colorspace as the destination surface so that Skia's color
-                           // management is a no-op.
-                           ? display.outputDataspace
-                           : layer->sourceDataspace)
-                : ui::Dataspace::UNKNOWN;
+        // Shadows are assumed to live only on their own layer - it's not valid
+        // to draw the boundary rectangles when there is already a caster shadow
+        // TODO(b/175915334): consider relaxing this restriction to enable more flexible
+        // composition - using a well-defined invalid color is long-term less error-prone.
+        if (layer->shadow.length > 0) {
+            const auto rect = layer->geometry.roundedCornersRadius > 0
+                    ? getSkRect(layer->geometry.roundedCornersCrop)
+                    : bounds;
+            drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
+            continue;
+        }
+
+        const bool requiresLinearEffect = layer->colorTransform != mat4() ||
+                (mUseColorManagement &&
+                 needsToneMapping(layer->sourceDataspace, display.outputDataspace));
+
+        // quick abort from drawing the remaining portion of the layer
+        if (layer->alpha == 0 && !requiresLinearEffect &&
+            (!displayColorTransform || displayColorTransform->isAlphaUnchanged())) {
+            continue;
+        }
+
+        // If we need to map to linear space or color management is disabled, then mark the source
+        // image with the same colorspace as the destination surface so that Skia's color
+        // management is a no-op.
+        const ui::Dataspace layerDataspace = (!mUseColorManagement || requiresLinearEffect)
+                ? dstDataspace
+                : layer->sourceDataspace;
 
         SkPaint paint;
         if (layer->source.buffer.buffer) {
@@ -792,7 +829,7 @@
             }
 
             sk_sp<SkImage> image =
-                    imageTextureRef->getTexture()->makeImage(targetDataspace,
+                    imageTextureRef->getTexture()->makeImage(layerDataspace,
                                                              item.usePremultipliedAlpha
                                                                      ? kPremul_SkAlphaType
                                                                      : kUnpremul_SkAlphaType,
@@ -848,12 +885,12 @@
             if (item.isOpaque) {
                 shader = SkShaders::Blend(SkBlendMode::kPlus, shader,
                                           SkShaders::Color(SkColors::kBlack,
-                                                           toSkColorSpace(targetDataspace)));
+                                                           toSkColorSpace(layerDataspace)));
             }
 
-            paint.setShader(
-                    createRuntimeEffectShader(shader, layer, display,
-                                              !item.isOpaque && item.usePremultipliedAlpha));
+            paint.setShader(createRuntimeEffectShader(shader, layer, display,
+                                                      !item.isOpaque && item.usePremultipliedAlpha,
+                                                      requiresLinearEffect));
             paint.setAlphaf(layer->alpha);
         } else {
             ATRACE_NAME("DrawColor");
@@ -862,35 +899,22 @@
                                                                 .fG = color.g,
                                                                 .fB = color.b,
                                                                 .fA = layer->alpha},
-                                                      toSkColorSpace(targetDataspace));
+                                                      toSkColorSpace(layerDataspace));
             paint.setShader(createRuntimeEffectShader(shader, layer, display,
-                                                      /* undoPremultipliedAlpha */ false));
+                                                      /* undoPremultipliedAlpha */ false,
+                                                      requiresLinearEffect));
         }
 
-        sk_sp<SkColorFilter> filter =
-                SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform));
+        paint.setColorFilter(displayColorTransform);
 
-        paint.setColorFilter(filter);
-
-        if (layer->shadow.length > 0) {
-            const auto rect = layer->geometry.roundedCornersRadius > 0
-                    ? getSkRect(layer->geometry.roundedCornersCrop)
-                    : bounds;
-            drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow);
+        if (layer->geometry.roundedCornersRadius > 0) {
+            paint.setAntiAlias(true);
+            canvas->drawRRect(getRoundedRect(layer), paint);
         } else {
-            // Shadows are assumed to live only on their own layer - it's not valid
-            // to draw the boundary retangles when there is already a caster shadow
-            // TODO(b/175915334): consider relaxing this restriction to enable more flexible
-            // composition - using a well-defined invalid color is long-term less error-prone.
-            // Push the clipRRect onto the clip stack. Draw the image. Pop the clip.
-            if (layer->geometry.roundedCornersRadius > 0) {
-                canvas->clipRRect(getRoundedRect(layer), true);
-            }
             canvas->drawRect(bounds, paint);
         }
-        canvas->restore();
     }
-    canvas->restore();
+    surfaceAutoSaveRestore.restore();
     mCapture->endCapture();
     {
         ATRACE_NAME("flush surface");
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 5779ae6..ad26206 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -92,11 +92,12 @@
     void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
     void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius,
                     const ShadowSettings& shadowSettings);
-    // If mUseColorManagement is correct and layer needsLinearEffect, it returns a linear runtime
-    // shader. Otherwise it returns the input shader.
+    // If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
+    // Otherwise it returns the input shader.
     sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer,
                                               const DisplaySettings& display,
-                                              bool undoPremultipliedAlpha);
+                                              bool undoPremultipliedAlpha,
+                                              bool requiresLinearEffect);
 
     EGLDisplay mEGLDisplay;
     EGLContext mEGLContext;
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index ac0ae72..de2b9a4 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -238,9 +238,11 @@
     },
     header_libs: [
         "libnativewindow_headers",
+        "libmath_headers",
     ],
     export_header_lib_headers: [
         "libnativewindow_headers",
+        "libmath_headers",
     ],
     min_sdk_version: "29",
 }
diff --git a/libs/ui/DynamicDisplayInfo.cpp b/libs/ui/DynamicDisplayInfo.cpp
index 11acdae..d5c4ef0 100644
--- a/libs/ui/DynamicDisplayInfo.cpp
+++ b/libs/ui/DynamicDisplayInfo.cpp
@@ -39,7 +39,9 @@
             FlattenableHelpers::getFlattenedSize(activeDisplayModeId) +
             FlattenableHelpers::getFlattenedSize(supportedColorModes) +
             FlattenableHelpers::getFlattenedSize(activeColorMode) +
-            FlattenableHelpers::getFlattenedSize(hdrCapabilities);
+            FlattenableHelpers::getFlattenedSize(hdrCapabilities) +
+            FlattenableHelpers::getFlattenedSize(autoLowLatencyModeSupported) +
+            FlattenableHelpers::getFlattenedSize(gameContentTypeSupported);
 }
 
 status_t DynamicDisplayInfo::flatten(void* buffer, size_t size) const {
@@ -51,6 +53,8 @@
     RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, supportedColorModes));
     RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, activeColorMode));
     RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, hdrCapabilities));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, autoLowLatencyModeSupported));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, gameContentTypeSupported));
     return OK;
 }
 
@@ -60,6 +64,8 @@
     RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &supportedColorModes));
     RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &activeColorMode));
     RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &hdrCapabilities));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &autoLowLatencyModeSupported));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &gameContentTypeSupported));
     return OK;
 }
 
diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS
index 5110a6c..a0b5fe7 100644
--- a/libs/ui/OWNERS
+++ b/libs/ui/OWNERS
@@ -1,3 +1,4 @@
+adyabr@google.com
 alecmouri@google.com
 chrisforbes@google.com
 jreck@google.com
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
index 6c349b7..a4c2f71 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -41,6 +41,14 @@
     ui::ColorMode activeColorMode;
     HdrCapabilities hdrCapabilities;
 
+    // True if the display reports support for HDMI 2.1 Auto Low Latency Mode.
+    // For more information, see the HDMI 2.1 specification.
+    bool autoLowLatencyModeSupported;
+
+    // True if the display reports support for Game Content Type.
+    // For more information, see the HDMI 1.4 specification.
+    bool gameContentTypeSupported;
+
     std::optional<ui::DisplayMode> getActiveDisplayMode() const;
 
     bool isFixedSize() const { return false; }
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index 5d329ea..4c9c7b7 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <math/HashCombine.h>
 #include <ostream>
 
 namespace android {
@@ -62,3 +63,13 @@
 }
 
 }  // namespace android
+
+namespace std {
+
+template <>
+struct hash<android::FloatRect> {
+    size_t operator()(const android::FloatRect& rect) const {
+        return android::hashCombine(rect.left, rect.top, rect.right, rect.bottom);
+    }
+};
+} // namespace std
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 58323e5..9e24a07 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -24,6 +24,7 @@
 #include <utils/Log.h>
 #include <utils/TypeHelpers.h>
 
+#include <math/HashCombine.h>
 #include <ui/FloatRect.h>
 #include <ui/Point.h>
 #include <ui/Size.h>
@@ -234,4 +235,13 @@
 
 }; // namespace android
 
+namespace std {
+template <>
+struct hash<android::Rect> {
+    size_t operator()(const android::Rect& rect) const {
+        return android::hashCombine(rect.left, rect.top, rect.right, rect.bottom);
+    }
+};
+} // namespace std
+
 #endif // ANDROID_UI_RECT
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 6bb7b8d..927c334 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -21,6 +21,7 @@
 #include <sys/types.h>
 #include <ostream>
 
+#include <math/HashCombine.h>
 #include <ui/Rect.h>
 #include <utils/Flattenable.h>
 
@@ -234,4 +235,17 @@
 // ---------------------------------------------------------------------------
 }; // namespace android
 
+namespace std {
+template <>
+struct hash<android::Region> {
+    size_t operator()(const android::Region& region) const {
+        size_t hash = 0;
+        for (const android::Rect& rect : region) {
+            android::hashCombineSingle(hash, rect);
+        }
+        return hash;
+    }
+};
+} // namespace std
+
 #endif // ANDROID_UI_REGION_H
diff --git a/libs/ui/tests/Rect_test.cpp b/libs/ui/tests/Rect_test.cpp
index 5499a5b..9cc36bb 100644
--- a/libs/ui/tests/Rect_test.cpp
+++ b/libs/ui/tests/Rect_test.cpp
@@ -259,4 +259,33 @@
     EXPECT_EQ(FloatRect(10.f, 20.f, 50.f, 60.f), floatRect);
 }
 
+TEST(RectTest, RectHash) {
+    const std::vector<Rect> rects = {
+            Rect(10, 20, 50, 60), Rect(11, 20, 50, 60), Rect(11, 21, 50, 60),
+            Rect(11, 21, 51, 60), Rect(11, 21, 51, 61),
+    };
+
+    for (const auto& a : rects) {
+        for (const auto& b : rects) {
+            const bool hashEq = std::hash<Rect>{}(a) == std::hash<Rect>{}(b);
+            EXPECT_EQ(a == b, hashEq);
+        }
+    }
+}
+
+TEST(RectTest, FloatRectHash) {
+    const std::vector<FloatRect> floatRects = {
+            Rect(10, 20, 50, 60).toFloatRect(), Rect(11, 20, 50, 60).toFloatRect(),
+            Rect(11, 21, 50, 60).toFloatRect(), Rect(11, 21, 51, 60).toFloatRect(),
+            Rect(11, 21, 51, 61).toFloatRect(),
+    };
+
+    for (const auto& a : floatRects) {
+        for (const auto& b : floatRects) {
+            const bool hashEq = std::hash<FloatRect>{}(a) == std::hash<FloatRect>{}(b);
+            EXPECT_EQ(a == b, hashEq);
+        }
+    }
+}
+
 } // namespace android::ui
diff --git a/libs/ui/tests/Region_test.cpp b/libs/ui/tests/Region_test.cpp
index c6b826d..74924bd 100644
--- a/libs/ui/tests/Region_test.cpp
+++ b/libs/ui/tests/Region_test.cpp
@@ -167,5 +167,17 @@
     ASSERT_TRUE(touchableRegion.contains(50, 50));
 }
 
+TEST_F(RegionTest, RegionHash) {
+    Region region1;
+    region1.addRectUnchecked(10, 20, 30, 40);
+    region1.addRectUnchecked(40, 30, 20, 10);
+
+    Region region2;
+    region2.addRectUnchecked(11, 20, 30, 40);
+    region2.addRectUnchecked(40, 31, 20, 10);
+
+    EXPECT_NE(std::hash<Region>{}(region1), std::hash<Region>{}(region2));
+}
+
 }; // namespace android
 
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 4b1f350..33b3e1e 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -50,9 +50,10 @@
 
 // --- NotifyKeyArgs ---
 
-NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                             int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
-                             int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime)
+NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+                             uint32_t source, int32_t displayId, uint32_t policyFlags,
+                             int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+                             int32_t metaState, nsecs_t downTime)
       : NotifyArgs(id, eventTime),
         deviceId(deviceId),
         source(source),
@@ -63,7 +64,8 @@
         keyCode(keyCode),
         scanCode(scanCode),
         metaState(metaState),
-        downTime(downTime) {}
+        downTime(downTime),
+        readTime(readTime) {}
 
 NotifyKeyArgs::NotifyKeyArgs(const NotifyKeyArgs& other)
       : NotifyArgs(other.id, other.eventTime),
@@ -76,13 +78,15 @@
         keyCode(other.keyCode),
         scanCode(other.scanCode),
         metaState(other.metaState),
-        downTime(other.downTime) {}
+        downTime(other.downTime),
+        readTime(other.readTime) {}
 
 bool NotifyKeyArgs::operator==(const NotifyKeyArgs& rhs) const {
-    return id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId &&
-            source == rhs.source && displayId == rhs.displayId && policyFlags == rhs.policyFlags &&
-            action == rhs.action && flags == rhs.flags && keyCode == rhs.keyCode &&
-            scanCode == rhs.scanCode && metaState == rhs.metaState && downTime == rhs.downTime;
+    return id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+            deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
+            policyFlags == rhs.policyFlags && action == rhs.action && flags == rhs.flags &&
+            keyCode == rhs.keyCode && scanCode == rhs.scanCode && metaState == rhs.metaState &&
+            downTime == rhs.downTime;
 }
 
 void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
@@ -91,16 +95,14 @@
 
 // --- NotifyMotionArgs ---
 
-NotifyMotionArgs::NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                                   int32_t displayId, uint32_t policyFlags, int32_t action,
-                                   int32_t actionButton, int32_t flags, int32_t metaState,
-                                   int32_t buttonState, MotionClassification classification,
-                                   int32_t edgeFlags, uint32_t pointerCount,
-                                   const PointerProperties* pointerProperties,
-                                   const PointerCoords* pointerCoords, float xPrecision,
-                                   float yPrecision, float xCursorPosition, float yCursorPosition,
-                                   nsecs_t downTime,
-                                   const std::vector<TouchVideoFrame>& videoFrames)
+NotifyMotionArgs::NotifyMotionArgs(
+        int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, uint32_t source,
+        int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
+        int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification,
+        int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties,
+        const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
+        float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+        const std::vector<TouchVideoFrame>& videoFrames)
       : NotifyArgs(id, eventTime),
         deviceId(deviceId),
         source(source),
@@ -119,6 +121,7 @@
         xCursorPosition(xCursorPosition),
         yCursorPosition(yCursorPosition),
         downTime(downTime),
+        readTime(readTime),
         videoFrames(videoFrames) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
@@ -145,6 +148,7 @@
         xCursorPosition(other.xCursorPosition),
         yCursorPosition(other.yCursorPosition),
         downTime(other.downTime),
+        readTime(other.readTime),
         videoFrames(other.videoFrames) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         pointerProperties[i].copyFrom(other.pointerProperties[i]);
@@ -157,11 +161,12 @@
 }
 
 bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const {
-    bool equal = id == rhs.id && eventTime == rhs.eventTime && deviceId == rhs.deviceId &&
-            source == rhs.source && displayId == rhs.displayId && policyFlags == rhs.policyFlags &&
-            action == rhs.action && actionButton == rhs.actionButton && flags == rhs.flags &&
-            metaState == rhs.metaState && buttonState == rhs.buttonState &&
-            classification == rhs.classification && edgeFlags == rhs.edgeFlags &&
+    bool equal = id == rhs.id && eventTime == rhs.eventTime && readTime == rhs.readTime &&
+            deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId &&
+            policyFlags == rhs.policyFlags && action == rhs.action &&
+            actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState &&
+            buttonState == rhs.buttonState && classification == rhs.classification &&
+            edgeFlags == rhs.edgeFlags &&
             pointerCount == rhs.pointerCount
             // PointerProperties and PointerCoords are compared separately below
             && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision &&
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index d6b7259..b2ddb42 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -105,7 +105,7 @@
 
     void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
 
-    void pokeUserActivity(nsecs_t, int32_t) override {}
+    void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
 
     bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
 
@@ -249,8 +249,9 @@
 
     const nsecs_t currentTime = now();
     // Define a valid motion event.
-    NotifyMotionArgs args(/* id */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
-                          ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER, AMOTION_EVENT_ACTION_DOWN,
+    NotifyMotionArgs args(/* id */ 0, currentTime, currentTime, DEVICE_ID,
+                          AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER,
+                          AMOTION_EVENT_ACTION_DOWN,
                           /* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0,
                           MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
                           pointerProperties, pointerCoords,
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 45a007b..ed17e68 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -273,6 +273,7 @@
     bool enabled;
     int32_t pid;
     nsecs_t consumeTime; // time when the event was consumed by InputConsumer
+    int32_t displayId;
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 9898e9d..d0a5c09 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -2591,6 +2591,7 @@
             std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);
     commandEntry->eventTime = eventEntry.eventTime;
     commandEntry->userActivityEventType = eventType;
+    commandEntry->displayId = displayId;
     postCommandLocked(std::move(commandEntry));
 }
 
@@ -5715,7 +5716,8 @@
 void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) {
     mLock.unlock();
 
-    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);
+    mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType,
+                              commandEntry->displayId);
 
     mLock.lock();
 }
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 909ce54..439d85e 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -134,7 +134,7 @@
                               uint32_t policyFlags) = 0;
 
     /* Poke user activity for an event dispatched to a window. */
-    virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0;
+    virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) = 0;
 
     /* Checks whether a given application pid/uid has permission to inject input events
      * into other applications.
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index 094bc0c..4b7d26d 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -73,12 +73,14 @@
     int32_t scanCode;
     int32_t metaState;
     nsecs_t downTime;
+    nsecs_t readTime;
 
     inline NotifyKeyArgs() { }
 
-    NotifyKeyArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                  int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
-                  int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime);
+    NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+                  uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
+                  int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
+                  nsecs_t downTime);
 
     bool operator==(const NotifyKeyArgs& rhs) const;
 
@@ -120,13 +122,14 @@
     float xCursorPosition;
     float yCursorPosition;
     nsecs_t downTime;
+    nsecs_t readTime;
     std::vector<TouchVideoFrame> videoFrames;
 
     inline NotifyMotionArgs() { }
 
-    NotifyMotionArgs(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
-                     int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
-                     int32_t flags, int32_t metaState, int32_t buttonState,
+    NotifyMotionArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
+                     uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
+                     int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
                      MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount,
                      const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
                      float xPrecision, float yPrecision, float xCursorPosition,
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index e939d1c..d0a9ca7 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -37,6 +37,7 @@
 
 // #define LOG_NDEBUG 0
 #include <android-base/file.h>
+#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
 #include <cutils/properties.h>
 #include <input/KeyCharacterMap.h>
@@ -1361,13 +1362,14 @@
 
     // Some devices report battery capacity as an integer through the "capacity" file
     if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity", &buffer)) {
-        return std::stoi(buffer);
+        return std::stoi(base::Trim(buffer));
     }
 
     // Other devices report capacity as an enum value POWER_SUPPLY_CAPACITY_LEVEL_XXX
     // These values are taken from kernel source code include/linux/power_supply.h
     if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity_level", &buffer)) {
-        const auto it = BATTERY_LEVEL.find(buffer);
+        // Remove any white space such as trailing new line
+        const auto it = BATTERY_LEVEL.find(base::Trim(buffer));
         if (it != BATTERY_LEVEL.end()) {
             return it->second;
         }
@@ -1389,9 +1391,8 @@
         return std::nullopt;
     }
 
-    // Remove trailing new line
-    buffer.erase(std::remove(buffer.begin(), buffer.end(), '\n'), buffer.end());
-    const auto it = BATTERY_STATUS.find(buffer);
+    // Remove white space like trailing new line
+    const auto it = BATTERY_STATUS.find(base::Trim(buffer));
 
     if (it != BATTERY_STATUS.end()) {
         return it->second;
@@ -1567,6 +1568,7 @@
                     for (size_t i = 0; i < count; i++) {
                         struct input_event& iev = readBuffer[i];
                         event->when = processEventTimestamp(iev);
+                        event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
                         event->deviceId = deviceId;
                         event->type = iev.type;
                         event->code = iev.code;
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index cbf3b69..dd1abeb 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -506,8 +506,8 @@
     for_each_mapper([sensorType](InputMapper& mapper) { mapper.flushSensor(sensorType); });
 }
 
-void InputDevice::cancelTouch(nsecs_t when) {
-    for_each_mapper([when](InputMapper& mapper) { mapper.cancelTouch(when); });
+void InputDevice::cancelTouch(nsecs_t when, nsecs_t readTime) {
+    for_each_mapper([when, readTime](InputMapper& mapper) { mapper.cancelTouch(when, readTime); });
 }
 
 std::optional<int32_t> InputDevice::getBatteryCapacity() {
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index e6164d3..4059729 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -53,7 +53,11 @@
  * A raw event as retrieved from the EventHub.
  */
 struct RawEvent {
+    // Time when the event happened
     nsecs_t when;
+    // Time when the event was read by EventHub. Only populated for input events.
+    // For other events (device added/removed/etc), this value is undefined and should not be read.
+    nsecs_t readTime;
     int32_t deviceId;
     int32_t type;
     int32_t code;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 34c330b..863cd41 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -86,7 +86,7 @@
     void cancelVibrate(int32_t token);
     bool isVibrating();
     std::vector<int32_t> getVibratorIds();
-    void cancelTouch(nsecs_t when);
+    void cancelTouch(nsecs_t when, nsecs_t readTime);
     bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
                       std::chrono::microseconds maxBatchReportLatency);
     void disableSensor(InputDeviceSensorType sensorType);
@@ -351,7 +351,7 @@
     inline std::optional<DisplayViewport> getAssociatedViewport() const {
         return mDevice.getAssociatedViewport();
     }
-    inline void cancelTouch(nsecs_t when) { mDevice.cancelTouch(when); }
+    inline void cancelTouch(nsecs_t when, nsecs_t readTime) { mDevice.cancelTouch(when, readTime); }
     inline void bumpGeneration() { mDevice.bumpGeneration(); }
     inline const PropertyMap& getConfiguration() { return mDevice.getConfiguration(); }
 
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 254b64b..bb12be7 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -265,11 +265,11 @@
     mCursorScrollAccumulator.process(rawEvent);
 
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when);
+        sync(rawEvent->when, rawEvent->readTime);
     }
 }
 
-void CursorInputMapper::sync(nsecs_t when) {
+void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
     int32_t lastButtonState = mButtonState;
     int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
     mButtonState = currentButtonState;
@@ -362,8 +362,8 @@
     }
 
     // Synthesize key down from buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
-                         displayId, policyFlags, lastButtonState, currentButtonState);
+    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
+                         mSource, displayId, policyFlags, lastButtonState, currentButtonState);
 
     // Send motion event.
     if (downChanged || moved || scrolled || buttonsChanged) {
@@ -383,8 +383,8 @@
             while (!released.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
                 buttonState &= ~actionButton;
-                NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, getDeviceId(),
-                                             mSource, displayId, policyFlags,
+                NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime,
+                                             getDeviceId(), mSource, displayId, policyFlags,
                                              AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
                                              metaState, buttonState, MotionClassification::NONE,
                                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -395,11 +395,11 @@
             }
         }
 
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, motionEventAction, 0, 0, metaState, currentButtonState,
-                              MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
-                              &pointerProperties, &pointerCoords, mXPrecision, mYPrecision,
-                              xCursorPosition, yCursorPosition, downTime,
+        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                              displayId, policyFlags, motionEventAction, 0, 0, metaState,
+                              currentButtonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
+                              mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
                               /* videoFrames */ {});
         getListener()->notifyMotion(&args);
 
@@ -408,8 +408,8 @@
             while (!pressed.isEmpty()) {
                 int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
                 buttonState |= actionButton;
-                NotifyMotionArgs pressArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
-                                           displayId, policyFlags,
+                NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                           mSource, displayId, policyFlags,
                                            AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
                                            metaState, buttonState, MotionClassification::NONE,
                                            AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
@@ -424,9 +424,10 @@
 
         // Send hover move after UP to tell the application that the mouse is hovering now.
         if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
-            NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
-                                       displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
-                                       0, metaState, currentButtonState, MotionClassification::NONE,
+            NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                       mSource, displayId, policyFlags,
+                                       AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
+                                       currentButtonState, MotionClassification::NONE,
                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
                                        yCursorPosition, downTime, /* videoFrames */ {});
@@ -438,9 +439,10 @@
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-            NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
-                                        displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
-                                        metaState, currentButtonState, MotionClassification::NONE,
+            NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                        mSource, displayId, policyFlags,
+                                        AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+                                        currentButtonState, MotionClassification::NONE,
                                         AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                         &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
                                         yCursorPosition, downTime, /* videoFrames */ {});
@@ -449,7 +451,7 @@
     }
 
     // Synthesize key up from buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
+    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
                          displayId, policyFlags, lastButtonState, currentButtonState);
 
     mCursorMotionAccumulator.finishSync();
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 05bbb26..9a8ca01 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -114,7 +114,7 @@
     void configureParameters();
     void dumpParameters(std::string& dump);
 
-    void sync(nsecs_t when);
+    void sync(nsecs_t when, nsecs_t readTime);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 1ce54ae..df1acd4 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -68,7 +68,7 @@
     return {};
 }
 
-void InputMapper::cancelTouch(nsecs_t when) {}
+void InputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {}
 
 bool InputMapper::enableSensor(InputDeviceSensorType sensorType,
                                std::chrono::microseconds samplingPeriod,
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index bd543e5..15cff1c 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -67,7 +67,7 @@
     virtual void cancelVibrate(int32_t token);
     virtual bool isVibrating();
     virtual std::vector<int32_t> getVibratorIds();
-    virtual void cancelTouch(nsecs_t when);
+    virtual void cancelTouch(nsecs_t when, nsecs_t readTime);
     virtual bool enableSensor(InputDeviceSensorType sensorType,
                               std::chrono::microseconds samplingPeriod,
                               std::chrono::microseconds maxBatchReportLatency);
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 37aa140..0dc312e 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -299,14 +299,14 @@
         case EV_SYN:
             switch (rawEvent->code) {
                 case SYN_REPORT:
-                    sync(rawEvent->when, false /*force*/);
+                    sync(rawEvent->when, rawEvent->readTime, false /*force*/);
                     break;
             }
             break;
     }
 }
 
-void JoystickInputMapper::sync(nsecs_t when, bool force) {
+void JoystickInputMapper::sync(nsecs_t when, nsecs_t readTime, bool force) {
     if (!filterAxes(force)) {
         return;
     }
@@ -337,9 +337,10 @@
     // TODO: Use the input device configuration to control this behavior more finely.
     uint32_t policyFlags = 0;
 
-    NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK,
-                          ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                          buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+    NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
+                          AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags,
+                          AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState,
+                          MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
                           &pointerProperties, &pointerCoords, 0, 0,
                           AMOTION_EVENT_INVALID_CURSOR_POSITION,
                           AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 0cf60a2..bba95ad 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -92,7 +92,7 @@
     // Axes indexed by raw ABS_* axis index.
     std::unordered_map<int32_t, Axis> mAxes;
 
-    void sync(nsecs_t when, bool force);
+    void sync(nsecs_t when, nsecs_t readTime, bool force);
 
     bool haveAxis(int32_t axisId);
     void pruneAxes(bool ignoreExplicitlyMappedAxes);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 8b9f235..2c5a576 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -216,7 +216,8 @@
             mCurrentHidUsage = 0;
 
             if (isKeyboardOrGamepadKey(scanCode)) {
-                processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
+                processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0, scanCode,
+                           usageCode);
             }
             break;
         }
@@ -269,7 +270,8 @@
     return false;
 }
 
-void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) {
+void KeyboardInputMapper::processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode,
+                                     int32_t usageCode) {
     int32_t keyCode;
     int32_t keyMetaState;
     uint32_t policyFlags;
@@ -299,7 +301,7 @@
                 return;
             }
             if (policyFlags & POLICY_FLAG_GESTURE) {
-                getDeviceContext().cancelTouch(when);
+                getDeviceContext().cancelTouch(when, readTime);
             }
 
             KeyDown keyDown;
@@ -350,8 +352,9 @@
         policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
     }
 
-    NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(),
-                       policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
+    NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                       getDisplayId(), policyFlags,
+                       down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
                        AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
     getListener()->notifyKey(&args);
 }
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 4c0b42a..ca41712 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -86,7 +86,7 @@
     bool isKeyboardOrGamepadKey(int32_t scanCode);
     bool isMediaKey(int32_t keyCode);
 
-    void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode);
+    void processKey(nsecs_t when, nsecs_t readTime, bool down, int32_t scanCode, int32_t usageCode);
 
     bool updateMetaStateIfNeeded(int32_t keyCode, bool down);
 
diff --git a/services/inputflinger/reader/mapper/LightInputMapper.h b/services/inputflinger/reader/mapper/LightInputMapper.h
index 9254720..43141b8 100644
--- a/services/inputflinger/reader/mapper/LightInputMapper.h
+++ b/services/inputflinger/reader/mapper/LightInputMapper.h
@@ -44,7 +44,7 @@
 
 private:
     struct Light {
-        explicit Light(InputDeviceContext& context, std::string name, int32_t id,
+        explicit Light(InputDeviceContext& context, const std::string& name, int32_t id,
                        InputDeviceLightType type)
               : context(context), name(name), id(id), type(type) {}
         virtual ~Light() {}
@@ -65,7 +65,7 @@
     };
 
     struct SingleLight : public Light {
-        explicit SingleLight(InputDeviceContext& context, std::string name, int32_t id,
+        explicit SingleLight(InputDeviceContext& context, const std::string& name, int32_t id,
                              int32_t rawId)
               : Light(context, name, id, InputDeviceLightType::SINGLE), rawId(rawId) {}
         int32_t rawId;
@@ -77,7 +77,7 @@
 
     struct RgbLight : public Light {
         explicit RgbLight(InputDeviceContext& context, int32_t id,
-                          std::unordered_map<LightColor, int32_t> rawRgbIds,
+                          const std::unordered_map<LightColor, int32_t>& rawRgbIds,
                           std::optional<int32_t> rawGlobalId)
               : Light(context, "RGB", id, InputDeviceLightType::RGB),
                 rawRgbIds(rawRgbIds),
@@ -98,7 +98,7 @@
     };
 
     struct MultiColorLight : public Light {
-        explicit MultiColorLight(InputDeviceContext& context, std::string name, int32_t id,
+        explicit MultiColorLight(InputDeviceContext& context, const std::string& name, int32_t id,
                                  int32_t rawId)
               : Light(context, name, id, InputDeviceLightType::MULTI_COLOR), rawId(rawId) {}
         int32_t rawId;
@@ -109,8 +109,8 @@
     };
 
     struct PlayerIdLight : public Light {
-        explicit PlayerIdLight(InputDeviceContext& context, std::string name, int32_t id,
-                               std::unordered_map<int32_t, int32_t> rawLightIds)
+        explicit PlayerIdLight(InputDeviceContext& context, const std::string& name, int32_t id,
+                               const std::unordered_map<int32_t, int32_t>& rawLightIds)
               : Light(context, name, id, InputDeviceLightType::PLAYER_ID),
                 rawLightIds(rawLightIds) {}
         // Map from player Id to raw light Id
@@ -132,4 +132,4 @@
 
 } // namespace android
 
-#endif // _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
\ No newline at end of file
+#endif // _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 594ff42..e9d0189 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -87,11 +87,11 @@
     mRotaryEncoderScrollAccumulator.process(rawEvent);
 
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when);
+        sync(rawEvent->when, rawEvent->readTime);
     }
 }
 
-void RotaryEncoderInputMapper::sync(nsecs_t when) {
+void RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
     PointerCoords pointerCoords;
     pointerCoords.clear();
 
@@ -121,9 +121,9 @@
         int32_t metaState = getContext()->getGlobalMetaState();
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor);
 
-        NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource,
-                                    displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0,
-                                    metaState, /* buttonState */ 0, MotionClassification::NONE,
+        NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
+                                    mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0,
+                                    0, metaState, /* buttonState */ 0, MotionClassification::NONE,
                                     AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
                                     &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                                     AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 7a77b12..e0c9404 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -42,7 +42,7 @@
     float mScalingFactor;
     int32_t mOrientation;
 
-    void sync(nsecs_t when);
+    void sync(nsecs_t when, nsecs_t readTime);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index a86443d..5344227 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -59,27 +59,27 @@
 }
 
 static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when,
-                                int32_t deviceId, uint32_t source, int32_t displayId,
-                                uint32_t policyFlags, int32_t lastButtonState,
+                                nsecs_t readTime, int32_t deviceId, uint32_t source,
+                                int32_t displayId, uint32_t policyFlags, int32_t lastButtonState,
                                 int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
     if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
          (currentButtonState & buttonState)) ||
         (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) &&
          !(currentButtonState & buttonState))) {
-        NotifyKeyArgs args(context->getNextId(), when, deviceId, source, displayId, policyFlags,
-                           action, 0, keyCode, 0, context->getGlobalMetaState(), when);
+        NotifyKeyArgs args(context->getNextId(), when, readTime, deviceId, source, displayId,
+                           policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when);
         context->getListener()->notifyKey(&args);
     }
 }
 
 static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when,
-                                 int32_t deviceId, uint32_t source, int32_t displayId,
-                                 uint32_t policyFlags, int32_t lastButtonState,
+                                 nsecs_t readTime, int32_t deviceId, uint32_t source,
+                                 int32_t displayId, uint32_t policyFlags, int32_t lastButtonState,
                                  int32_t currentButtonState) {
-    synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
+    synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags,
                         lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK,
                         AKEYCODE_BACK);
-    synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags,
+    synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId, policyFlags,
                         lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD,
                         AKEYCODE_FORWARD);
 }
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index d1df37b..16cf010 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1016,7 +1016,8 @@
             mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal;
 
             // Abort current pointer usages because the state has changed.
-            abortPointerUsage(when, 0 /*policyFlags*/);
+            const nsecs_t readTime = when; // synthetic event
+            abortPointerUsage(when, readTime, 0 /*policyFlags*/);
         }
 
         // Inform the dispatcher about the changes.
@@ -1406,11 +1407,11 @@
     mTouchButtonAccumulator.process(rawEvent);
 
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
-        sync(rawEvent->when);
+        sync(rawEvent->when, rawEvent->readTime);
     }
 }
 
-void TouchInputMapper::sync(nsecs_t when) {
+void TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) {
     const RawState* last =
             mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back();
 
@@ -1420,6 +1421,7 @@
     RawState* next = &mRawStatesPending.back();
     next->clear();
     next->when = when;
+    next->readTime = readTime;
 
     // Sync button state.
     next->buttonState =
@@ -1453,7 +1455,7 @@
 void TouchInputMapper::processRawTouches(bool timeout) {
     if (mDeviceMode == DeviceMode::DISABLED) {
         // Drop all input if the device is disabled.
-        cancelTouch(mCurrentRawState.when);
+        cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime);
         mCurrentCookedState.clear();
         updateTouchSpots();
         return;
@@ -1479,8 +1481,9 @@
         mCurrentRawState.copyFrom(next);
         if (mCurrentRawState.when < mLastRawState.when) {
             mCurrentRawState.when = mLastRawState.when;
+            mCurrentRawState.readTime = mLastRawState.readTime;
         }
-        cookAndDispatch(mCurrentRawState.when);
+        cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);
     }
     if (count != 0) {
         mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count);
@@ -1494,7 +1497,8 @@
 #if DEBUG_STYLUS_FUSION
             ALOGD("Timeout expired, synthesizing event with new stylus data");
 #endif
-            cookAndDispatch(when);
+            const nsecs_t readTime = when; // consider this synthetic event to be zero latency
+            cookAndDispatch(when, readTime);
         } else if (mExternalStylusFusionTimeout == LLONG_MAX) {
             mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT;
             getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
@@ -1502,7 +1506,7 @@
     }
 }
 
-void TouchInputMapper::cookAndDispatch(nsecs_t when) {
+void TouchInputMapper::cookAndDispatch(nsecs_t when, nsecs_t readTime) {
     // Always start with a clean state.
     mCurrentCookedState.clear();
 
@@ -1528,7 +1532,7 @@
 
     // Consume raw off-screen touches before cooking pointer data.
     // If touches are consumed, subsequent code will not receive any pointer data.
-    if (consumeRawTouches(when, policyFlags)) {
+    if (consumeRawTouches(when, readTime, policyFlags)) {
         mCurrentRawState.rawPointerData.clear();
     }
 
@@ -1541,8 +1545,8 @@
     applyExternalStylusTouchState(when);
 
     // Synthesize key down from raw buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,
-                         mViewport.displayId, policyFlags, mLastCookedState.buttonState,
+    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
+                         mSource, mViewport.displayId, policyFlags, mLastCookedState.buttonState,
                          mCurrentCookedState.buttonState);
 
     // Dispatch the touches either directly or by translation through a pointer on screen.
@@ -1585,16 +1589,16 @@
             pointerUsage = PointerUsage::GESTURES;
         }
 
-        dispatchPointerUsage(when, policyFlags, pointerUsage);
+        dispatchPointerUsage(when, readTime, policyFlags, pointerUsage);
     } else {
         updateTouchSpots();
 
         if (!mCurrentMotionAborted) {
-            dispatchButtonRelease(when, policyFlags);
-            dispatchHoverExit(when, policyFlags);
-            dispatchTouches(when, policyFlags);
-            dispatchHoverEnterAndMove(when, policyFlags);
-            dispatchButtonPress(when, policyFlags);
+            dispatchButtonRelease(when, readTime, policyFlags);
+            dispatchHoverExit(when, readTime, policyFlags);
+            dispatchTouches(when, readTime, policyFlags);
+            dispatchHoverEnterAndMove(when, readTime, policyFlags);
+            dispatchButtonPress(when, readTime, policyFlags);
         }
 
         if (mCurrentCookedState.cookedPointerData.pointerCount == 0) {
@@ -1603,7 +1607,7 @@
     }
 
     // Synthesize key up from raw buttons if needed.
-    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,
+    synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
                          mViewport.displayId, policyFlags, mLastCookedState.buttonState,
                          mCurrentCookedState.buttonState);
 
@@ -1716,7 +1720,9 @@
 void TouchInputMapper::timeoutExpired(nsecs_t when) {
     if (mDeviceMode == DeviceMode::POINTER) {
         if (mPointerUsage == PointerUsage::GESTURES) {
-            dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
+            // Since this is a synthetic event, we can consider its latency to be zero
+            const nsecs_t readTime = when;
+            dispatchPointerGestures(when, readTime, 0 /*policyFlags*/, true /*isTimeout*/);
         }
     } else if (mDeviceMode == DeviceMode::DIRECT) {
         if (mExternalStylusFusionTimeout < when) {
@@ -1738,7 +1744,7 @@
     }
 }
 
-bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) {
+bool TouchInputMapper::consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
     // Check for release of a virtual key.
     if (mCurrentVirtualKey.down) {
         if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
@@ -1749,7 +1755,7 @@
                 ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
                       mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
 #endif
-                dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP,
+                dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
                                    AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
             }
             return true;
@@ -1776,7 +1782,7 @@
             ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode,
                   mCurrentVirtualKey.scanCode);
 #endif
-            dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP,
+            dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
                                AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
                                        AKEY_EVENT_FLAG_CANCELED);
         }
@@ -1807,7 +1813,7 @@
                         ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
                               mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
 #endif
-                        dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_DOWN,
+                        dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_DOWN,
                                            AKEY_EVENT_FLAG_FROM_SYSTEM |
                                                    AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
                     }
@@ -1838,7 +1844,7 @@
     return false;
 }
 
-void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags,
+void TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
                                           int32_t keyEventAction, int32_t keyEventFlags) {
     int32_t keyCode = mCurrentVirtualKey.keyCode;
     int32_t scanCode = mCurrentVirtualKey.scanCode;
@@ -1846,19 +1852,19 @@
     int32_t metaState = getContext()->getGlobalMetaState();
     policyFlags |= POLICY_FLAG_VIRTUAL;
 
-    NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD,
-                       mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode,
-                       scanCode, metaState, downTime);
+    NotifyKeyArgs args(getContext()->getNextId(), when, readTime, getDeviceId(),
+                       AINPUT_SOURCE_KEYBOARD, mViewport.displayId, policyFlags, keyEventAction,
+                       keyEventFlags, keyCode, scanCode, metaState, downTime);
     getListener()->notifyKey(&args);
 }
 
-void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
     BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
     if (!currentIdBits.isEmpty()) {
         int32_t metaState = getContext()->getGlobalMetaState();
         int32_t buttonState = mCurrentCookedState.buttonState;
-        dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
-                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
+                       metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
@@ -1867,7 +1873,7 @@
     }
 }
 
-void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
     BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits;
     BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits;
     int32_t metaState = getContext()->getGlobalMetaState();
@@ -1877,8 +1883,8 @@
         if (!currentIdBits.isEmpty()) {
             // No pointer id changes so this is a move event.
             // The listener takes care of batching moves so we don't have to deal with that here.
-            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                           buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
+                           metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
@@ -1912,7 +1918,7 @@
             if (isCanceled) {
                 ALOGI("Canceling pointer %d for the palm event was detected.", upId);
             }
-            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,
+            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0,
                            isCanceled ? AMOTION_EVENT_FLAG_CANCELED : 0, metaState, buttonState, 0,
                            mLastCookedState.cookedPointerData.pointerProperties,
                            mLastCookedState.cookedPointerData.pointerCoords,
@@ -1927,8 +1933,9 @@
         // events, they do not generally handle them except when presented in a move event.
         if (moveNeeded && !moveIdBits.isEmpty()) {
             ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
-            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                           buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties,
+            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
+                           metaState, buttonState, 0,
+                           mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1,
                            mOrientedXPrecision, mOrientedYPrecision, mDownTime);
@@ -1944,8 +1951,8 @@
                 mDownTime = when;
             }
 
-            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
-                           metaState, buttonState, 0,
+            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
+                           0, 0, metaState, buttonState, 0,
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,
@@ -1954,13 +1961,13 @@
     }
 }
 
-void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
     if (mSentHoverEnter &&
         (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() ||
          !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) {
         int32_t metaState = getContext()->getGlobalMetaState();
-        dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
-                       mLastCookedState.buttonState, 0,
+        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
+                       metaState, mLastCookedState.buttonState, 0,
                        mLastCookedState.cookedPointerData.pointerProperties,
                        mLastCookedState.cookedPointerData.pointerCoords,
                        mLastCookedState.cookedPointerData.idToIndex,
@@ -1970,13 +1977,14 @@
     }
 }
 
-void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime,
+                                                 uint32_t policyFlags) {
     if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() &&
         !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) {
         int32_t metaState = getContext()->getGlobalMetaState();
         if (!mSentHoverEnter) {
-            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
-                           metaState, mCurrentRawState.buttonState, 0,
+            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER,
+                           0, 0, metaState, mCurrentRawState.buttonState, 0,
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex,
@@ -1985,8 +1993,8 @@
             mSentHoverEnter = true;
         }
 
-        dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                       mCurrentRawState.buttonState, 0,
+        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+                       metaState, mCurrentRawState.buttonState, 0,
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex,
@@ -1995,7 +2003,7 @@
     }
 }
 
-void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
     BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState);
     const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData);
     const int32_t metaState = getContext()->getGlobalMetaState();
@@ -2003,7 +2011,7 @@
     while (!releasedButtons.isEmpty()) {
         int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit());
         buttonState &= ~actionButton;
-        dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
                        actionButton, 0, metaState, buttonState, 0,
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
@@ -2012,7 +2020,7 @@
     }
 }
 
-void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
     BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState);
     const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData);
     const int32_t metaState = getContext()->getGlobalMetaState();
@@ -2020,8 +2028,8 @@
     while (!pressedButtons.isEmpty()) {
         int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit());
         buttonState |= actionButton;
-        dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton,
-                       0, metaState, buttonState, 0,
+        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS,
+                       actionButton, 0, metaState, buttonState, 0,
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
@@ -2308,38 +2316,38 @@
     }
 }
 
-void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags,
+void TouchInputMapper::dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
                                             PointerUsage pointerUsage) {
     if (pointerUsage != mPointerUsage) {
-        abortPointerUsage(when, policyFlags);
+        abortPointerUsage(when, readTime, policyFlags);
         mPointerUsage = pointerUsage;
     }
 
     switch (mPointerUsage) {
         case PointerUsage::GESTURES:
-            dispatchPointerGestures(when, policyFlags, false /*isTimeout*/);
+            dispatchPointerGestures(when, readTime, policyFlags, false /*isTimeout*/);
             break;
         case PointerUsage::STYLUS:
-            dispatchPointerStylus(when, policyFlags);
+            dispatchPointerStylus(when, readTime, policyFlags);
             break;
         case PointerUsage::MOUSE:
-            dispatchPointerMouse(when, policyFlags);
+            dispatchPointerMouse(when, readTime, policyFlags);
             break;
         case PointerUsage::NONE:
             break;
     }
 }
 
-void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
     switch (mPointerUsage) {
         case PointerUsage::GESTURES:
-            abortPointerGestures(when, policyFlags);
+            abortPointerGestures(when, readTime, policyFlags);
             break;
         case PointerUsage::STYLUS:
-            abortPointerStylus(when, policyFlags);
+            abortPointerStylus(when, readTime, policyFlags);
             break;
         case PointerUsage::MOUSE:
-            abortPointerMouse(when, policyFlags);
+            abortPointerMouse(when, readTime, policyFlags);
             break;
         case PointerUsage::NONE:
             break;
@@ -2348,7 +2356,8 @@
     mPointerUsage = PointerUsage::NONE;
 }
 
-void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) {
+void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+                                               bool isTimeout) {
     // Update current gesture coordinates.
     bool cancelPreviousGesture, finishPreviousGesture;
     bool sendEvents =
@@ -2441,8 +2450,8 @@
     BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits);
     if (!dispatchedGestureIdBits.isEmpty()) {
         if (cancelPreviousGesture) {
-            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
-                           buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
+                           metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                            mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
                            mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
                            mPointerGesture.downTime);
@@ -2459,9 +2468,9 @@
             while (!upGestureIdBits.isEmpty()) {
                 uint32_t id = upGestureIdBits.clearFirstMarkedBit();
 
-                dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
-                               metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                               mPointerGesture.lastGestureProperties,
+                dispatchMotion(when, readTime, policyFlags, mSource,
+                               AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState,
+                               AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties,
                                mPointerGesture.lastGestureCoords,
                                mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
                                0, mPointerGesture.downTime);
@@ -2473,8 +2482,8 @@
 
     // Send motion events for all pointers that moved.
     if (moveNeeded) {
-        dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0,
+                       metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.currentGestureProperties,
                        mPointerGesture.currentGestureCoords,
                        mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
@@ -2493,8 +2502,9 @@
                 mPointerGesture.downTime = when;
             }
 
-            dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
-                           metaState, buttonState, 0, mPointerGesture.currentGestureProperties,
+            dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN,
+                           0, 0, metaState, buttonState, 0,
+                           mPointerGesture.currentGestureProperties,
                            mPointerGesture.currentGestureCoords,
                            mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
                            0, mPointerGesture.downTime);
@@ -2503,8 +2513,8 @@
 
     // Send motion events for hover.
     if (mPointerGesture.currentGestureMode == PointerGesture::Mode::HOVER) {
-        dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+                       metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.currentGestureProperties,
                        mPointerGesture.currentGestureCoords,
                        mPointerGesture.currentGestureIdToIndex,
@@ -2528,11 +2538,11 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
 
         const int32_t displayId = mPointerController->getDisplayId();
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                              buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
-                              1, &pointerProperties, &pointerCoords, 0, 0, x, y,
-                              mPointerGesture.downTime, /* videoFrames */ {});
+        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+                              metaState, buttonState, MotionClassification::NONE,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
+                              0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -2554,13 +2564,13 @@
     }
 }
 
-void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
     // Cancel previously dispatches pointers.
     if (!mPointerGesture.lastGestureIdBits.isEmpty()) {
         int32_t metaState = getContext()->getGlobalMetaState();
         int32_t buttonState = mCurrentRawState.buttonState;
-        dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
-                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+        dispatchMotion(when, readTime, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0,
+                       metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
                        mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
                        0, 0, mPointerGesture.downTime);
@@ -3335,7 +3345,7 @@
     return true;
 }
 
-void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
     mPointerSimple.currentCoords.clear();
     mPointerSimple.currentProperties.clear();
 
@@ -3363,14 +3373,14 @@
         hovering = false;
     }
 
-    dispatchPointerSimple(when, policyFlags, down, hovering);
+    dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
 }
 
-void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) {
-    abortPointerSimple(when, policyFlags);
+void TouchInputMapper::abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+    abortPointerSimple(when, readTime, policyFlags);
 }
 
-void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
     mPointerSimple.currentCoords.clear();
     mPointerSimple.currentProperties.clear();
 
@@ -3417,17 +3427,17 @@
         hovering = false;
     }
 
-    dispatchPointerSimple(when, policyFlags, down, hovering);
+    dispatchPointerSimple(when, readTime, policyFlags, down, hovering);
 }
 
-void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) {
-    abortPointerSimple(when, policyFlags);
+void TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
+    abortPointerSimple(when, readTime, policyFlags);
 
     mPointerVelocityControl.reset();
 }
 
-void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down,
-                                             bool hovering) {
+void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+                                             bool down, bool hovering) {
     int32_t metaState = getContext()->getGlobalMetaState();
 
     if (down || hovering) {
@@ -3448,8 +3458,8 @@
         mPointerSimple.down = false;
 
         // Send up.
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
+        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                              displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState,
                               mLastRawState.buttonState, MotionClassification::NONE,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
                               &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
@@ -3462,9 +3472,9 @@
         mPointerSimple.hovering = false;
 
         // Send hover exit.
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
-                              mLastRawState.buttonState, MotionClassification::NONE,
+        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0,
+                              metaState, mLastRawState.buttonState, MotionClassification::NONE,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
                               &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
                               xCursorPosition, yCursorPosition, mPointerSimple.downTime,
@@ -3478,7 +3488,7 @@
             mPointerSimple.downTime = when;
 
             // Send down.
-            NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
+            NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
                                   displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0,
                                   metaState, mCurrentRawState.buttonState,
                                   MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
@@ -3489,8 +3499,8 @@
         }
 
         // Send move.
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
+        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                              displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
                               mCurrentRawState.buttonState, MotionClassification::NONE,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
                               &mPointerSimple.currentCoords, mOrientedXPrecision,
@@ -3504,7 +3514,7 @@
             mPointerSimple.hovering = true;
 
             // Send hover enter.
-            NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource,
+            NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
                                   displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
                                   metaState, mCurrentRawState.buttonState,
                                   MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
@@ -3515,9 +3525,9 @@
         }
 
         // Send hover move.
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                              mCurrentRawState.buttonState, MotionClassification::NONE,
+        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                              displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
+                              metaState, mCurrentRawState.buttonState, MotionClassification::NONE,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
                               &mPointerSimple.currentCoords, mOrientedXPrecision,
                               mOrientedYPrecision, xCursorPosition, yCursorPosition,
@@ -3537,8 +3547,8 @@
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
 
-        NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId,
-                              policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
+        NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
+                              displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
                               mCurrentRawState.buttonState, MotionClassification::NONE,
                               AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
                               &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
@@ -3556,17 +3566,17 @@
     }
 }
 
-void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) {
+void TouchInputMapper::abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) {
     mPointerSimple.currentCoords.clear();
     mPointerSimple.currentProperties.clear();
 
-    dispatchPointerSimple(when, policyFlags, false, false);
+    dispatchPointerSimple(when, readTime, policyFlags, false, false);
 }
 
-void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
-                                      int32_t action, int32_t actionButton, int32_t flags,
-                                      int32_t metaState, int32_t buttonState, int32_t edgeFlags,
-                                      const PointerProperties* properties,
+void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+                                      uint32_t source, int32_t action, int32_t actionButton,
+                                      int32_t flags, int32_t metaState, int32_t buttonState,
+                                      int32_t edgeFlags, const PointerProperties* properties,
                                       const PointerCoords* coords, const uint32_t* idToIndex,
                                       BitSet32 idBits, int32_t changedId, float xPrecision,
                                       float yPrecision, nsecs_t downTime) {
@@ -3615,8 +3625,8 @@
     std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
     std::for_each(frames.begin(), frames.end(),
                   [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
-    NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags,
-                          action, actionButton, flags, metaState, buttonState,
+    NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
+                          policyFlags, action, actionButton, flags, metaState, buttonState,
                           MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
                           pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
                           downTime, std::move(frames));
@@ -3653,9 +3663,9 @@
     return changed;
 }
 
-void TouchInputMapper::cancelTouch(nsecs_t when) {
-    abortPointerUsage(when, 0 /*policyFlags*/);
-    abortTouches(when, 0 /* policyFlags*/);
+void TouchInputMapper::cancelTouch(nsecs_t when, nsecs_t readTime) {
+    abortPointerUsage(when, readTime, 0 /*policyFlags*/);
+    abortTouches(when, readTime, 0 /* policyFlags*/);
 }
 
 // Transform raw coordinate to surface coordinate
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 6621825..cb52e2d 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -150,7 +150,7 @@
     bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes,
                                uint8_t* outFlags) override;
 
-    void cancelTouch(nsecs_t when) override;
+    void cancelTouch(nsecs_t when, nsecs_t readTime) override;
     void timeoutExpired(nsecs_t when) override;
     void updateExternalStylusState(const StylusState& state) override;
     std::optional<int32_t> getAssociatedDisplayId() override;
@@ -298,6 +298,7 @@
 
     struct RawState {
         nsecs_t when;
+        nsecs_t readTime;
 
         // Raw pointer sample data.
         RawPointerData rawPointerData;
@@ -310,6 +311,7 @@
 
         void copyFrom(const RawState& other) {
             when = other.when;
+            readTime = other.readTime;
             rawPointerData.copyFrom(other.rawPointerData);
             buttonState = other.buttonState;
             rawVScroll = other.rawVScroll;
@@ -318,6 +320,7 @@
 
         void clear() {
             when = 0;
+            readTime = 0;
             rawPointerData.clear();
             buttonState = 0;
             rawVScroll = 0;
@@ -702,39 +705,42 @@
     void resetExternalStylus();
     void clearStylusDataPendingFlags();
 
-    void sync(nsecs_t when);
+    void sync(nsecs_t when, nsecs_t readTime);
 
-    bool consumeRawTouches(nsecs_t when, uint32_t policyFlags);
+    bool consumeRawTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
     void processRawTouches(bool timeout);
-    void cookAndDispatch(nsecs_t when);
-    void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t keyEventAction,
-                            int32_t keyEventFlags);
+    void cookAndDispatch(nsecs_t when, nsecs_t readTime);
+    void dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+                            int32_t keyEventAction, int32_t keyEventFlags);
 
-    void dispatchTouches(nsecs_t when, uint32_t policyFlags);
-    void dispatchHoverExit(nsecs_t when, uint32_t policyFlags);
-    void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags);
-    void dispatchButtonRelease(nsecs_t when, uint32_t policyFlags);
-    void dispatchButtonPress(nsecs_t when, uint32_t policyFlags);
+    void dispatchTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    void dispatchHoverExit(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    void dispatchHoverEnterAndMove(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    void dispatchButtonRelease(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    void dispatchButtonPress(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
     const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData);
     void cookPointerData();
-    void abortTouches(nsecs_t when, uint32_t policyFlags);
+    void abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
 
-    void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage);
-    void abortPointerUsage(nsecs_t when, uint32_t policyFlags);
+    void dispatchPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+                              PointerUsage pointerUsage);
+    void abortPointerUsage(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
 
-    void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
-    void abortPointerGestures(nsecs_t when, uint32_t policyFlags);
+    void dispatchPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags,
+                                 bool isTimeout);
+    void abortPointerGestures(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
     bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture,
                                 bool* outFinishPreviousGesture, bool isTimeout);
 
-    void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags);
-    void abortPointerStylus(nsecs_t when, uint32_t policyFlags);
+    void dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    void abortPointerStylus(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
 
-    void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags);
-    void abortPointerMouse(nsecs_t when, uint32_t policyFlags);
+    void dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
+    void abortPointerMouse(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
 
-    void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering);
-    void abortPointerSimple(nsecs_t when, uint32_t policyFlags);
+    void dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, bool down,
+                               bool hovering);
+    void abortPointerSimple(nsecs_t when, nsecs_t readTime, uint32_t policyFlags);
 
     bool assignExternalStylusId(const RawState& state, bool timeout);
     void applyExternalStylusButtonState(nsecs_t when);
@@ -744,9 +750,9 @@
     // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
     // method will take care of setting the index and transmuting the action to DOWN or UP
     // it is the first / last pointer to go down / up.
-    void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action,
-                        int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
-                        int32_t edgeFlags, const PointerProperties* properties,
+    void dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t policyFlags, uint32_t source,
+                        int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
+                        int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties,
                         const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
                         int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
 
diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp
index f58b628..c0ada9d 100644
--- a/services/inputflinger/tests/InputClassifierConverter_test.cpp
+++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp
@@ -38,13 +38,13 @@
     coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 2);
     coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.5);
     static constexpr nsecs_t downTime = 2;
-    NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/,
-                                AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/,
-                                AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/,
-                                AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
-                                AMOTION_EVENT_EDGE_FLAG_NONE, 1 /*pointerCount*/, &properties,
-                                &coords, 0 /*xPrecision*/, 0 /*yPrecision*/,
-                                AMOTION_EVENT_INVALID_CURSOR_POSITION,
+    NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 2 /*readTime*/,
+                                3 /*deviceId*/, AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT,
+                                4 /*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/,
+                                0 /*flags*/, AMETA_NONE, 0 /*buttonState*/,
+                                MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                1 /*pointerCount*/, &properties, &coords, 0 /*xPrecision*/,
+                                0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                                 AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
                                 {} /*videoFrames*/);
     return motionArgs;
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index ab74a04..a72df01 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -41,13 +41,13 @@
     coords.setAxisValue(AMOTION_EVENT_AXIS_X, 1);
     coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
     static constexpr nsecs_t downTime = 2;
-    NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/,
-                                AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/,
-                                AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/,
-                                AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
-                                AMOTION_EVENT_EDGE_FLAG_NONE, 1 /*pointerCount*/, &properties,
-                                &coords, 0 /*xPrecision*/, 0 /*yPrecision*/,
-                                AMOTION_EVENT_INVALID_CURSOR_POSITION,
+    NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 2 /*readTime*/,
+                                3 /*deviceId*/, AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT,
+                                4 /*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/,
+                                0 /*flags*/, AMETA_NONE, 0 /*buttonState*/,
+                                MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
+                                1 /*pointerCount*/, &properties, &coords, 0 /*xPrecision*/,
+                                0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
                                 AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
                                 {} /*videoFrames*/);
     return motionArgs;
@@ -85,9 +85,10 @@
 
 TEST_F(InputClassifierTest, SendToNextStage_NotifyKeyArgs) {
     // Create a basic key event and send to classifier
-    NotifyKeyArgs args(1/*sequenceNum*/, 2/*eventTime*/, 3/*deviceId*/, AINPUT_SOURCE_KEYBOARD,
-            ADISPLAY_ID_DEFAULT, 0/*policyFlags*/, AKEY_EVENT_ACTION_DOWN, 4/*flags*/,
-            AKEYCODE_HOME, 5/*scanCode*/, AMETA_NONE, 6/*downTime*/);
+    NotifyKeyArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 21 /*readTime*/, 3 /*deviceId*/,
+                       AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT, 0 /*policyFlags*/,
+                       AKEY_EVENT_ACTION_DOWN, 4 /*flags*/, AKEYCODE_HOME, 5 /*scanCode*/,
+                       AMETA_NONE, 6 /*downTime*/);
 
     mClassifier->notifyKey(&args);
     NotifyKeyArgs outArgs;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 8456387..e027c5e 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -377,7 +377,7 @@
         mLastNotifySwitch = NotifySwitchArgs(1 /*id*/, when, policyFlags, switchValues, switchMask);
     }
 
-    void pokeUserActivity(nsecs_t, int32_t) override {}
+    void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
 
     bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; }
 
@@ -1234,9 +1234,9 @@
 static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid key event.
-    NotifyKeyArgs args(/* id */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId,
-                       POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0, AKEYCODE_A, KEY_A,
-                       AMETA_NONE, currentTime);
+    NotifyKeyArgs args(/* id */ 0, currentTime, 0 /*readTime*/, DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+                       displayId, POLICY_FLAG_PASS_TO_USER, action, /* flags */ 0, AKEYCODE_A,
+                       KEY_A, AMETA_NONE, currentTime);
 
     return args;
 }
@@ -1263,7 +1263,7 @@
 
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid motion event.
-    NotifyMotionArgs args(/* id */ 0, currentTime, DEVICE_ID, source, displayId,
+    NotifyMotionArgs args(/* id */ 0, currentTime, 0 /*readTime*/, DEVICE_ID, source, displayId,
                           POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
                           AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
                           AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 2836516..d69bb6a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -50,7 +50,8 @@
 static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
 
 // An arbitrary time value.
-static const nsecs_t ARBITRARY_TIME = 1234;
+static constexpr nsecs_t ARBITRARY_TIME = 1234;
+static constexpr nsecs_t READ_TIME = 4321;
 
 // Arbitrary display properties.
 static constexpr int32_t DISPLAY_ID = 0;
@@ -442,14 +443,14 @@
         device->identifier.name = name;
         mDevices.add(deviceId, device);
 
-        enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0);
+        enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0);
     }
 
     void removeDevice(int32_t deviceId) {
         delete mDevices.valueFor(deviceId);
         mDevices.removeItem(deviceId);
 
-        enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
+        enqueueEvent(ARBITRARY_TIME, READ_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0);
     }
 
     bool isDeviceEnabled(int32_t deviceId) {
@@ -490,7 +491,7 @@
     }
 
     void finishDeviceScan() {
-        enqueueEvent(ARBITRARY_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
+        enqueueEvent(ARBITRARY_TIME, READ_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0);
     }
 
     void addConfigurationProperty(int32_t deviceId, const String8& key, const String8& value) {
@@ -604,11 +605,12 @@
         device->virtualKeys.push_back(definition);
     }
 
-    void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type,
-            int32_t code, int32_t value) {
+    void enqueueEvent(nsecs_t when, nsecs_t readTime, int32_t deviceId, int32_t type, int32_t code,
+                      int32_t value) {
         std::scoped_lock<std::mutex> lock(mLock);
         RawEvent event;
         event.when = when;
+        event.readTime = readTime;
         event.deviceId = deviceId;
         event.type = type;
         event.code = code;
@@ -1774,18 +1776,21 @@
 TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
     constexpr int32_t deviceId = END_RESERVED_ID + 1000;
     constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD;
+    constexpr nsecs_t when = 0;
     constexpr int32_t eventHubId = 1;
+    constexpr nsecs_t readTime = 2;
     FakeInputMapper& mapper =
             addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass,
                                          AINPUT_SOURCE_KEYBOARD, nullptr);
 
-    mFakeEventHub->enqueueEvent(0, eventHubId, EV_KEY, KEY_A, 1);
+    mFakeEventHub->enqueueEvent(when, readTime, eventHubId, EV_KEY, KEY_A, 1);
     mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
 
     RawEvent event;
     ASSERT_NO_FATAL_FAILURE(mapper.assertProcessWasCalled(&event));
-    ASSERT_EQ(0, event.when);
+    ASSERT_EQ(when, event.when);
+    ASSERT_EQ(readTime, event.readTime);
     ASSERT_EQ(eventHubId, event.deviceId);
     ASSERT_EQ(EV_KEY, event.type);
     ASSERT_EQ(KEY_A, event.code);
@@ -2193,12 +2198,14 @@
     ASSERT_NE(prevId, keyArgs.id);
     prevId = keyArgs.id;
     ASSERT_LE(prevTimestamp, keyArgs.eventTime);
+    ASSERT_LE(keyArgs.eventTime, keyArgs.readTime);
     prevTimestamp = keyArgs.eventTime;
 
     ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
     ASSERT_NE(prevId, keyArgs.id);
     ASSERT_LE(prevTimestamp, keyArgs.eventTime);
+    ASSERT_LE(keyArgs.eventTime, keyArgs.readTime);
 }
 
 /**
@@ -2675,9 +2682,11 @@
         mFakePolicy->clearViewports();
     }
 
-    void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code, int32_t value) {
+    void process(InputMapper& mapper, nsecs_t when, nsecs_t readTime, int32_t type, int32_t code,
+                 int32_t value) {
         RawEvent event;
         event.when = when;
+        event.readTime = readTime;
         event.deviceId = mapper.getDeviceContext().getEventHubId();
         event.type = type;
         event.code = code;
@@ -2756,10 +2765,10 @@
 TEST_F(SwitchInputMapperTest, Process) {
     SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
 
-    process(mapper, ARBITRARY_TIME, EV_SW, SW_LID, 1);
-    process(mapper, ARBITRARY_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
-    process(mapper, ARBITRARY_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_LID, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SW, SW_HEADPHONE_INSERT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
 
     NotifySwitchArgs args;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySwitchWasCalled(&args));
@@ -2929,11 +2938,11 @@
                                     std::chrono::microseconds(10000),
                                     std::chrono::microseconds(0)));
     ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, 20000);
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, -20000);
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Z, 40000);
-    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, 20000);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, -20000);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Z, 40000);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
 
     NotifySensorArgs args;
     std::vector<float> values = {20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT,
@@ -2959,11 +2968,11 @@
                                     std::chrono::microseconds(10000),
                                     std::chrono::microseconds(0)));
     ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID));
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RX, 20000);
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RY, -20000);
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RZ, 40000);
-    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RX, 20000);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RY, -20000);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RZ, 40000);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
 
     NotifySensorArgs args;
     std::vector<float> values = {20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT,
@@ -3154,14 +3163,14 @@
                                                   int32_t rotatedKeyCode, int32_t displayId) {
     NotifyKeyArgs args;
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
     ASSERT_EQ(originalScanCode, args.scanCode);
     ASSERT_EQ(rotatedKeyCode, args.keyCode);
     ASSERT_EQ(displayId, args.displayId);
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
     ASSERT_EQ(originalScanCode, args.scanCode);
@@ -3194,7 +3203,7 @@
     mapper.updateMetaState(AKEYCODE_NUM_LOCK);
 
     // Key down by scan code.
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
     NotifyKeyArgs args;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -3209,7 +3218,7 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Key up by scan code.
-    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -3223,8 +3232,8 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Key down by usage code.
-    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
-    process(mapper, ARBITRARY_TIME, EV_KEY, 0, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, 0, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -3238,8 +3247,8 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Key up by usage code.
-    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A);
-    process(mapper, ARBITRARY_TIME + 1, EV_KEY, 0, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_A);
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, 0, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -3253,8 +3262,8 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Key down with unknown scan code or usage code.
-    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UNKNOWN, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -3268,8 +3277,8 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Key up with unknown scan code or usage code.
-    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
-    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN);
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_UNKNOWN, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DEVICE_ID, args.deviceId);
     ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source);
@@ -3283,6 +3292,28 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 }
 
+/**
+ * Ensure that the readTime is set to the time when the EV_KEY is received.
+ */
+TEST_F(KeyboardInputMapperTest, Process_SendsReadTime) {
+    mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    NotifyKeyArgs args;
+
+    // Key down
+    process(mapper, ARBITRARY_TIME, 12 /*readTime*/, EV_KEY, KEY_HOME, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(12, args.readTime);
+
+    // Key up
+    process(mapper, ARBITRARY_TIME, 15 /*readTime*/, EV_KEY, KEY_HOME, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(15, args.readTime);
+}
+
 TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) {
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
     mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
@@ -3299,7 +3330,7 @@
     mapper.updateMetaState(AKEYCODE_NUM_LOCK);
 
     // Metakey down.
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1);
+    process(mapper, ARBITRARY_TIME, READ_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);
@@ -3307,19 +3338,19 @@
     ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled());
 
     // Key down.
-    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1);
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, 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());
 
     // Key up.
-    process(mapper, ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0);
+    process(mapper, ARBITRARY_TIME + 2, READ_TIME, 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());
 
     // Metakey up.
-    process(mapper, ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0);
+    process(mapper, ARBITRARY_TIME + 3, READ_TIME, EV_KEY, KEY_LEFTSHIFT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_NONE, args.metaState);
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
@@ -3406,7 +3437,7 @@
     NotifyKeyArgs args;
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_270);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
     ASSERT_EQ(KEY_UP, args.scanCode);
@@ -3414,7 +3445,7 @@
 
     clearViewports();
     prepareDisplay(DISPLAY_ORIENTATION_180);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
     ASSERT_EQ(KEY_UP, args.scanCode);
@@ -3432,16 +3463,16 @@
     NotifyKeyArgs args;
 
     // Display id should be ADISPLAY_ID_NONE without any display configuration.
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
 
     prepareDisplay(DISPLAY_ORIENTATION_0);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
 }
@@ -3462,9 +3493,9 @@
 
     setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
                                  UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(DISPLAY_ID, args.displayId);
 
@@ -3472,9 +3503,9 @@
     clearViewports();
     setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
                                  UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(newDisplayId, args.displayId);
 }
@@ -3538,48 +3569,48 @@
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
 
     // Toggle caps lock on.
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
 
     // Toggle num lock on.
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     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);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
 
     // Toggle scroll lock on.
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     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);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
 
     // Toggle scroll lock off.
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
@@ -3607,7 +3638,7 @@
 
     NotifyKeyArgs args;
     // Press button "A"
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_A, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_A, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_NONE, args.metaState);
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
@@ -3615,7 +3646,7 @@
     ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
 
     // Button up.
-    process(mapper, ARBITRARY_TIME + 2, EV_KEY, BTN_A, 0);
+    process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, BTN_A, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(AMETA_NONE, args.metaState);
     ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
@@ -3722,20 +3753,20 @@
     ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
 
     // Toggle caps lock on.
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0);
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL));
     ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
 
     // Toggle num lock on.
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0);
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML));
     ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
 
     // Toggle scroll lock on.
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0);
     ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL));
     ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
 
@@ -3790,28 +3821,28 @@
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
     NotifyKeyArgs args;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
 
-    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(uint32_t(0), args.policyFlags);
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(uint32_t(0), args.policyFlags);
 
-    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(uint32_t(0), args.policyFlags);
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
 
-    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
 }
@@ -3828,28 +3859,28 @@
             addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
                                                        AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
     NotifyKeyArgs args;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
 
-    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_DOWN, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(uint32_t(0), args.policyFlags);
 
-    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0);
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_DOWN, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(uint32_t(0), args.policyFlags);
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
 
-    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
     ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
 }
@@ -3887,9 +3918,9 @@
                                                int32_t rotatedY) {
     NotifyMotionArgs args;
 
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, originalX);
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, originalY);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, originalX);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, originalY);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -3970,8 +4001,8 @@
 
     // Button press.
     // Mostly testing non x/y behavior here so we don't need to check again elsewhere.
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -4011,8 +4042,8 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 
     // Button release.  Should have same down time.
-    process(mapper, ARBITRARY_TIME + 1, EV_KEY, BTN_MOUSE, 0);
-    process(mapper, ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, BTN_MOUSE, 0);
+    process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
     ASSERT_EQ(DEVICE_ID, args.deviceId);
@@ -4059,16 +4090,16 @@
     NotifyMotionArgs args;
 
     // Motion in X but not Y.
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 1);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
             1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Motion in Y but not X.
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, -2);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -4082,8 +4113,8 @@
     NotifyMotionArgs args;
 
     // Button press.
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -4095,8 +4126,8 @@
             0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Button release.
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -4115,10 +4146,10 @@
     NotifyMotionArgs args;
 
     // Combined X, Y and Button.
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 1);
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, -2);
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -4132,9 +4163,9 @@
             1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Move X, Y a bit while pressed.
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 2);
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 1);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 2);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -4142,8 +4173,8 @@
             1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Release Button.
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
@@ -4228,8 +4259,8 @@
     NotifyKeyArgs keyArgs;
 
     // press BTN_LEFT, release BTN_LEFT
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
@@ -4244,8 +4275,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -4268,9 +4299,9 @@
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1);
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
@@ -4297,8 +4328,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
@@ -4313,16 +4344,16 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
     ASSERT_EQ(0, mFakePointerController->getButtonState());
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -4339,8 +4370,8 @@
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // press BTN_BACK, release BTN_BACK
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_BACK, 1);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -4359,8 +4390,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_BACK, 0);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -4380,8 +4411,8 @@
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
 
     // press BTN_SIDE, release BTN_SIDE
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_SIDE, 1);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
@@ -4400,8 +4431,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_SIDE, 0);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -4421,8 +4452,8 @@
     ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
 
     // press BTN_FORWARD, release BTN_FORWARD
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_FORWARD, 1);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -4441,8 +4472,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_FORWARD, 0);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -4462,8 +4493,8 @@
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
 
     // press BTN_EXTRA, release BTN_EXTRA
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_EXTRA, 1);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
     ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
@@ -4482,8 +4513,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0],
             100.0f, 200.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_EXTRA, 0);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
     ASSERT_EQ(0, motionArgs.buttonState);
@@ -4513,9 +4544,9 @@
 
     NotifyMotionArgs args;
 
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
@@ -4541,9 +4572,9 @@
     NotifyMotionArgs args;
 
     // Move.
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
@@ -4552,8 +4583,8 @@
     ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f));
 
     // Button press.
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
@@ -4566,8 +4597,8 @@
             0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Button release.
-    process(mapper, ARBITRARY_TIME + 2, EV_KEY, BTN_MOUSE, 0);
-    process(mapper, ARBITRARY_TIME + 2, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, BTN_MOUSE, 0);
+    process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
@@ -4580,9 +4611,9 @@
             0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 
     // Another move.
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 30);
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 40);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 30);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 40);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
@@ -4601,9 +4632,9 @@
     ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
     ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
 
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
@@ -4629,9 +4660,9 @@
     mFakePointerController->setButtonState(0);
 
     NotifyMotionArgs args;
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_X, 10);
-    process(mapper, ARBITRARY_TIME, EV_REL, REL_Y, 20);
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
     ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
     ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
@@ -4854,46 +4885,46 @@
 }
 
 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);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 1);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, x);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, 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);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, x);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, y);
 }
 
 void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper& mapper) {
-    process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_TOUCH, 0);
 }
 
 void SingleTouchInputMapperTest::processPressure(SingleTouchInputMapper& mapper, int32_t pressure) {
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_PRESSURE, pressure);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_PRESSURE, pressure);
 }
 
 void SingleTouchInputMapperTest::processToolMajor(SingleTouchInputMapper& mapper,
                                                   int32_t toolMajor) {
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
 }
 
 void SingleTouchInputMapperTest::processDistance(SingleTouchInputMapper& mapper, int32_t distance) {
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_DISTANCE, distance);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_DISTANCE, distance);
 }
 
 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);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TILT_X, tiltX);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_TILT_Y, tiltY);
 }
 
 void SingleTouchInputMapperTest::processKey(SingleTouchInputMapper& mapper, int32_t code,
                                             int32_t value) {
-    process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value);
 }
 
 void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper& mapper) {
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
 }
 
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) {
@@ -6181,64 +6212,64 @@
 
 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);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_X, x);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_POSITION_Y, y);
 }
 
 void MultiTouchInputMapperTest::processTouchMajor(MultiTouchInputMapper& mapper,
                                                   int32_t touchMajor) {
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
 }
 
 void MultiTouchInputMapperTest::processTouchMinor(MultiTouchInputMapper& mapper,
                                                   int32_t touchMinor) {
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
 }
 
 void MultiTouchInputMapperTest::processToolMajor(MultiTouchInputMapper& mapper, int32_t toolMajor) {
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
 }
 
 void MultiTouchInputMapperTest::processToolMinor(MultiTouchInputMapper& mapper, int32_t toolMinor) {
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
 }
 
 void MultiTouchInputMapperTest::processOrientation(MultiTouchInputMapper& mapper,
                                                    int32_t orientation) {
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_ORIENTATION, orientation);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_ORIENTATION, orientation);
 }
 
 void MultiTouchInputMapperTest::processPressure(MultiTouchInputMapper& mapper, int32_t pressure) {
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_PRESSURE, pressure);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_PRESSURE, pressure);
 }
 
 void MultiTouchInputMapperTest::processDistance(MultiTouchInputMapper& mapper, int32_t distance) {
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_DISTANCE, distance);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_DISTANCE, distance);
 }
 
 void MultiTouchInputMapperTest::processId(MultiTouchInputMapper& mapper, int32_t id) {
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, id);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_TRACKING_ID, id);
 }
 
 void MultiTouchInputMapperTest::processSlot(MultiTouchInputMapper& mapper, int32_t slot) {
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, slot);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_SLOT, slot);
 }
 
 void MultiTouchInputMapperTest::processToolType(MultiTouchInputMapper& mapper, int32_t toolType) {
-    process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
 }
 
 void MultiTouchInputMapperTest::processKey(MultiTouchInputMapper& mapper, int32_t code,
                                            int32_t value) {
-    process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, code, value);
 }
 
 void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) {
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_MT_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_MT_REPORT, 0);
 }
 
 void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper& mapper) {
-    process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+    process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
@@ -7672,6 +7703,32 @@
 }
 
 /**
+ * Ensure that the readTime is set to the SYN_REPORT value when processing touch events.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_SendsReadTime) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareAxes(POSITION);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    process(mapper, 10, 11 /*readTime*/, EV_ABS, ABS_MT_TRACKING_ID, 1);
+    process(mapper, 15, 16 /*readTime*/, EV_ABS, ABS_MT_POSITION_X, 100);
+    process(mapper, 20, 21 /*readTime*/, EV_ABS, ABS_MT_POSITION_Y, 100);
+    process(mapper, 25, 26 /*readTime*/, EV_SYN, SYN_REPORT, 0);
+
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(26, args.readTime);
+
+    process(mapper, 30, 31 /*readTime*/, EV_ABS, ABS_MT_POSITION_X, 110);
+    process(mapper, 30, 32 /*readTime*/, EV_ABS, ABS_MT_POSITION_Y, 220);
+    process(mapper, 30, 33 /*readTime*/, EV_SYN, SYN_REPORT, 0);
+
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(33, args.readTime);
+}
+
+/**
  * When the viewport is not active (isActive=false), the touch mapper should be disabled and the
  * events should not be delivered to the listener.
  */
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index ad93a65..a489253 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
 cc_benchmark {
     name: "libpowermanager_benchmarks",
     srcs: [
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 0f5037e..69e4041 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
 cc_test {
     name: "libpowermanager_test",
     test_suites: ["device-tests"],
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 625f315..470059a 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -154,6 +154,7 @@
         "DisplayRenderArea.cpp",
         "Effects/Daltonizer.cpp",
         "EventLog/EventLog.cpp",
+        "FpsReporter.cpp",
         "FrameTracer/FrameTracer.cpp",
         "FrameTracker.cpp",
         "Layer.cpp",
diff --git a/services/surfaceflinger/FpsReporter.cpp b/services/surfaceflinger/FpsReporter.cpp
new file mode 100644
index 0000000..c7dbf88
--- /dev/null
+++ b/services/surfaceflinger/FpsReporter.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "FpsReporter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "FpsReporter.h"
+
+#include "Layer.h"
+
+namespace android {
+
+FpsReporter::FpsReporter(frametimeline::FrameTimeline& frameTimeline)
+      : mFrameTimeline(frameTimeline) {}
+
+void FpsReporter::dispatchLayerFps() const {
+    std::vector<TrackedListener> localListeners;
+    {
+        std::scoped_lock lock(mMutex);
+        if (mListeners.empty()) {
+            return;
+        }
+
+        std::transform(mListeners.begin(), mListeners.end(), std::back_inserter(localListeners),
+                       [](const std::pair<wp<IBinder>, TrackedListener>& entry) {
+                           return entry.second;
+                       });
+    }
+
+    for (const auto& listener : localListeners) {
+        sp<Layer> promotedLayer = listener.layer.promote();
+        if (promotedLayer != nullptr) {
+            std::unordered_set<int32_t> layerIds;
+
+            promotedLayer->traverse(LayerVector::StateSet::Drawing,
+                                    [&](Layer* layer) { layerIds.insert(layer->getSequence()); });
+
+            listener.listener->onFpsReported(mFrameTimeline.computeFps(layerIds));
+        }
+    }
+}
+
+void FpsReporter::binderDied(const wp<IBinder>& who) {
+    std::scoped_lock lock(mMutex);
+    mListeners.erase(who);
+}
+
+void FpsReporter::addListener(const sp<gui::IFpsListener>& listener, const wp<Layer>& layer) {
+    sp<IBinder> asBinder = IInterface::asBinder(listener);
+    asBinder->linkToDeath(this);
+    std::lock_guard lock(mMutex);
+    mListeners.emplace(wp<IBinder>(asBinder), TrackedListener{listener, layer});
+}
+
+void FpsReporter::removeListener(const sp<gui::IFpsListener>& listener) {
+    std::lock_guard lock(mMutex);
+    mListeners.erase(wp<IBinder>(IInterface::asBinder(listener)));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
new file mode 100644
index 0000000..d64b3dc
--- /dev/null
+++ b/services/surfaceflinger/FpsReporter.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <android/gui/IFpsListener.h>
+#include <binder/IBinder.h>
+
+#include <unordered_map>
+
+#include "FrameTimeline/FrameTimeline.h"
+
+namespace android {
+
+class Layer;
+
+class FpsReporter : public IBinder::DeathRecipient {
+public:
+    FpsReporter(frametimeline::FrameTimeline& frameTimeline);
+
+    // Dispatches updated layer fps values for the registered listeners
+    // This method promotes Layer weak pointers and performs layer stack traversals, so mStateLock
+    // must be held when calling this method.
+    void dispatchLayerFps() const EXCLUDES(mMutex);
+
+    // Override for IBinder::DeathRecipient
+    void binderDied(const wp<IBinder>&) override;
+
+    // Registers an Fps listener that listens to fps updates for the provided layer
+    void addListener(const sp<gui::IFpsListener>& listener, const wp<Layer>& layer);
+    // Deregisters an Fps listener
+    void removeListener(const sp<gui::IFpsListener>& listener);
+
+private:
+    mutable std::mutex mMutex;
+    struct WpHash {
+        size_t operator()(const wp<IBinder>& p) const {
+            return std::hash<IBinder*>()(p.unsafe_get());
+        }
+    };
+
+    struct TrackedListener {
+        sp<gui::IFpsListener> listener;
+        wp<Layer> layer;
+    };
+
+    frametimeline::FrameTimeline& mFrameTimeline;
+    std::unordered_map<wp<IBinder>, TrackedListener, WpHash> mListeners GUARDED_BY(mMutex);
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
index 1e6d21e..10a5833 100644
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
 cc_library_static {
     name: "libframetimeline",
     defaults: ["surfaceflinger_defaults"],
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index da04202..ff000c9 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -19,12 +19,15 @@
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "FrameTimeline.h"
+
 #include <android-base/stringprintf.h>
 #include <utils/Log.h>
 #include <utils/Trace.h>
+
 #include <chrono>
 #include <cinttypes>
 #include <numeric>
+#include <unordered_set>
 
 namespace android::frametimeline {
 
@@ -277,8 +280,8 @@
 }
 
 SurfaceFrame::SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid,
-                           uid_t ownerUid, std::string layerName, std::string debugName,
-                           PredictionState predictionState,
+                           uid_t ownerUid, int32_t layerId, std::string layerName,
+                           std::string debugName, PredictionState predictionState,
                            frametimeline::TimelineItem&& predictions,
                            std::shared_ptr<TimeStats> timeStats,
                            JankClassificationThresholds thresholds,
@@ -289,6 +292,7 @@
         mOwnerUid(ownerUid),
         mLayerName(std::move(layerName)),
         mDebugName(std::move(debugName)),
+        mLayerId(layerId),
         mPresentState(PresentState::Unknown),
         mPredictionState(predictionState),
         mPredictions(predictions),
@@ -397,6 +401,8 @@
     StringAppendF(&result, "Scheduled rendering rate: %d fps\n",
                   mRenderRate ? mRenderRate->getIntValue() : 0);
     StringAppendF(&result, "%s", indent.c_str());
+    StringAppendF(&result, "Layer ID : %d\n", mLayerId);
+    StringAppendF(&result, "%s", indent.c_str());
     StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str());
     StringAppendF(&result, "%s", indent.c_str());
     if (mPresentState == PresentState::Dropped) {
@@ -458,7 +464,9 @@
 
     const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
     const nsecs_t deadlineDelta = mActuals.endTime - mPredictions.endTime;
-    const nsecs_t deltaToVsync = std::abs(presentDelta) % refreshRate.getPeriodNsecs();
+    const nsecs_t deltaToVsync = refreshRate.getPeriodNsecs() > 0
+            ? std::abs(presentDelta) % refreshRate.getPeriodNsecs()
+            : 0;
 
     if (deadlineDelta > mJankClassificationThresholds.deadlineThreshold) {
         mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
@@ -698,11 +706,11 @@
 }
 
 std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
-        const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
+        const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
         std::string layerName, std::string debugName) {
     ATRACE_CALL();
     if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
-        return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid,
+        return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
                                               std::move(layerName), std::move(debugName),
                                               PredictionState::None, TimelineItem(), mTimeStats,
                                               mJankClassificationThresholds, &mTraceCookieCounter);
@@ -710,13 +718,13 @@
     std::optional<TimelineItem> predictions =
             mTokenManager.getPredictionsForToken(frameTimelineInfo.vsyncId);
     if (predictions) {
-        return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid,
+        return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
                                               std::move(layerName), std::move(debugName),
                                               PredictionState::Valid, std::move(*predictions),
                                               mTimeStats, mJankClassificationThresholds,
                                               &mTraceCookieCounter);
     }
-    return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid,
+    return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
                                           std::move(layerName), std::move(debugName),
                                           PredictionState::Expired, TimelineItem(), mTimeStats,
                                           mJankClassificationThresholds, &mTraceCookieCounter);
@@ -804,7 +812,9 @@
 
     // How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there
     // was a prediction error or not.
-    nsecs_t deltaToVsync = std::abs(presentDelta) % mRefreshRate.getPeriodNsecs();
+    nsecs_t deltaToVsync = mRefreshRate.getPeriodNsecs() > 0
+            ? std::abs(presentDelta) % mRefreshRate.getPeriodNsecs()
+            : 0;
     if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) {
         mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent
                                                  : FramePresentMetadata::EarlyPresent;
@@ -968,6 +978,65 @@
     }
 }
 
+float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) {
+    if (layerIds.empty()) {
+        return 0.0f;
+    }
+
+    std::vector<nsecs_t> presentTimes;
+    {
+        std::scoped_lock lock(mMutex);
+        presentTimes.reserve(mDisplayFrames.size());
+        for (size_t i = 0; i < mDisplayFrames.size(); i++) {
+            const auto& displayFrame = mDisplayFrames[i];
+            if (displayFrame->getActuals().presentTime <= 0) {
+                continue;
+            }
+            for (const auto& surfaceFrame : displayFrame->getSurfaceFrames()) {
+                if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented &&
+                    layerIds.count(surfaceFrame->getLayerId()) > 0) {
+                    // We're looking for DisplayFrames that presents at least one layer from
+                    // layerIds, so push the present time and skip looking through the rest of the
+                    // SurfaceFrames.
+                    presentTimes.push_back(displayFrame->getActuals().presentTime);
+                    break;
+                }
+            }
+        }
+    }
+
+    // FPS can't be computed when there's fewer than 2 presented frames.
+    if (presentTimes.size() <= 1) {
+        return 0.0f;
+    }
+
+    nsecs_t priorPresentTime = -1;
+    nsecs_t totalPresentToPresentWalls = 0;
+
+    for (const nsecs_t presentTime : presentTimes) {
+        if (priorPresentTime == -1) {
+            priorPresentTime = presentTime;
+            continue;
+        }
+
+        totalPresentToPresentWalls += (presentTime - priorPresentTime);
+        priorPresentTime = presentTime;
+    }
+
+    if (CC_UNLIKELY(totalPresentToPresentWalls <= 0)) {
+        ALOGW("Invalid total present-to-present duration when computing fps: %" PRId64,
+              totalPresentToPresentWalls);
+        return 0.0f;
+    }
+
+    const constexpr nsecs_t kOneSecond =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+    // (10^9 nanoseconds / second) * (N present deltas) / (total nanoseconds in N present deltas) =
+    // M frames / second
+    return kOneSecond * static_cast<nsecs_t>((presentTimes.size() - 1)) /
+            static_cast<float>(totalPresentToPresentWalls);
+}
+
 void FrameTimeline::flushPendingPresentFences() {
     for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
         const auto& pendingPresentFence = mPendingPresentFences[i];
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 8f3157d..d65769b 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -156,9 +156,10 @@
     // Only FrameTimeline can construct a SurfaceFrame as it provides Predictions(through
     // TokenManager), Thresholds and TimeStats pointer.
     SurfaceFrame(const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
-                 std::string layerName, std::string debugName, PredictionState predictionState,
-                 TimelineItem&& predictions, std::shared_ptr<TimeStats> timeStats,
-                 JankClassificationThresholds thresholds, TraceCookieCounter* traceCookieCounter);
+                 int32_t layerId, std::string layerName, std::string debugName,
+                 PredictionState predictionState, TimelineItem&& predictions,
+                 std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
+                 TraceCookieCounter* traceCookieCounter);
     ~SurfaceFrame() = default;
 
     // Returns std::nullopt if the frame hasn't been classified yet.
@@ -199,6 +200,7 @@
     // Getter functions used only by FrameTimelineTests and SurfaceFrame internally
     TimelineItem getActuals() const;
     pid_t getOwnerPid() const { return mOwnerPid; };
+    int32_t getLayerId() const { return mLayerId; };
     PredictionState getPredictionState() const;
     PresentState getPresentState() const;
     FrameReadyMetadata getFrameReadyMetadata() const;
@@ -221,6 +223,7 @@
     const uid_t mOwnerUid;
     const std::string mLayerName;
     const std::string mDebugName;
+    const int32_t mLayerId;
     PresentState mPresentState GUARDED_BY(mMutex);
     const PredictionState mPredictionState;
     const TimelineItem mPredictions;
@@ -267,7 +270,7 @@
     // Debug name is the human-readable debugging string for dumpsys.
     virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
             const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
-            std::string layerName, std::string debugName) = 0;
+            int32_t layerId, std::string layerName, std::string debugName) = 0;
 
     // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
     // composited into one display frame.
@@ -292,6 +295,11 @@
     // Sets the max number of display frames that can be stored. Called by SF backdoor.
     virtual void setMaxDisplayFrames(uint32_t size);
 
+    // Computes the historical fps for the provided set of layer IDs
+    // The fps is compted from the linear timeline of present timestamps for DisplayFrames
+    // containing at least one layer ID.
+    virtual float computeFps(const std::unordered_set<int32_t>& layerIds);
+
     // Restores the max number of display frames to default. Called by SF backdoor.
     virtual void reset() = 0;
 };
@@ -417,13 +425,14 @@
     frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
     std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
             const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
-            std::string layerName, std::string debugName) override;
+            int32_t layerId, std::string layerName, std::string debugName) override;
     void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
     void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
     void setSfPresent(nsecs_t sfPresentTime,
                       const std::shared_ptr<FenceTime>& presentFence) override;
     void parseArgs(const Vector<String16>& args, std::string& result) override;
     void setMaxDisplayFrames(uint32_t size) override;
+    float computeFps(const std::unordered_set<int32_t>& layerIds) override;
     void reset() override;
 
     // Sets up the perfetto tracing backend and data source.
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 141a112..937868a 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1595,7 +1595,8 @@
 std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction(
         const FrameTimelineInfo& info, nsecs_t postTime) {
     auto surfaceFrame =
-            mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, mName,
+            mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
+                                                                 getSequence(), mName,
                                                                  mTransactionName);
     // For Transactions, the post time is considered to be both queue and acquire fence time.
     surfaceFrame->setActualQueueTime(postTime);
@@ -1611,8 +1612,8 @@
 std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer(
         const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName) {
     auto surfaceFrame =
-            mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, mName,
-                                                                 debugName);
+            mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
+                                                                 getSequence(), mName, debugName);
     // For buffers, acquire fence time will set during latch.
     surfaceFrame->setActualQueueTime(queueTime);
     const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 14bb5b7..664c10b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -501,6 +501,7 @@
     // one empty rect.
     virtual void useSurfaceDamage() {}
     virtual void useEmptyDamage() {}
+    Region getVisibleRegion(const DisplayDevice*) const;
 
     virtual void incrementPendingBufferCount() {}
 
@@ -1009,6 +1010,7 @@
 
     // For unit tests
     friend class TestableSurfaceFlinger;
+    friend class FpsReporterTest;
     friend class RefreshRateSelectionTest;
     friend class SetFrameRateTest;
     friend class TransactionFrameTracerTest;
@@ -1165,7 +1167,6 @@
     virtual bool canDrawShadows() const { return true; }
 
     Hwc2::IComposerClient::Composition getCompositionType(const DisplayDevice&) const;
-    Region getVisibleRegion(const DisplayDevice*) const;
 
     /**
      * Returns an unsorted vector of all layers that are part of this tree.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 4ee759f..6a98e36 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -108,6 +108,7 @@
 #include "DisplayRenderArea.h"
 #include "EffectLayer.h"
 #include "Effects/Daltonizer.h"
+#include "FpsReporter.h"
 #include "FrameTimeline/FrameTimeline.h"
 #include "FrameTracer/FrameTracer.h"
 #include "Layer.h"
@@ -254,6 +255,11 @@
     std::function<void()> mCallback;
 };
 
+enum Permission {
+    ACCESS_SURFACE_FLINGER = 0x1,
+    ROTATE_SURFACE_FLINGER = 0x2,
+};
+
 }  // namespace anonymous
 
 struct SetInputWindowsListener : os::BnSetInputWindowsListener {
@@ -966,9 +972,18 @@
     }
 
     info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
-    info->supportedColorModes = getDisplayColorModes(display->getPhysicalId());
+    const auto displayId = display->getPhysicalId();
+    info->supportedColorModes = getDisplayColorModes(displayId);
 
     info->hdrCapabilities = display->getHdrCapabilities();
+    info->autoLowLatencyModeSupported =
+            getHwComposer().hasDisplayCapability(displayId,
+                                                 hal::DisplayCapability::AUTO_LOW_LATENCY_MODE);
+    std::vector<hal::ContentType> types;
+    getHwComposer().getSupportedContentTypes(displayId, &types);
+    info->gameContentTypeSupported = std::any_of(types.begin(), types.end(), [](auto type) {
+        return type == hal::ContentType::GAME;
+    });
     return NO_ERROR;
 }
 
@@ -1243,24 +1258,6 @@
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
-                                                      bool* outSupport) const {
-    if (!displayToken) {
-        return BAD_VALUE;
-    }
-
-    Mutex::Autolock lock(mStateLock);
-
-    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
-    if (!displayId) {
-        return NAME_NOT_FOUND;
-    }
-    *outSupport =
-            getHwComposer().hasDisplayCapability(*displayId,
-                                                 hal::DisplayCapability::AUTO_LOW_LATENCY_MODE);
-    return NO_ERROR;
-}
-
 void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
     static_cast<void>(schedule([=]() MAIN_THREAD {
         if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
@@ -1271,27 +1268,6 @@
     }));
 }
 
-status_t SurfaceFlinger::getGameContentTypeSupport(const sp<IBinder>& displayToken,
-                                                   bool* outSupport) const {
-    if (!displayToken) {
-        return BAD_VALUE;
-    }
-
-    Mutex::Autolock lock(mStateLock);
-
-    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
-    if (!displayId) {
-        return NAME_NOT_FOUND;
-    }
-
-    std::vector<hal::ContentType> types;
-    getHwComposer().getSupportedContentTypes(*displayId, &types);
-
-    *outSupport = std::any_of(types.begin(), types.end(),
-                              [](auto type) { return type == hal::ContentType::GAME; });
-    return NO_ERROR;
-}
-
 void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
     static_cast<void>(schedule([=]() MAIN_THREAD {
         if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
@@ -1452,6 +1428,25 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::addFpsListener(const sp<IBinder>& layerHandle,
+                                        const sp<gui::IFpsListener>& listener) {
+    if (!listener) {
+        return BAD_VALUE;
+    }
+
+    const wp<Layer> layer = fromHandle(layerHandle);
+    mFpsReporter->addListener(listener, layer);
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeFpsListener(const sp<gui::IFpsListener>& listener) {
+    if (!listener) {
+        return BAD_VALUE;
+    }
+    mFpsReporter->removeListener(listener);
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
                                                      bool* outSupport) const {
     if (!displayToken || !outSupport) {
@@ -2138,6 +2133,13 @@
         }
     });
 
+    {
+        Mutex::Autolock lock(mStateLock);
+        if (mFpsReporter) {
+            mFpsReporter->dispatchLayerFps();
+        }
+    }
+
     mTransactionCallbackInvoker.addPresentFence(mPreviousPresentFences[0]);
     mTransactionCallbackInvoker.sendCallbacks();
 
@@ -2954,6 +2956,7 @@
     mRegionSamplingThread =
             new RegionSamplingThread(*this, *mScheduler,
                                      RegionSamplingThread::EnvironmentTimingTunables());
+    mFpsReporter = new FpsReporter(*mFrameTimeline);
     // Dispatch a mode change request for the primary display on scheduler
     // initialization, so that the EventThreads always contain a reference to a
     // prior configuration.
@@ -3313,7 +3316,7 @@
                                   transaction.displays, transaction.flags,
                                   transaction.inputWindowCommands, transaction.desiredPresentTime,
                                   transaction.isAutoTimestamp, transaction.buffer,
-                                  transaction.postTime, transaction.privileged,
+                                  transaction.postTime, transaction.permissions,
                                   transaction.hasListenerCallbacks, transaction.listenerCallbacks,
                                   transaction.originPid, transaction.originUid, transaction.id);
         }
@@ -3386,16 +3389,23 @@
         const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
     ATRACE_CALL();
 
+    uint32_t permissions =
+            callingThreadHasUnscopedSurfaceFlingerAccess() ? Permission::ACCESS_SURFACE_FLINGER : 0;
+    // Avoid checking for rotation permissions if the caller already has ACCESS_SURFACE_FLINGER
+    // permissions.
+    if ((permissions & Permission::ACCESS_SURFACE_FLINGER) ||
+        callingThreadHasRotateSurfaceFlingerAccess()) {
+        permissions |= Permission::ROTATE_SURFACE_FLINGER;
+    }
+
+    const int64_t postTime = systemTime();
+
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int originPid = ipc->getCallingPid();
+    const int originUid = ipc->getCallingUid();
+
     {
         Mutex::Autolock _l(mQueueLock);
-
-        const int64_t postTime = systemTime();
-        bool privileged = callingThreadHasUnscopedSurfaceFlingerAccess();
-
-        IPCThreadState* ipc = IPCThreadState::self();
-        const int originPid = ipc->getCallingPid();
-        const int originUid = ipc->getCallingUid();
-
         // If its TransactionQueue already has a pending TransactionState or if it is pending
         auto itr = mPendingTransactionQueues.find(applyToken);
         // if this is an animation frame, wait until prior animation frame has
@@ -3430,7 +3440,7 @@
 
         mTransactionQueue.emplace(frameTimelineInfo, states, displays, flags, applyToken,
                                   inputWindowCommands, desiredPresentTime, isAutoTimestamp,
-                                  uncacheBuffer, postTime, privileged, hasListenerCallbacks,
+                                  uncacheBuffer, postTime, permissions, hasListenerCallbacks,
                                   listenerCallbacks, originPid, originUid, transactionId);
 
         if (pendingTransactions ||
@@ -3444,7 +3454,8 @@
             ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]");
         }
 
-        if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
+        if (!(permissions & Permission::ACCESS_SURFACE_FLINGER) &&
+            (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) {
             ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags");
             flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd);
         }
@@ -3499,12 +3510,11 @@
                                            const InputWindowCommands& inputWindowCommands,
                                            const int64_t desiredPresentTime, bool isAutoTimestamp,
                                            const client_cache_t& uncacheBuffer,
-                                           const int64_t postTime, bool privileged,
+                                           const int64_t postTime, uint32_t permissions,
                                            bool hasListenerCallbacks,
                                            const std::vector<ListenerCallbacks>& listenerCallbacks,
                                            int originPid, int originUid, uint64_t transactionId) {
     uint32_t transactionFlags = 0;
-
     for (const DisplayState& display : displays) {
         transactionFlags |= setDisplayStateLocked(display);
     }
@@ -3522,7 +3532,7 @@
     for (const ComposerState& state : states) {
         clientStateFlags |=
                 setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, isAutoTimestamp,
-                                     postTime, privileged, listenerCallbacksWithSurfaces);
+                                     postTime, permissions, listenerCallbacksWithSurfaces);
         if ((flags & eAnimation) && state.state.surface) {
             if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) {
                 mScheduler->recordLayerHistory(layer.get(),
@@ -3542,7 +3552,7 @@
     }
     transactionFlags |= clientStateFlags;
 
-    if (privileged) {
+    if (permissions & Permission::ACCESS_SURFACE_FLINGER) {
         transactionFlags |= addInputWindowCommands(inputWindowCommands);
     } else if (!inputWindowCommands.empty()) {
         ALOGE("Only privileged callers are allowed to send input commands.");
@@ -3646,10 +3656,10 @@
 
 uint32_t SurfaceFlinger::setClientStateLocked(
         const FrameTimelineInfo& frameTimelineInfo, const ComposerState& composerState,
-        int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, bool privileged,
+        int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, uint32_t permissions,
         std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
     const layer_state_t& s = composerState.state;
-
+    const bool privileged = permissions & Permission::ACCESS_SURFACE_FLINGER;
     for (auto& listener : s.listeners) {
         // note that startRegistration will not re-register if the listener has
         // already be registered for a prior surface control
@@ -3774,8 +3784,8 @@
         // ACCESS_SURFACE_FLINGER nor ROTATE_SURFACE_FLINGER
         // (a.k.a. everyone except WindowManager / tests / Launcher) from setting non rectangle
         // preserving transformations.
-        bool allowNonRectPreservingTransforms =
-                privileged || callingThreadHasRotateSurfaceFlingerAccess();
+        const bool allowNonRectPreservingTransforms =
+                permissions & Permission::ROTATE_SURFACE_FLINGER;
         if (layer->setMatrix(s.matrix, allowNonRectPreservingTransforms)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eTransparentRegionChanged) {
@@ -4942,6 +4952,8 @@
         case GET_DISPLAYED_CONTENT_SAMPLE:
         case NOTIFY_POWER_BOOST:
         case SET_GLOBAL_SHADOW_SETTINGS:
+        case ADD_FPS_LISTENER:
+        case REMOVE_FPS_LISTENER:
         case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
             // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the
             // necessary permission dynamically. Don't use the permission cache for this check.
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 21cd2a5..68f22e8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -89,6 +89,7 @@
 
 class Client;
 class EventThread;
+class FpsReporter;
 class HWComposer;
 struct SetInputWindowsListener;
 class IGraphicBufferProducer;
@@ -344,7 +345,8 @@
 
     virtual uint32_t setClientStateLocked(
             const FrameTimelineInfo& info, const ComposerState& composerState,
-            int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime, bool privileged,
+            int64_t desiredPresentTime, bool isAutoTimestamp, int64_t postTime,
+            uint32_t permissions,
             std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
             REQUIRES(mStateLock);
     virtual void commitTransactionLocked();
@@ -441,7 +443,7 @@
                          const sp<IBinder>& applyToken,
                          const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
                          bool isAutoTimestamp, const client_cache_t& uncacheBuffer,
-                         int64_t postTime, bool privileged, bool hasListenerCallbacks,
+                         int64_t postTime, uint32_t permissions, bool hasListenerCallbacks,
                          std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
                          int originUid, uint64_t transactionId)
               : frameTimelineInfo(frameTimelineInfo),
@@ -454,7 +456,7 @@
                 isAutoTimestamp(isAutoTimestamp),
                 buffer(uncacheBuffer),
                 postTime(postTime),
-                privileged(privileged),
+                permissions(permissions),
                 hasListenerCallbacks(hasListenerCallbacks),
                 listenerCallbacks(listenerCallbacks),
                 originPid(originPid),
@@ -471,7 +473,7 @@
         const bool isAutoTimestamp;
         client_cache_t buffer;
         const int64_t postTime;
-        bool privileged;
+        uint32_t permissions;
         bool hasListenerCallbacks;
         std::vector<ListenerCallbacks> listenerCallbacks;
         int originPid;
@@ -561,11 +563,7 @@
     status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
                                        ui::DisplayPrimaries&) override;
     status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
-    status_t getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
-                                          bool* outSupported) const override;
     void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) override;
-    status_t getGameContentTypeSupport(const sp<IBinder>& displayToken,
-                                       bool* outSupported) const override;
     void setGameContentType(const sp<IBinder>& displayToken, bool on) override;
     void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
     status_t clearAnimationFrameStats() override;
@@ -592,6 +590,9 @@
     status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
                                        const sp<IRegionSamplingListener>& listener) override;
     status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
+    status_t addFpsListener(const sp<IBinder>& layerHandle,
+                            const sp<gui::IFpsListener>& listener) override;
+    status_t removeFpsListener(const sp<gui::IFpsListener>& listener) override;
     status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
                                         ui::DisplayModeId displayModeId, bool allowGroupSwitching,
                                         float primaryRefreshRateMin, float primaryRefreshRateMax,
@@ -740,7 +741,7 @@
                                const InputWindowCommands& inputWindowCommands,
                                const int64_t desiredPresentTime, bool isAutoTimestamp,
                                const client_cache_t& uncacheBuffer, const int64_t postTime,
-                               bool privileged, bool hasListenerCallbacks,
+                               uint32_t permissions, bool hasListenerCallbacks,
                                const std::vector<ListenerCallbacks>& listenerCallbacks,
                                int originPid, int originUid, uint64_t transactionId)
             REQUIRES(mStateLock);
@@ -1268,6 +1269,7 @@
 
     bool mLumaSampling = true;
     sp<RegionSamplingThread> mRegionSamplingThread;
+    sp<FpsReporter> mFpsReporter;
     ui::DisplayPrimaries mInternalDisplayPrimaries;
 
     const float mInternalDisplayDensity;
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 2ac6b09..3c1b9d8 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -53,6 +53,7 @@
         "DisplayDevice_GetBestColorModeTest.cpp",
         "DisplayDevice_SetProjectionTest.cpp",
         "EventThreadTest.cpp",
+        "FpsReporterTest.cpp",
         "FpsTest.cpp",
         "FrameTimelineTest.cpp",
         "HWComposerTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
new file mode 100644
index 0000000..a9e5df3
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "FpsReporterTest"
+
+#include <android/gui/BnFpsListener.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "EffectLayer.h"
+#include "FpsReporter.h"
+#include "Layer.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockFrameTimeline.h"
+#include "mock/MockVsyncController.h"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::UnorderedElementsAre;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+struct TestableFpsListener : public gui::BnFpsListener {
+    TestableFpsListener() {}
+
+    float lastReportedFps = 0;
+
+    binder::Status onFpsReported(float fps) override {
+        lastReportedFps = fps;
+        return binder::Status::ok();
+    }
+};
+
+/**
+ * This class covers all the test that are related to refresh rate selection.
+ */
+class FpsReporterTest : public testing::Test {
+public:
+    FpsReporterTest();
+    ~FpsReporterTest() override;
+
+protected:
+    static constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
+    static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
+    static constexpr uint32_t WIDTH = 100;
+    static constexpr uint32_t HEIGHT = 100;
+    static constexpr uint32_t LAYER_FLAGS = 0;
+    static constexpr int32_t PRIORITY_UNSET = -1;
+
+    void setupScheduler();
+    void setupComposer(uint32_t virtualDisplayCount);
+    sp<BufferStateLayer> createBufferStateLayer();
+
+    TestableSurfaceFlinger mFlinger;
+    Hwc2::mock::Composer* mComposer = nullptr;
+    mock::FrameTimeline mFrameTimeline =
+            mock::FrameTimeline(std::make_shared<impl::TimeStats>(), 0);
+
+    sp<Client> mClient;
+    sp<Layer> mParent;
+    sp<Layer> mTarget;
+    sp<Layer> mChild;
+    sp<Layer> mGrandChild;
+    sp<Layer> mUnrelated;
+
+    sp<TestableFpsListener> mFpsListener;
+    sp<FpsReporter> mFpsReporter = new FpsReporter(mFrameTimeline);
+};
+
+FpsReporterTest::FpsReporterTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+
+    setupScheduler();
+    setupComposer(0);
+    mFpsListener = new TestableFpsListener();
+}
+
+FpsReporterTest::~FpsReporterTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+}
+
+sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer() {
+    sp<Client> client;
+    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
+                           LAYER_FLAGS, LayerMetadata());
+    return new BufferStateLayer(args);
+}
+
+void FpsReporterTest::setupScheduler() {
+    auto eventThread = std::make_unique<mock::EventThread>();
+    auto sfEventThread = std::make_unique<mock::EventThread>();
+
+    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*eventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+                                                       ResyncCallback())));
+
+    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+                                                       ResyncCallback())));
+
+    auto vsyncController = std::make_unique<mock::VsyncController>();
+    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*vsyncTracker, currentPeriod())
+            .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                            std::move(eventThread), std::move(sfEventThread));
+}
+
+void FpsReporterTest::setupComposer(uint32_t virtualDisplayCount) {
+    mComposer = new Hwc2::mock::Composer();
+    EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+    mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+    Mock::VerifyAndClear(mComposer);
+}
+
+namespace {
+
+TEST_F(FpsReporterTest, callsListeners) {
+    mParent = createBufferStateLayer();
+    mTarget = createBufferStateLayer();
+    mChild = createBufferStateLayer();
+    mGrandChild = createBufferStateLayer();
+    mUnrelated = createBufferStateLayer();
+    mParent->addChild(mTarget);
+    mTarget->addChild(mChild);
+    mChild->addChild(mGrandChild);
+    mParent->commitChildList();
+
+    float expectedFps = 44.0;
+
+    EXPECT_CALL(mFrameTimeline,
+                computeFps(UnorderedElementsAre(mTarget->getSequence(), mChild->getSequence(),
+                                                mGrandChild->getSequence())))
+            .WillOnce(Return(expectedFps));
+
+    mFpsReporter->addListener(mFpsListener, mTarget);
+    mFpsReporter->dispatchLayerFps();
+    EXPECT_EQ(expectedFps, mFpsListener->lastReportedFps);
+    mFpsReporter->removeListener(mFpsListener);
+    Mock::VerifyAndClearExpectations(&mFrameTimeline);
+
+    EXPECT_CALL(mFrameTimeline, computeFps(_)).Times(0);
+    mFpsReporter->dispatchLayerFps();
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index b8c1607..9a4e020 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -170,6 +170,8 @@
 static constexpr pid_t sPidOne = 10;
 static constexpr pid_t sPidTwo = 20;
 static constexpr int32_t sInputEventId = 5;
+static constexpr int32_t sLayerIdOne = 1;
+static constexpr int32_t sLayerIdTwo = 2;
 
 TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
     int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
@@ -187,17 +189,20 @@
 }
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) {
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne,
-                                                                    sLayerNameOne, sLayerNameOne);
-    auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne,
-                                                                    sLayerNameOne, sLayerNameOne);
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne);
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken({}, sPidTwo, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne);
     EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne);
     EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo);
 }
 
 TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) {
-    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne,
-                                                                   sLayerNameOne, sLayerNameOne);
+    auto surfaceFrame =
+            mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne);
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None);
 }
 
@@ -206,7 +211,7 @@
     flushTokens(systemTime() + maxTokenRetentionTime);
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired);
 }
@@ -215,7 +220,7 @@
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
 
     EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid);
     EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true);
@@ -226,7 +231,7 @@
     constexpr int32_t inputEventId = 1;
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({token1, inputEventId}, sPidOne, sUidOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
 
     EXPECT_EQ(inputEventId, surfaceFrame->getInputEventId());
 }
@@ -236,7 +241,7 @@
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -263,10 +268,12 @@
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameTwo, sLayerNameTwo);
+                                                       sUidOne, sLayerIdTwo, sLayerNameTwo,
+                                                       sLayerNameTwo);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame1);
@@ -307,8 +314,8 @@
                 {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
         auto surfaceFrame =
                 mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId},
-                                                           sPidOne, sUidOne, sLayerNameOne,
-                                                           sLayerNameOne);
+                                                           sPidOne, sUidOne, sLayerIdOne,
+                                                           sLayerNameOne, sLayerNameOne);
         mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
         surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
         mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -329,7 +336,8 @@
             {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor});
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11));
     surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -343,18 +351,18 @@
 }
 
 TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) {
-    auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, "acquireFenceAfterQueue",
-                                                       "acquireFenceAfterQueue");
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+                                                                   "acquireFenceAfterQueue",
+                                                                   "acquireFenceAfterQueue");
     surfaceFrame->setActualQueueTime(123);
     surfaceFrame->setAcquireFenceTime(456);
     EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
 }
 
 TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) {
-    auto surfaceFrame =
-            mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, "acquireFenceAfterQueue",
-                                                       "acquireFenceAfterQueue");
+    auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, 0, sLayerIdOne,
+                                                                   "acquireFenceAfterQueue",
+                                                                   "acquireFenceAfterQueue");
     surfaceFrame->setActualQueueTime(456);
     surfaceFrame->setAcquireFenceTime(123);
     EXPECT_EQ(surfaceFrame->getActuals().endTime, 456);
@@ -367,8 +375,8 @@
     // Size shouldn't exceed maxDisplayFrames - 64
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
-                mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne,
-                                                           sLayerNameOne);
+                mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+                                                           sLayerNameOne, sLayerNameOne);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
         mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
         surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -383,8 +391,8 @@
 
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
-                mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne,
-                                                           sLayerNameOne);
+                mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+                                                           sLayerNameOne, sLayerNameOne);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
         mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
         surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -399,8 +407,8 @@
 
     for (size_t i = 0; i < *maxDisplayFrames + 10; i++) {
         auto surfaceFrame =
-                mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne,
-                                                           sLayerNameOne);
+                mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+                                                           sLayerNameOne, sLayerNameOne);
         int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30});
         mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11));
         surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -434,7 +442,8 @@
              std::chrono::nanoseconds(60ms).count()});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
     surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(20ms).count());
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -460,7 +469,8 @@
              std::chrono::nanoseconds(60ms).count()});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
     surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(20ms).count());
@@ -488,7 +498,8 @@
              std::chrono::nanoseconds(90ms).count()});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(45ms).count());
     mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
 
@@ -519,7 +530,8 @@
              std::chrono::nanoseconds(60ms).count()});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(50ms).count());
     mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
 
@@ -550,7 +562,8 @@
              std::chrono::nanoseconds(60ms).count()});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(40ms).count());
     mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
 
@@ -580,7 +593,8 @@
              std::chrono::nanoseconds(90ms).count()});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(40ms).count());
     mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(82ms).count(), refreshRate);
 
@@ -614,7 +628,8 @@
              std::chrono::nanoseconds(90ms).count()});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(45ms).count());
     mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate);
 
@@ -641,7 +656,7 @@
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -667,7 +682,7 @@
     int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne,
-                                                       sLayerNameOne, sLayerNameOne);
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(token2, 20, Fps::fromPeriodNsecs(11));
@@ -710,8 +725,9 @@
 
     tracingSession->StartBlocking();
     int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
-    auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne,
-                                                                    sLayerNameOne, sLayerNameOne);
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerIdOne,
+                                                       sLayerNameOne, sLayerNameOne);
 
     // Set up the display frame
     mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11));
@@ -1015,10 +1031,12 @@
 
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame1->setActualQueueTime(10);
     surfaceFrame1->setDropTime(15);
 
@@ -1174,7 +1192,7 @@
     flushTokens(systemTime() + maxTokenRetentionTime);
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0},
-                                                       sPidOne, sUidOne, sLayerNameOne,
+                                                       sPidOne, sUidOne, sLayerIdOne, sLayerNameOne,
                                                        sLayerNameOne);
     surfaceFrame1->setActualQueueTime(appEndTime);
     surfaceFrame1->setAcquireFenceTime(appEndTime);
@@ -1246,7 +1264,8 @@
     int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
     auto surfaceFrame =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented);
     mFrameTimeline->addSurfaceFrame(surfaceFrame);
@@ -1401,7 +1420,8 @@
     int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame1->setAcquireFenceTime(16);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1420,7 +1440,8 @@
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame2->setAcquireFenceTime(36);
     mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1479,7 +1500,8 @@
     int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 70});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame1->setAcquireFenceTime(16);
     mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1498,7 +1520,8 @@
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame2->setAcquireFenceTime(36);
     mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1556,7 +1579,8 @@
     int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 26, 60});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame1->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1599,7 +1623,8 @@
     int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({25, 36, 50});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame1->setAcquireFenceTime(26);
     mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1618,7 +1643,8 @@
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame2->setAcquireFenceTime(40);
     mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1671,7 +1697,8 @@
     int64_t sfToken2 = mTokenManager->generateTokenForPredictions({112, 116, 120});
     auto surfaceFrame1 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame1->setAcquireFenceTime(50);
     mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30));
     surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
@@ -1690,7 +1717,8 @@
     auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
     auto surfaceFrame2 =
             mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne,
-                                                       sUidOne, sLayerNameOne, sLayerNameOne);
+                                                       sUidOne, sLayerIdOne, sLayerNameOne,
+                                                       sLayerNameOne);
     surfaceFrame2->setAcquireFenceTime(84);
     mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30));
     surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54);
@@ -1735,4 +1763,151 @@
     EXPECT_EQ(presentedSurfaceFrame2.getJankType(),
               JankType::AppDeadlineMissed | JankType::BufferStuffing);
 }
+
+TEST_F(FrameTimelineTest, computeFps_noLayerIds_returnsZero) {
+    EXPECT_EQ(mFrameTimeline->computeFps({}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_singleDisplayFrame_returnsZero) {
+    const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    presentFence1->signalForTest(oneHundredMs);
+    mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+    EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_oneLayer) {
+    const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+    const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    presentFence1->signalForTest(oneHundredMs);
+    mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+    presentFence2->signalForTest(twoHundredMs);
+    mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+    EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 10.0);
+}
+
+TEST_F(FrameTimelineTest, computeFps_twoDisplayFrames_twoLayers) {
+    const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+    const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    presentFence1->signalForTest(oneHundredMs);
+    mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+    presentFence2->signalForTest(twoHundredMs);
+    mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+    EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne, sLayerIdTwo}), 10.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_filtersOutLayers) {
+    const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+    const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    presentFence1->signalForTest(oneHundredMs);
+    mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+    presentFence2->signalForTest(twoHundredMs);
+    mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+    EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 0.0f);
+}
+
+TEST_F(FrameTimelineTest, computeFps_averagesOverMultipleFrames) {
+    const auto oneHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+    const auto twoHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(200ms).count();
+    const auto threeHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(300ms).count();
+    const auto fiveHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(500ms).count();
+    const auto sixHundredMs = std::chrono::duration_cast<std::chrono::nanoseconds>(600ms).count();
+    auto surfaceFrame1 =
+            mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+    auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+    presentFence1->signalForTest(oneHundredMs);
+    mFrameTimeline->setSfPresent(oneHundredMs, presentFence1);
+
+    auto surfaceFrame2 =
+            mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+    auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+    presentFence2->signalForTest(twoHundredMs);
+    mFrameTimeline->setSfPresent(twoHundredMs, presentFence2);
+
+    auto surfaceFrame3 =
+            mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+                                                       sLayerIdTwo, sLayerNameTwo, sLayerNameTwo);
+    auto presentFence3 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame3);
+    presentFence3->signalForTest(threeHundredMs);
+    mFrameTimeline->setSfPresent(threeHundredMs, presentFence3);
+
+    auto surfaceFrame4 =
+            mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+    auto presentFence4 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    surfaceFrame4->setPresentState(SurfaceFrame::PresentState::Presented);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame4);
+    presentFence4->signalForTest(fiveHundredMs);
+    mFrameTimeline->setSfPresent(fiveHundredMs, presentFence4);
+
+    auto surfaceFrame5 =
+            mFrameTimeline->createSurfaceFrameForToken(FrameTimelineInfo(), sPidOne, sUidOne,
+                                                       sLayerIdOne, sLayerNameOne, sLayerNameOne);
+    auto presentFence5 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    // Dropped frames will be excluded from fps computation
+    surfaceFrame5->setPresentState(SurfaceFrame::PresentState::Dropped);
+    mFrameTimeline->addSurfaceFrame(surfaceFrame5);
+    presentFence5->signalForTest(sixHundredMs);
+    mFrameTimeline->setSfPresent(sixHundredMs, presentFence5);
+
+    EXPECT_EQ(mFrameTimeline->computeFps({sLayerIdOne}), 5.0f);
+}
+
 } // namespace android::frametimeline
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
index 44b9b73..b9d1794 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h
@@ -33,6 +33,7 @@
     MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>));
     MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, Fps));
     MOCK_METHOD2(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&));
+    MOCK_METHOD1(computeFps, float(const std::unordered_set<int32_t>&));
 };
 
 } // namespace android::mock
diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp
index 4f89353..2002bdf 100644
--- a/services/vibratorservice/Android.bp
+++ b/services/vibratorservice/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
 cc_library_shared {
     name: "libvibratorservice",
 
diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp
index 7b4cc19..a468146 100644
--- a/services/vibratorservice/benchmarks/Android.bp
+++ b/services/vibratorservice/benchmarks/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
 cc_benchmark {
     name: "libvibratorservice_benchmarks",
     srcs: [
diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp
index ad85990..3294724 100644
--- a/services/vibratorservice/test/Android.bp
+++ b/services/vibratorservice/test/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
 cc_test {
     name: "libvibratorservice_test",
     test_suites: ["device-tests"],
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index 2d4690a..d1cd397 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -33,6 +33,7 @@
 #include <unordered_set>
 #include <utility>
 
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <cutils/properties.h>
 #include <log/log.h>
@@ -134,7 +135,7 @@
         // If no layers specified via Settings, check legacy properties
         if (implicit_layers_.count <= 0) {
             ParseDebugVulkanLayers();
-            property_list(ParseDebugVulkanLayer, this);
+            ParseDebugVulkanLayer();
 
             // sort by priorities
             auto& arr = implicit_layers_;
@@ -181,30 +182,39 @@
             AddImplicitLayer(prio, p, strlen(p));
     }
 
-    static void ParseDebugVulkanLayer(const char* key,
-                                      const char* val,
-                                      void* user_data) {
+    void ParseDebugVulkanLayer() {
+        // Checks for consecutive debug.vulkan.layer.<priority> system
+        // properties after always checking an initial fixed range.
         static const char prefix[] = "debug.vulkan.layer.";
-        const size_t prefix_len = sizeof(prefix) - 1;
+        static constexpr int kFixedRangeBeginInclusive = 0;
+        static constexpr int kFixedRangeEndInclusive = 9;
 
-        if (strncmp(key, prefix, prefix_len) || val[0] == '\0')
-            return;
-        key += prefix_len;
+        bool logged = false;
 
-        // debug.vulkan.layer.<priority>
-        int priority = -1;
-        if (key[0] >= '0' && key[0] <= '9')
-            priority = atoi(key);
+        int priority = kFixedRangeBeginInclusive;
+        while (true) {
+            const std::string prop_key =
+                std::string(prefix) + std::to_string(priority);
+            const std::string prop_val =
+                android::base::GetProperty(prop_key, "");
 
-        if (priority < 0) {
-            ALOGW("Ignored implicit layer %s with invalid priority %s", val,
-                  key);
-            return;
+            if (!prop_val.empty()) {
+                if (!logged) {
+                    ALOGI(
+                        "Detected Vulkan layers configured with "
+                        "debug.vulkan.layer.<priority>. Checking for "
+                        "debug.vulkan.layer.<priority> in the range [%d, %d] "
+                        "followed by a consecutive scan.",
+                        kFixedRangeBeginInclusive, kFixedRangeEndInclusive);
+                    logged = true;
+                }
+                AddImplicitLayer(priority, prop_val.c_str(), prop_val.length());
+            } else if (priority >= kFixedRangeEndInclusive) {
+                return;
+            }
+
+            ++priority;
         }
-
-        OverrideLayerNames& override_layers =
-            *reinterpret_cast<OverrideLayerNames*>(user_data);
-        override_layers.AddImplicitLayer(priority, val, strlen(val));
     }
 
     void AddImplicitLayer(int priority, const char* name, size_t len) {